/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2011 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This 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 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.addon.deploy.impl.deployer;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.ow2.jonas.addon.deploy.api.config.IAddonConfig;
import org.ow2.jonas.addon.deploy.api.deployable.IAddonDeployable;
import org.ow2.jonas.addon.deploy.api.deployable.IAddonMetadata;
import org.ow2.jonas.addon.deploy.api.deployer.IAddonDeployer;
import org.ow2.jonas.addon.deploy.api.deployer.IConfDeployer;
import org.ow2.jonas.addon.deploy.impl.config.AddonConfigImpl;
import org.ow2.jonas.addon.deploy.impl.deployable.AddonDeployableImpl;
import org.ow2.jonas.addon.deploy.impl.util.AddonUtil;
import org.ow2.util.archive.api.ArchiveException;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.ow2.util.file.FileUtils;
import org.ow2.util.file.FileUtilsException;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Dictionary;
import java.util.Hashtable;

/**
 * Represents a conf deployer
 * @author Jérémy Cazaux
 */
public class ConfDeployerImpl implements IConfDeployer {

    /**
     * The logger
     */
    private static Log logger = LogFactory.getLog(ConfDeployerImpl.class);

    /**
     * The property name to register a new IAddonConfig component as an OSGi service
     */
    public final String SERVICE_REGISTRATION_ADDON_PROPERTY = "addon";

    /**
     * OSGi bundle context
     */
    private BundleContext bundleContext;

    /**
     * List of service registration for IAddonConfig components
     */
    private List<ServiceRegistration> serviceRegistrations;

    /**
     * The addon deployer
      */
    private IAddonDeployer addonDeployer;

    /**
     * Default constructor
     * @param bundleContext OSGi bundle context
     */
    public ConfDeployerImpl(final BundleContext bundleContext) {
        this.bundleContext = bundleContext;
        this.serviceRegistrations = new ArrayList<ServiceRegistration>();
    }

