package alien4cloud.paas;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.inject.Inject;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Component;

import com.google.common.collect.Maps;

import alien4cloud.dao.IGenericSearchDAO;
import alien4cloud.exception.AlreadyExistException;
import alien4cloud.orchestrators.plugin.IOrchestratorPlugin;
import alien4cloud.paas.exception.OrchestratorDisabledException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Manages the paas providers currently used by an enabled Cloud in ALIEN.
 */
@Slf4j
@Component
public class OrchestratorPluginService implements IPaasEventService {
    @Resource(name = "alien-es-dao")
    private IGenericSearchDAO alienDao;
    @Resource(name = "alien-monitor-es-dao")
    private IGenericSearchDAO alienMonitorDao;
    @Resource(name = "paas-monitor-scheduler")
    private TaskScheduler scheduler;
    /** Interval in milliseconds on which to retrieve monitoring events from a PaaS provider. */
    @Value("${paas_monitor.monitor_interval_ms}")
    private long monitorIntervalMs = 1000 * 30;
    @Inject
    private DeploymentStatusEventHandler deploymentStatusEventHandler;

    private Map<String, Registration> monitorRegistrations = Maps.newHashMap();

    @SuppressWarnings("rawtypes")
    private List<IPaasEventListener> listeners = Collections.synchronizedList(new ArrayList<IPaasEventListener>());

    public OrchestratorPluginService() {
        log.info("Create new PaaSProvider instance.");
    }

    @Override
    public void addListener(IPaasEventListener<?> listener) {
        listeners.add(listener);
    }

    @PostConstruct
    public void init() {
        // Deployment status event handler should be the first handler has quite important.
        listeners.add(0, deploymentStatusEventHandler);
    }

    /**
     * Register an {@link IOrchestratorPlugin} for a given cloud.
     *
     * @param orchestratorId Id of the cloud.
     * @param instance Instance of the IOrchestratorPlugin for the given cloud.
     */
    public void register(String orchestratorId, IOrchestratorPlugin instance) {
        log.info("Register provider with id {}", orchestratorId);
        if (monitorRegistrations.containsKey(orchestratorId)) {
            throw new AlreadyExistException("Cloud [" + orchestratorId + "] has already been registered");
        }
        // create the polling monitor responsible to monitor this instance.
        PaaSProviderPollingMonitor monitor = new PaaSProviderPollingMonitor(alienDao, alienMonitorDao, instance, listeners, orchestratorId);
        ScheduledFuture<?> monitorFuture = scheduler.scheduleAtFixedRate(monitor, monitorIntervalMs);
        Registration registration = new Registration(instance, monitorFuture);
        monitorRegistrations.put(orchestratorId, registration);
    }

    /**
     * Remove the registration for the given cloud (will stop monitoring the cloud using the registered IOrchestratorPlugin).
     *
     * @param orchestratorId The id of the cloud for which to remove registration.
     */
    public IOrchestratorPlugin unregister(String orchestratorId) {
        log.info("Unregister provider with id {}", orchestratorId);
        Registration registration = monitorRegistrations.remove(orchestratorId);
        if (registration != null) {
            registration.registration.cancel(false);
            return registration.instance;
        } else {
            return null;
        }
    }

    /**
     * Get a registered IOrchestratorPlugin for a cloud.
     *
     * @param orchestratorId The id of the cloud for which to get the IOrchestratorPlugin instance.
     * @return The {@link IOrchestratorPlugin} for the given cloud or null if none is registered for the given cloud id.
     */
    public IOrchestratorPlugin get(String orchestratorId) {
        Registration registration = monitorRegistrations.get(orchestratorId);
        return registration == null ? null : registration.instance;
    }

    /**
     * Get a registered IOrchestratorPlugin for a cloud.
     *
     * @param orchestratorId The id of the cloud for which to get the IOrchestratorPlugin instance.
     * @return The {@link IOrchestratorPlugin} for the given cloud or throw OrchestratorDisabledException if none is registered for the given cloud id.
     */
    public IOrchestratorPlugin getOrFail(String orchestratorId) {
        Registration registration = monitorRegistrations.get(orchestratorId);
        if (registration == null) {
            throw new OrchestratorDisabledException("The orchestrator with id <" + orchestratorId + "> is not enabled or loaded yet.");
        }
        return registration == null ? null : registration.instance;
    }

    /**
     * A registration for a paasProvider and the associated monitoring registration.
     */
    @AllArgsConstructor(suppressConstructorProperties = true)
    private class Registration {
        private IOrchestratorPlugin instance;
        private ScheduledFuture<?> registration;
    }
}

@@ -123,16 +123,44 @@
 il.Map;%0A
+import java.util.Map.Entry;%0A
 import j
@@ -5031,32 +5031,551 @@
 nstance;%0A    %7D%0A%0A
+    /**%0A     * Find the orchestrator id from the actual plugin instance.%0A     *%0A     * @param instance The instance for which to find the orchestrator Id.%0A     * @return The orchestratorId or null if not found.%0A     */%0A    public String getOrchestratorId(IOrchestratorPlugin instance) %7B%0A        for (Entry%3CString, Registration%3E entry : monitorRegistrations.entrySet()) %7B%0A            if (entry.getValue().instance == instance) %7B%0A                return entry.getKey();%0A            %7D%0A        %7D%0A        return null;%0A    %7D%0A%0A
     /**%0A     * A

Add a way for orchestrator plugin to retrieve their ids.