/**
 * Copyright (c) 2005-2012 EBM WebSourcing, 2012-2015 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see <http://www.gnu.org/licenses/>
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.microkernel.container;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.jbi.JBIException;

import org.objectweb.fractal.adl.ADLException;
import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.BindingController;
import org.objectweb.fractal.api.control.ContentController;
import org.objectweb.fractal.api.control.IllegalBindingException;
import org.objectweb.fractal.api.control.IllegalContentException;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;
import org.objectweb.fractal.api.control.NameController;
import org.objectweb.fractal.api.control.SuperController;
import org.objectweb.fractal.fraclet.annotation.annotations.FractalComponent;
import org.objectweb.fractal.fraclet.annotation.annotations.LifeCycle;
import org.objectweb.fractal.fraclet.annotation.annotations.Provides;
import org.objectweb.fractal.fraclet.annotation.annotations.Service;
import org.objectweb.fractal.fraclet.annotation.annotations.type.LifeCycleType;
import org.objectweb.fractal.util.Fractal;
import org.ow2.petals.basisapi.exception.PetalsException;
import org.ow2.petals.jbi.descriptor.original.generated.Jbi.SharedLibrary;
import org.ow2.petals.microkernel.api.container.SharedLibraryLifeCycle;
import org.ow2.petals.microkernel.api.server.Binding;
import org.ow2.petals.microkernel.api.server.FractalHelper;
import org.ow2.petals.microkernel.api.util.LoggingUtil;
import org.ow2.petals.microkernel.container.lifecycle.ComponentLifeCycleImpl;
import org.ow2.petals.microkernel.container.lifecycle.InstallerImpl;
import org.ow2.petals.microkernel.container.lifecycle.ServiceAssemblyLifeCycleImpl;
import org.ow2.petals.microkernel.container.lifecycle.SharedLibraryLifeCycleImpl;

/**
 * This {@link ContainerServiceImpl} implementation allow to create and handle JBI containers.
 * @author Nicolas SALATGE - EBM WebSourcing
 */
@FractalComponent
@Provides(interfaces = @org.objectweb.fractal.fraclet.annotation.annotations.Interface(name = "service", signature = ContainerService.class))
public class ContainerServiceImpl implements ContainerService {

    /**
     * Fractal name of the container service.
     */
    public static final String FRACTAL_CONTAINER_SERVICE_NAME = "ContainerServiceImpl";

    /**
     * Configuration interface name.
     */
    public static final String CONFIGURATION_ITF = "configuration";

    /**
     * Admin interface name.
     */
    public static final String ADMIN_ITF = "admin";

    /**
     * Classloader interface name.
     */
    public static final String CLASSLOADER_ITF = "classloader";

    /**
     * Router interface name.
     */
    public static final String ROUTER_ITF = "router";

    /**
     * Repository interface name.
     */
    public static final String REPOSITORY_ITF = "repository";

    /**
     * Endpoint interface name.
     */
    public static final String ENDPOINT_ITF = "endpoint";

    /**
     * Endpoint properties finder interface name
     */
    public static final String ENDPOINT_PROPERTIES_ITF = "endpointpropertiesfinder";

    /**
     * JNDI interface name.
     */
    public static final String JNDI_ITF = "jndi";

    /**
     * Systemstate interface name.
     */
    public static final String SYSTEMSTATE_ITF = "systemstate";

    /**
     * Container interface name.
     */
    public static final String CONTAINER_ITF = "container";

    /**
     * Transaction manager interface name.
     */
    public static final String TRANSACTIONMANAGER_ITF = "transactionManager";

    /**
     * the log.
     */
    private LoggingUtil log;

    /**
     * The component.
     */
    @Service
    private Component comp;

    /**
     * The parent component.
     */
    private Component parentcontainer;

    /**
     * Default container.
     * 
     */
    public ContainerServiceImpl() {
    }

    /**
     * Get the parent container.
     * 
     * @return the parent container
     */
    public Component getParentContainer() {
        return this.parentcontainer;
    }