    /**
     * {@inheritDoc}
     */
    public void deploy(final IAddonDeployable unpackedDeployable) throws DeployerException {
        try {
            //install configuration files
            install(unpackedDeployable);

            //add access to the configuration files of the given addon through the OSGi registry
            try {
                start(unpackedDeployable.getMetadata().getName());
            } catch (DeployerException e) {
                throw new DeployerException("Could not add access to the configuration files of the addon " + unpackedDeployable.getMetadata().getName()
                        + " through the OSGi registry.", e);
            }
        } catch (Exception e) {
            throw new DeployerException("Could not install configuration files of the deployable " + unpackedDeployable.getShortName(), e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void install(final IAddonDeployable unpackedDeployable) throws DeployerException {
        //get configuration files
        String addonConfigurationPath = null;
        String addonDirectory;
        try {
            //get addon directory
            addonDirectory = unpackedDeployable.getArchive().getURL().getPath();
            addonConfigurationPath = addonDirectory + IAddonDeployable.CONF_DIRECTORY;
        } catch (ArchiveException e) {
            logger.error("Cant get URL archive to install", e);
        }

        //get addon metadata
        IAddonMetadata addonMetadata = unpackedDeployable.getMetadata();

        if (addonMetadata != null && addonMetadata.getName() != null && addonMetadata.getMetaDataFile() != null) {
            final String addonName = addonMetadata.getName();
            final String JOnASAddonDirectoryPath = AddonUtil.getAddonDirectoryPath(addonName);

            //create addons directory in $JONAS_BASE/conf if not exist
            File JOnASAddonDirectory = new File(JOnASAddonDirectoryPath);
            if (!JOnASAddonDirectory.exists()) {
                JOnASAddonDirectory.mkdirs();
            }

            //copy addon metadata
            File addonMetaDataFile = addonMetadata.getMetaDataFile();
            try {
                FileUtils.copyFile(addonMetaDataFile.getAbsolutePath(), JOnASAddonDirectory + File.separator + addonMetaDataFile.getName());
            } catch (FileUtilsException e) {
                logger.error("Cannot copy file " + addonMetaDataFile.getAbsolutePath() + ".", e);
            }

            //copy others configuration files
            AddonUtil.copyFiles(addonConfigurationPath, JOnASAddonDirectoryPath);

            //add configuration directory to the deployable
            AddonDeployableImpl addon = AddonDeployableImpl.class.cast(unpackedDeployable);
            addon.setConfigurationDirectory(JOnASAddonDirectory.getAbsoluteFile());

        } else {
            throw new DeployerException("Configuration file couldn't be installed");
        }
    }

    /**
     * {@inheritDoc}
     */
    public void start(final String addonName) throws DeployerException {

        //check that the name is not null
        if (addonName == null) {
            throw new DeployerException("The name of the addon to start could not be null");
        }

        //compute the configuration directory of an addon
        String directoryPath = AddonUtil.getAddonDirectoryPath(addonName);

        //check that the directory exist
        if (!new File(directoryPath).exists()) {
            throw new DeployerException("Directory " + directoryPath + " doesn't exist ");
        }

        //register a new IAddonConfig component as an OSGi service
        try {
            register(directoryPath, addonName);
        } catch (Exception e) {
            throw new DeployerException("Cannot register a new IAddonConfig component with " + directoryPath + " directory", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void stop(final String addonName) throws DeployerException {

        //check that the name is not null
        if (addonName == null) {
            throw new DeployerException("The name of the addon to start could not be null");
        }

        //unregister the IAddonConfig component with the given addon name
        unregister(addonName);
    }

    /**
     * {@inheritDoc}
     */
    public void uninstall(final String addonName) throws DeployerException {

        //check that the name is not null
        if (addonName == null) {
            throw new DeployerException("The name of the addon to start could not be null");
        }

        //deletion of addon directory in $JONAS_BASE/conf
        String directoryPath = AddonUtil.getAddonDirectoryPath(addonName);
        File file = new File(directoryPath);
        if (file.exists()) {
            if (!FileUtils.delete(directoryPath)) {
                logger.error("Could not delete directory " + directoryPath);
            }
        }

        //remove configuration directory to the deployable
        if (this.addonDeployer != null) {
            IAddonDeployable addon = this.addonDeployer.getAddon(addonName);
            if (addon != null) {
                (AddonDeployableImpl.class.cast(addon)).setConfigurationDirectory(null);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void undeploy(final IAddonDeployable unpackedDeployable) {

        //get addon metadata
        IAddonMetadata addonMetadata = unpackedDeployable.getMetadata();

        if (addonMetadata != null) {
            //get the name of the addon to undeployed
            String addonName = addonMetadata.getName();

            //remove access to configuration files of the addon
            try {
                stop(addonName);
            } catch (DeployerException e) {
                logger.error("Could not remove access to configuration files of the addon " + addonName);
            } finally {
                //uninstall the addon
                try {
                    uninstall(addonName);
                } catch (DeployerException e) {
                    logger.error("Could not uninstall configuration files of the addon " + addonName);
                }
            }
        } else {
            logger.error("Could not retrieve metadata of the addon " + unpackedDeployable.getShortName() +
                    ". Configuration files couldn't be undeployed");
        }
    }

    /**
     * Register a new IAddonConfig component
     * @param configurationDirectory The configuration directory of the addon
     * @param addonName The name of the addon
     */
    private void register(final String configurationDirectory, final String addonName) throws Exception {
        IAddonConfig addonConfig = new AddonConfigImpl(configurationDirectory);

        Dictionary<String, String> dictionary = new Hashtable<String, String>();
        dictionary.put(SERVICE_REGISTRATION_ADDON_PROPERTY, addonName);
        this.serviceRegistrations.add(this.bundleContext.registerService(IAddonConfig.class.getName(), addonConfig, dictionary));
    }

    /**
     * Unregister an IAddonConfig component associated to the given addon name
     * @param addonName The name of an addon
     */
    private void unregister(final String addonName) {
        ServiceRegistration serviceRegistration = AddonUtil.getServiceRegistration(this.serviceRegistrations,
                SERVICE_REGISTRATION_ADDON_PROPERTY, addonName);
        if (serviceRegistration != null) {
            serviceRegistration.unregister();
            this.serviceRegistrations.remove(serviceRegistration);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void setAddonDeployer(final IAddonDeployer addonDeployer) {
        this.addonDeployer = addonDeployer;
    }
}
