/*******************************************************************************
 * Copyright (c) 2012-2016 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.che.ide.extension.machine.client.command.valueproviders;

import com.google.common.collect.Maps.EntryTransformer;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;

import org.eclipse.che.api.machine.gwt.client.MachineServiceClient;
import org.eclipse.che.api.machine.shared.dto.MachineDto;
import org.eclipse.che.api.machine.shared.dto.ServerDto;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.workspace.gwt.client.event.WorkspaceStartedEvent;
import org.eclipse.che.api.workspace.gwt.client.event.WorkspaceStartedHandler;
import org.eclipse.che.api.workspace.gwt.client.event.WorkspaceStoppedEvent;
import org.eclipse.che.api.workspace.gwt.client.event.WorkspaceStoppedHandler;
import org.eclipse.che.api.workspace.shared.dto.UsersWorkspaceDto;
import org.eclipse.che.ide.api.app.AppContext;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import static com.google.common.collect.Maps.transformEntries;

/**
 * Provide mapping internal port, i.e. ${server.port.8080} to 127.0.0.1:21212.
 *
 * @author Vlad Zhukovskiy
 */
@Singleton
public class ServerPortProvider implements WorkspaceStartedHandler, WorkspaceStoppedHandler {

    public static final String KEY_TEMPLATE = "${server.port.%}";

    private final MachineServiceClient                 machineServiceClient;
    private final CommandPropertyValueProviderRegistry commandPropertyRegistry;
    private final AppContext                           appContext;

    private Collection<CommandPropertyValueProvider> providers;

    private final Operation<MachineDto> registerProviders = new Operation<MachineDto>() {
        @Override
        public void apply(MachineDto machine) throws OperationException {
            providers = getProviders(machine);
            commandPropertyRegistry.register(Sets.newHashSet(providers));
        }
    };

    @Inject
    public ServerPortProvider(EventBus eventBus,
                              MachineServiceClient machineServiceClient,
                              CommandPropertyValueProviderRegistry commandPropertyRegistry,
                              AppContext appContext) {
        this.machineServiceClient = machineServiceClient;
        this.commandPropertyRegistry = commandPropertyRegistry;
        this.appContext = appContext;

        eventBus.addHandler(WorkspaceStartedEvent.TYPE, this);
        eventBus.addHandler(WorkspaceStoppedEvent.TYPE, this);

        registerProviders();
    }

    private void registerProviders() {
        String devMachineId = appContext.getDevMachineId();
        if (devMachineId != null) {
            machineServiceClient.getMachine(devMachineId).then(registerProviders);
        }
    }

    private Collection<CommandPropertyValueProvider> getProviders(MachineDto machine) {
        EntryTransformer<String, ServerDto, CommandPropertyValueProvider> machineToProvider =
                new EntryTransformer<String, ServerDto, CommandPropertyValueProvider>() {
                    @Override
                    public CommandPropertyValueProvider transformEntry(String internalPort, ServerDto serverConfiguration) {
                        return new AddressProvider(internalPort, serverConfiguration.getAddress());
                    }
                };

        Map<String, CommandPropertyValueProvider> providers = transformEntries(machine.getRuntime().getServers(), machineToProvider);
        Map<String, CommandPropertyValueProvider> providersWithNoTcpAliases = new HashMap<>();
        for (Map.Entry<String, CommandPropertyValueProvider> providerEntry : providers.entrySet()) {
            if (providerEntry.getKey().endsWith("/tcp")) {
                providersWithNoTcpAliases.put(providerEntry.getKey().substring(0, providerEntry.getKey().length() - 4),
                                              providerEntry.getValue());
            }
        }
        providersWithNoTcpAliases.putAll(providers);

        return providersWithNoTcpAliases.values();
    }

    @Override
    public void onWorkspaceStarted(UsersWorkspaceDto workspace) {
        registerProviders();
    }

    @Override
    public void onWorkspaceStopped(WorkspaceStoppedEvent event) {
        for (CommandPropertyValueProvider provider : providers) {
            commandPropertyRegistry.unregister(provider);
        }

        providers.clear();
    }

    private class AddressProvider implements CommandPropertyValueProvider {

        String variable;
        String address;

        AddressProvider(String internalPort, String address) {
            this.variable = KEY_TEMPLATE.replaceAll("%", internalPort);
            this.address = address;
        }

        @Override
        public String getKey() {
            return variable;
        }

        @Override
        public String getValue() {
            return address;
        }
    }
}

@@ -597,64 +597,8 @@
 s;%0A%0A
-import com.google.common.collect.Maps.EntryTransformer;%0A
 impo
@@ -1503,130 +1503,33 @@
 til.
-Collection;%0Aimport java.util.HashMap;%0Aimport java.util.Map;%0A%0Aimport static com.google.common.collect.Maps.transformEntries
+Map;%0Aimport java.util.Set
 ;%0A%0A/
@@ -2050,34 +2050,27 @@
     private 
-Collection
+Set
 %3CCommandProp
@@ -2378,24 +2378,8 @@
 ter(
-Sets.newHashSet(
 prov
@@ -2384,17 +2384,16 @@
 oviders)
-)
 ;%0A      
@@ -3260,18 +3260,11 @@
 ate 
-Collection
+Set
 %3CCom
@@ -3337,44 +3337,12 @@
     
-EntryTransformer%3CString, ServerDto, 
+Set%3C
 Comm
@@ -3371,744 +3371,232 @@
 er%3E 
-machineToP
+p
 rovider
+s
  =
-%0A                new EntryTransformer%3CString, ServerDto, CommandPropertyValueProvider%3E() %7B%0A                    @Override%0A                    public CommandPropertyValueProvider transform
+ Sets.newHashSet();%0A        for (Map.
 Entry
-(
+%3C
 String
- internalPort, ServerDto serverConfiguration) %7B%0A                        return new AddressProvider(internalPort, serverConfiguration.getAddress());%0A                    %7D%0A                %7D;%0A%0A        Map%3CString, CommandPropertyValueProvider%3E providers = transformEntries(machine.getRuntime().getServers(), machineToProvider);%0A        Map%3CString, CommandPropertyValueProvider%3E providersWithNoTcpAliases = new HashMap%3C%3E();%0A        for (Map.Entry%3CString, CommandPropertyValueProvider%3E providerEntry : providers.entrySet()) %7B
+, ServerDto%3E entry : machine.getRuntime().getServers().entrySet()) %7B%0A            providers.add(new AddressProvider(entry.getKey(), entry.getValue().getAddress()));%0A
 %0A   
@@ -3608,25 +3608,17 @@
     if (
-providerE
+e
 ntry.get
@@ -3672,38 +3672,34 @@
 ders
-WithNoTcpAliases.put(p
+.add(new AddressP
 rovider
-E
+(e
 ntry
@@ -3721,25 +3721,17 @@
 ring(0, 
-providerE
+e
 ntry.get
@@ -3754,64 +3754,10 @@
  4),
-%0A                                              providerE
+ e
 ntry
@@ -3767,16 +3767,30 @@
 tValue()
+.getAddress())
 );%0A     
@@ -3812,111 +3812,33 @@
   %7D%0A
-        providersWithNoTcpAliases.putAll(providers);%0A%0A        return providersWithNoTcpAliases.values()
+%0A        return providers
 ;%0A  

Support exposed port with tcp suffix