    /**
     * Create an installer from the component description of the component.
     * 
     * @param componentDescription
     *            the component description of the component
     * @return the fractal component of the installer
     * @throws PetalsException
     *             : impossible to create the installer
     */
    public Component createInstaller(
            final org.ow2.petals.jbi.descriptor.original.generated.Component componentDescription)
            throws PetalsException {
        Component installer = null;

        try {
            // Creation of the installer
            installer = FractalHelper.createNewComponent(InstallerImpl.class.getName());

            // Change name of the installer
            NameController nameController = Fractal.getNameController(installer);
            nameController.setFcName(PREFIX_INSTALLER_NAME
                    + componentDescription.getIdentification().getName());

            // Get the content controller of the container
            ContentController containerContentController = Fractal
                    .getContentController(this.parentcontainer);

            // Creation of the list of bindings of the component with others
            // components
            List<Binding> listOfBindings = new ArrayList<Binding>();
            synchronized (this.parentcontainer) {
                listOfBindings.add(new Binding(CONFIGURATION_ITF,
                        (Interface) containerContentController
                                .getFcInternalInterface(CONFIGURATION_ITF)));
                listOfBindings.add(new Binding(ADMIN_ITF, (Interface) containerContentController
                        .getFcInternalInterface(ADMIN_ITF)));
                listOfBindings.add(new Binding(CLASSLOADER_ITF,
                        (Interface) containerContentController
                                .getFcInternalInterface(CLASSLOADER_ITF)));
                listOfBindings.add(new Binding(ROUTER_ITF, (Interface) containerContentController
                        .getFcInternalInterface(ROUTER_ITF)));
                listOfBindings.add(new Binding(REPOSITORY_ITF,
                        (Interface) containerContentController
                                .getFcInternalInterface(REPOSITORY_ITF)));
                listOfBindings.add(new Binding(ENDPOINT_ITF, (Interface) containerContentController
                        .getFcInternalInterface(ENDPOINT_ITF)));
                listOfBindings.add(new Binding(JNDI_ITF, (Interface) containerContentController
                        .getFcInternalInterface(JNDI_ITF)));
                listOfBindings.add(new Binding(SYSTEMSTATE_ITF,
                        (Interface) containerContentController
                                .getFcInternalInterface(SYSTEMSTATE_ITF)));
                // The transaction manager is not available for Quick Start
                // distribution
                try {
                    containerContentController.getFcInternalInterface(TRANSACTIONMANAGER_ITF);
                    listOfBindings.add(new Binding(TRANSACTIONMANAGER_ITF,
                            (Interface) containerContentController
                                    .getFcInternalInterface(TRANSACTIONMANAGER_ITF)));
                } catch (NoSuchInterfaceException e) {
                    // do nothing
                }

                try {
                    // The endpoint properties finder is an optional component
                    containerContentController.getFcInternalInterface(ENDPOINT_PROPERTIES_ITF);
                    listOfBindings.add(new Binding(ENDPOINT_PROPERTIES_ITF,
                            (Interface) containerContentController
                                    .getFcInternalInterface(ENDPOINT_PROPERTIES_ITF)));
                } catch (NoSuchInterfaceException e) {
                    // do nothing
                }

                // Search of this Fractal component
                Component containerService = FractalHelper.getComponentByName(
                        containerContentController, FRACTAL_CONTAINER_SERVICE_NAME);
                listOfBindings.add(new Binding(CONTAINER_ITF, (Interface) containerService
                        .getFcInterface("service")));
            }

            // Add the new intaller in the framework
            FractalHelper.addComponent(installer, this.parentcontainer, listOfBindings);
        } catch (ADLException e) {
            throw new PetalsException(e);
        } catch (NoSuchInterfaceException e) {
            throw new PetalsException(e);
        } catch (IllegalContentException e) {
            throw new PetalsException(e);
        } catch (IllegalLifeCycleException e) {
            throw new PetalsException(e);
        } catch (IllegalBindingException e) {
            throw new PetalsException(e);
        }
        return installer;
    }

