/*
 * Copyright (c) 2014-2015 University of Ulm
 *
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.  Licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package de.uniulm.omi.cloudiator.lance.lca;

import java.rmi.RemoteException;
import java.util.List;

import de.uniulm.omi.cloudiator.lance.application.ApplicationInstanceId;
import de.uniulm.omi.cloudiator.lance.application.DeploymentContext;
import de.uniulm.omi.cloudiator.lance.application.component.DeployableComponent;
import de.uniulm.omi.cloudiator.lance.container.spec.os.OperatingSystem;
import de.uniulm.omi.cloudiator.lance.lca.container.*;
import de.uniulm.omi.cloudiator.lance.lca.containers.docker.DockerContainerManagerFactory;
import de.uniulm.omi.cloudiator.lance.lca.registry.RegistrationException;

public class LifecycleAgentImpl implements LifecycleAgent {

    private volatile AgentStatus status = AgentStatus.NEW;
    //private final ContainerManager manager;
    private  ContainerManager manager;

    private final HostContext hostContext;
    
    LifecycleAgentImpl(HostContext contex) {
        //FIXME: where to put this code?
        DockerContainerManagerFactory.enableRemoteAccess();

        //manager = ContainerManagerFactory.createContainerManager(contex, ContainerType.fromString(contex.getContainerType()));
        this.hostContext = contex;
        status = AgentStatus.CREATED;
    }
    
    synchronized void init() {
        status = AgentStatus.READY;
    }
    
    @Override
    public synchronized void terminate() {
        //FIXME: terminate all instances //
        LifecycleAgentBooter.unregister(this);
        manager.terminate();
    }

    
    @Override
    public synchronized void stop() {
        LifecycleAgentBooter.unregister(this);
    }

    @Override
    public List<ComponentInstanceId> listContainers() throws RemoteException {
        return manager.getAllContainers();
    }

    @Override
    public ComponentInstanceId deployComponent(DeploymentContext ctx, DeployableComponent component, OperatingSystem os, ContainerType containerType) throws RemoteException, LcaException, RegistrationException, ContainerException {
        applicationRegistered(ctx);
        componentPartOfApplication(ctx, component);

        //fixme: add containertype parameter!!!! just for testing plain
        manager = MultiContainerManagerFactory.createContainerManager(this.hostContext, os, containerType);

        ContainerController cc = manager.createNewContainer(ctx, component, os);
        cc.awaitCreation();
        cc.bootstrap();
        cc.awaitBootstrap();
        cc.init(component.getLifecycleStore());
        cc.awaitInitialisation();
        return cc.getId();
    }
    
    private static void applicationRegistered(DeploymentContext ctx) throws LcaException, RegistrationException {
        LcaRegistry reg = ctx.getRegistry();
        ApplicationInstanceId appInstId = ctx.getApplicationInstanceId();
        
        if(reg.applicationInstanceExists(appInstId)) 
            return;
        
        throw new LcaException("cannot proceed: application instance is not known.");
    }
    
    private static void componentPartOfApplication(DeploymentContext ctx, DeployableComponent component) throws RegistrationException, LcaException {
        LcaRegistry reg = ctx.getRegistry();
        ApplicationInstanceId appInstId = ctx.getApplicationInstanceId();
        
        if(reg.applicationComponentExists(appInstId, component.getComponentId())) 
            return;
        
        throw new LcaException("cannot proceed: component is not known within application instance.");
    }

    @Override
    public AgentStatus getAgentStatus() {
        return status;
    }

    @Override
    public ContainerStatus getComponentContainerStatus(ComponentInstanceId cid) {
        return manager.getComponentContainerStatus(cid);
    }
}

@@ -2890,13 +2890,13 @@
   //
-fixme
+FIXME
 : ad
@@ -3334,24 +3334,24 @@
 c.getId();%0D%0A
-
     %7D%0D%0A    %0D
@@ -3343,32 +3343,601 @@
 ;%0D%0A    %7D%0D%0A    %0D%0A
+%09@Override%0D%0A%09public boolean stopComponentInstance( ContainerType containerType, ComponentInstanceId instanceId) throws RemoteException, LcaException, ContainerException %7B%0D%0A%09%09ContainerController cid = manager.getContainer(instanceId);%0D%0A%09%09if(cid == null) %7B %0D%0A%09%09%09throw new LcaException(%22Component instance not known: %22 + instanceId);%0D%0A%09%09%7D%0D%0A%09%09ContainerStatus state = cid.getState();%0D%0A%09%09if(state != ContainerStatus.READY) %7B%0D%0A%09%09%09throw new ContainerException(%22container in wrong state: %22 + state);%0D%0A%09%09%7D%0D%0A%09%09cid.tearDown();%0D%0A%09%09cid.awaitDestruction();%0D%0A%09%09return true;%0D%0A%09%7D%0D%0A    %0D%0A
     private stat

add method for terminating a single component instance.