    /**
     * Create a component life cycle from the component description of the
     * component.
     * 
     * @param componentDescription
     *            the component description of the component
     * @return the fractal component of the component life cycle
     * @throws PetalsException
     *             : impossible to create a component life cycle
     */
    public Component createComponentLifeCycle(
            final org.ow2.petals.jbi.descriptor.original.generated.Component componentDescription)
            throws PetalsException {
        Component componentLifeCycle = null;
        try {

            // Get the content-controller of the container
            ContentController containerContentController = Fractal
                    .getContentController(this.parentcontainer);

            // Creation of the list of bindings of the component with others
            // components
            List<Binding> listOfBindings = new ArrayList<Binding>();
            synchronized (this.parentcontainer) {
                listOfBindings.add(new Binding(CONFIGURATION_ITF,
                        (Interface) containerContentController
                                .getFcInternalInterface(CONFIGURATION_ITF)));
                listOfBindings.add(new Binding(ADMIN_ITF,
                        (org.objectweb.fractal.api.Interface) containerContentController
                                .getFcInternalInterface(ADMIN_ITF)));
                listOfBindings.add(new Binding(JNDI_ITF,
                        (org.objectweb.fractal.api.Interface) containerContentController
                                .getFcInternalInterface(JNDI_ITF)));
                listOfBindings.add(new Binding(SYSTEMSTATE_ITF,
                        (org.objectweb.fractal.api.Interface) containerContentController
                                .getFcInternalInterface(SYSTEMSTATE_ITF)));

                // Creation of the installer
                componentLifeCycle = FractalHelper.createNewComponent(ComponentLifeCycleImpl.class
                        .getName());

                // Rename of the component life cycle
                NameController nameController = Fractal.getNameController(componentLifeCycle);
                nameController.setFcName(PREFIX_COMPONENT_LIFE_CYCLE_NAME
                        + componentDescription.getIdentification().getName());
            }
            // Add the new lifecycle in the framework
            FractalHelper.addComponent(componentLifeCycle, this.parentcontainer, listOfBindings);

        } catch (ADLException e) {
            throw new PetalsException(e);
        } catch (NoSuchInterfaceException e) {
            throw new PetalsException(e);
        } catch (IllegalContentException e) {
            throw new PetalsException(e);
        } catch (IllegalLifeCycleException e) {
            throw new PetalsException(e);
        } catch (IllegalBindingException e) {
            throw new PetalsException(e);
        }
        return componentLifeCycle;
    }

    /**
     * Remove a component life cycle from the component description of the
     * component.
     * 
     * @param componentDescription
     *            the component description of the component
     * @throws PetalsException
     *             : impossible to remove a component life cycle
     */
    public void removeComponentLifeCycle(
            final org.ow2.petals.jbi.descriptor.original.generated.Component componentDescription)
            throws PetalsException {
        try {

            // Get the content-controller of the container
            ContentController containerContentController = Fractal.getContentController(this
                    .getParentContainer());

            // Get the component lifecycle component
            org.objectweb.fractal.api.Component componentLifeCycle = FractalHelper
                    .getRecursiveComponentByName(containerContentController,
                            PREFIX_COMPONENT_LIFE_CYCLE_NAME
                                    + componentDescription.getIdentification().getName());

            // Stop the Fractal component
            FractalHelper.stopComponent(componentLifeCycle);

            // Remove of all bind
            BindingController lifeCycleBindingController = Fractal
                    .getBindingController(componentLifeCycle);
            for (int i = 0; i < lifeCycleBindingController.listFc().length; i++) {
                lifeCycleBindingController.unbindFc(lifeCycleBindingController.listFc()[i]);
            }

            // Remove the component lifecycle component
            containerContentController.removeFcSubComponent(componentLifeCycle);

        } catch (NoSuchInterfaceException e) {
            throw new PetalsException(e);
        } catch (IllegalContentException e) {
            throw new PetalsException(e);
        } catch (IllegalLifeCycleException e) {
            throw new PetalsException(e);
        } catch (IllegalBindingException e) {
            throw new PetalsException(e);
        }
    }

    /**
     * Remove an installer from the name of the component.
     * 
     * @param name
     *            the name of the component
     * @throws PetalsException
     *             impossible to remove the installer
     */
    public void removeInstaller(String name) throws PetalsException {
        try {

            // Get the content-controller of the container
            ContentController containerContentController = Fractal.getContentController(this
                    .getParentContainer());

            // Get the installer Fractal component
            org.objectweb.fractal.api.Component installer = FractalHelper
                    .getRecursiveComponentByName(containerContentController, PREFIX_INSTALLER_NAME
                            + name);

            // Stop the Fractal component
            FractalHelper.stopComponent(installer);

            // Remove of bound Fractal components
            BindingController lifeCycleBindingController = Fractal.getBindingController(installer);
            for (int i = 0; i < lifeCycleBindingController.listFc().length; i++) {
                if (lifeCycleBindingController.lookupFc(lifeCycleBindingController.listFc()[i]) != null) {
                    lifeCycleBindingController.unbindFc(lifeCycleBindingController.listFc()[i]);
                }
            }

            // Remove the Fractal component
            containerContentController.removeFcSubComponent(installer);

        } catch (NoSuchInterfaceException e) {
            throw new PetalsException(e);
        } catch (IllegalContentException e) {
            throw new PetalsException(e);
        } catch (IllegalLifeCycleException e) {
            throw new PetalsException(e);
        } catch (IllegalBindingException e) {
            throw new PetalsException(e);
        }
    }

    /**
     * Create a service assembly life cycle from the component description of
     * the component.
     * 
     * @param sa
     *            the component description of the component
     * @return the fractal component of the service assembly life cycle
     * @throws PetalsException
     *             impossible to create a service assembly
     */
    public Component createServiceAssemblyLifeCycle(
            final org.ow2.petals.jbi.descriptor.original.generated.ServiceAssembly sa)
            throws PetalsException {
        Component serviceAssemblyLifeCycle = null;
        try {

            // Get the content-controller of the container
            ContentController containerContentController = Fractal
                    .getContentController(this.parentcontainer);

            // Creation of the installer
            serviceAssemblyLifeCycle = FractalHelper
                    .createNewComponent(ServiceAssemblyLifeCycleImpl.class.getName());

            // Creation of the list of bindings of the component with others
            // components
            List<Binding> listOfBindings = new ArrayList<Binding>();

            synchronized (this.parentcontainer) {
                listOfBindings.add(new Binding(SYSTEMSTATE_ITF,
                        (org.objectweb.fractal.api.Interface) containerContentController
                                .getFcInternalInterface(SYSTEMSTATE_ITF)));
                listOfBindings.add(new Binding(ROUTER_ITF,
                        (org.objectweb.fractal.api.Interface) containerContentController
                                .getFcInternalInterface(ROUTER_ITF)));
            }

            // Rename of the component life cycle
            NameController nameController = Fractal.getNameController(serviceAssemblyLifeCycle);
            nameController.setFcName(PREFIX_SERVICE_ASSEMBLY_LIFE_CYCLE_NAME
                    + sa.getIdentification().getName());

            // Add the new lifecycle in the framework
            FractalHelper.addComponent(serviceAssemblyLifeCycle, this.parentcontainer,
                    listOfBindings);

        } catch (ADLException e) {
            throw new PetalsException(e);
        } catch (NoSuchInterfaceException e) {
            throw new PetalsException(e);
        } catch (IllegalContentException e) {
            throw new PetalsException(e);
        } catch (IllegalLifeCycleException e) {
            throw new PetalsException(e);
        } catch (IllegalBindingException e) {
            throw new PetalsException(e);
        }
        return serviceAssemblyLifeCycle;
    }

    /**
     * {@inheritDoc}
     */
    public void removeServiceAssemblyLifeCycle(final String saName)
            throws PetalsException {
        try {

            // Get the content-controller of the container
            ContentController containerContentController = Fractal.getContentController(this
                    .getParentContainer());

            // Get the component sharedLibraryLifeCycle component
            org.objectweb.fractal.api.Component serviceAssemblyLifeCycle = FractalHelper
                    .getRecursiveComponentByName(containerContentController,
                            PREFIX_SERVICE_ASSEMBLY_LIFE_CYCLE_NAME + saName);

            // stop the Fractal component (not in the life-cycle mean...)
            FractalHelper.stopComponent(serviceAssemblyLifeCycle);

            // Remove of all bind
            BindingController lifeCycleBindingController = Fractal
                    .getBindingController(serviceAssemblyLifeCycle);
            for (int i = 0; i < lifeCycleBindingController.listFc().length; i++) {
                lifeCycleBindingController.unbindFc(lifeCycleBindingController.listFc()[i]);
            }

            // Remove the Service Assembly lifecycle component
            containerContentController.removeFcSubComponent(serviceAssemblyLifeCycle);

        } catch (NoSuchInterfaceException e) {
            throw new PetalsException(e);
        } catch (IllegalContentException e) {
            throw new PetalsException(e);
        } catch (IllegalLifeCycleException e) {
            throw new PetalsException(e);
        } catch (IllegalBindingException e) {
            throw new PetalsException(e);
        }
    }

    /**
     * Create a shared library life cycle component.
     * 
     * @param sl
     *            the shared library
     * @return the fractal component of the shared library
     * @throws PetalsException
     *             : occurs when it is impossible to create a shared library
     */
    public Component createSharedLibraryLifeCycle(final SharedLibrary sl) throws PetalsException {
        Component sharedLibraryLifeCycle = null;
        try {

            // Get the content-controller of the container
            ContentController containerContentController = Fractal
                    .getContentController(this.parentcontainer);

            // Creation of the shared library life cycle
            sharedLibraryLifeCycle = FractalHelper
                    .createNewComponent(SharedLibraryLifeCycleImpl.class.getName());

            // Creation of the list of bindings of the component with others
            // components
            List<Binding> listOfBindings = new ArrayList<Binding>();

            synchronized (this.parentcontainer) {
                listOfBindings.add(new Binding(CLASSLOADER_ITF,
                        (org.objectweb.fractal.api.Interface) containerContentController
                                .getFcInternalInterface(CLASSLOADER_ITF)));
            }

            // Rename of the component life cycle
            NameController nameController = Fractal.getNameController(sharedLibraryLifeCycle);
            nameController.setFcName(ContainerServiceImpl.PREFIX_SHARED_LIBRARY_LIFE_CYCLE_NAME
                    + sl.getIdentification().getName() + "-" + sl.getVersion());

            // Add the new lifecycle in the framework
            FractalHelper
                    .addComponent(sharedLibraryLifeCycle, this.parentcontainer, listOfBindings);

        } catch (ADLException e) {
            throw new PetalsException(e);
        } catch (NoSuchInterfaceException e) {
            throw new PetalsException(e);
        } catch (IllegalContentException e) {
            throw new PetalsException(e);
        } catch (IllegalLifeCycleException e) {
            throw new PetalsException(e);
        } catch (IllegalBindingException e) {
            throw new PetalsException(e);
        }
        return sharedLibraryLifeCycle;
    }

    /**
     * Remove a shared library life cycle component.
     * 
     * @param slName
     *            the shared library name
     * @throws PetalsException
     *             : occurs when it is impossible to remove a shared library
     */
    public void removeSharedLibraryLifeCycle(final String slName) throws PetalsException {
        try {

            // Get the content-controller of the container
            ContentController containerContentController = Fractal.getContentController(this
                    .getParentContainer());

            // Get the component sharedLibraryLifeCycle component
            org.objectweb.fractal.api.Component sharedLibraryLifeCycle = FractalHelper
                    .getRecursiveComponentByName(containerContentController,
                            PREFIX_SHARED_LIBRARY_LIFE_CYCLE_NAME + slName);

            // unload of PetalsShared Libraries
            ((SharedLibraryLifeCycle) sharedLibraryLifeCycle.getFcInterface("service"))
                    .unLoadSharedLibrary();

            // Stop the component
            FractalHelper.stopComponent(sharedLibraryLifeCycle);

            // Remove of all bind
            BindingController lifeCycleBindingController = Fractal
                    .getBindingController(sharedLibraryLifeCycle);
            for (int i = 0; i < lifeCycleBindingController.listFc().length; i++) {
                lifeCycleBindingController.unbindFc(lifeCycleBindingController.listFc()[i]);
            }

            // Remove the component lifecycle component
            containerContentController.removeFcSubComponent(sharedLibraryLifeCycle);

        } catch (NoSuchInterfaceException e) {
            throw new PetalsException(e);
        } catch (IllegalContentException e) {
            throw new PetalsException(e);
        } catch (IllegalLifeCycleException e) {
            throw new PetalsException(e);
        } catch (IllegalBindingException e) {
            throw new PetalsException(e);
        }
    }

    /**
     * Initialization of the petals component.
     * 
     * @throws PetalsException
     *             : impossible to start the component
     * @throws JBIException
     *             : JBI error
     * @throws NoSuchInterfaceException
     *             : Fractal Error
     * @throws ADLException
     *             : Fractal error
     * 
     */
    @LifeCycle(on = LifeCycleType.START)
    protected void start() throws PetalsException, NoSuchInterfaceException {
        this.log = new LoggingUtil(Logger.getLogger(Constants.FRACTAL_COMPONENT_LOGGER_NAME));
        this.log.start();

        try {
            SuperController sc = Fractal.getSuperController(this.comp);
            if (sc.getFcSuperComponents().length != 1) {
                throw new PetalsException("Error in fractal architecture");
            }
            this.parentcontainer = sc.getFcSuperComponents()[0];
        } catch (PetalsException e) {
            this.log.error("Failed to start Container service", e);
            throw e;
        } catch (NoSuchInterfaceException e) {
            this.log.error("Failed to start Container service", e);
            throw e;
        }

        this.log.end();
    }

    /**
     * Stop of the petals component.
     * 
     */
    @LifeCycle(on = LifeCycleType.STOP)
    protected void stop() {
        this.log.call();
    }

}
