/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2005-2008 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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * Initial developer: Benoit Pelletier
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ant;

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

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.types.FileSet;
import org.ow2.jonas.ant.cluster.ClusterDaemon;
import org.ow2.jonas.ant.cluster.ClusterTasks;
import org.ow2.jonas.ant.cluster.Common;
import org.ow2.jonas.ant.cluster.EjbLevel;
import org.ow2.jonas.ant.cluster.Script;
import org.ow2.jonas.ant.cluster.WebLevel;
import org.ow2.jonas.ant.jonasbase.BaseTaskItf;
import org.ow2.jonas.ant.jonasbase.JMkdir;
import org.ow2.jonas.ant.jonasbase.Tasks;


/**
 * Class used to create a JOnAS Cluster Configuration
 * @author Benoit Pelletier
 */
public class JOnASClusterConfigTask extends Task {

    /**
     * Architecture of the JOnAS's instances : contain both Web level and Ejb level
     */
    private static final String ARCH_BOTH_WEB_EJB = "bothWebEjb";

    /**
     * Architecture of the JOnAS's instances : separate Web level and Ejb level
     */
    private static final String ARCH_DIFF_WEB_EJB = "diffWebEjb";

    /**
     * Source directory (JOnAS root)
     */
    private File jonasRoot = null;

    /**
     * Destination directory prefix (used to create the jonasBase(s))
     */
    private String destDirPrefix = null;

    /**
     * cluster daemon destination directory
     */
    private String cdDir = null;

    /**
     * Cluster architecture : share instance for web/ejb or separate them
     */
    private String arch = null;

    /**
     * number of JOnAS's instance for web level
     */
    private int webInstNb = -1;

    /**
     * number of JOnAS's instance for ejb level
     */
    private int ejbInstNb = -1;

    /**
     * Update only JONAS_BASE without erasing it
     */
    private boolean onlyUpdate = false;

    /**
     * List of tasks to do
     */
    private List<Task> tasks = null;

    /**
     * Name of logs directory.
     */
    public static final String LOG_DIR_NAME = "logs";

    /**
     * Name of deploy directory.
     */
    public static final String DEPLOY_DIR_NAME = "deploy";

    /**
     * Name of lib/ext directory.
     */
    public static final String LIB_EXT_DIR_NAME = "lib/ext";

    /**
     * List of deployment plans to copy for each JONAS_BASE.
     */
    public static final String[] DEPLOYMENT_PLAN_LIST =  new String[] {"jonasAdmin.xml",
                                                                       "mejb.xml",
                                                                       "ctxroot.xml",
                                                                       "doc.xml"};

    /**
     * Constructor
     */
    public JOnASClusterConfigTask() {
        tasks = new ArrayList<Task>();
    }

    /**
     * Creates a new JONAS_BASE or update it
     * @param destDir destination directory
     */
    private void createJonasBase(final String destDir) {
        File destDirFile = new File(destDir);
        File jprops = new File(destDir + File.separator + "conf" + File.separator + "jonas.properties");

        if (onlyUpdate) {
            if (jprops.exists()) {
                log("Only updating JONAS_BASE in the directory '" + destDir + "' from source directory '" + jonasRoot + "'",
                    Project.MSG_INFO);
                JOnASAntTool.updateJonasBase(this, jonasRoot, destDirFile, DEPLOYMENT_PLAN_LIST);

                return;
            } else {
                throw new BuildException("JOnAS base directory '" + destDir + "' doesn't exists. Cannot update.");
            }
        }

        log("Creating JONAS_BASE in the directory '" + destDir + "' from source directory '" + jonasRoot + "'",
                Project.MSG_INFO);
        Copy copy = new Copy();
        JOnASAntTool.configure(this, copy);
        copy.setTodir(destDirFile);
        FileSet fileSet = new FileSet();
        fileSet.setDir(new File(new File(jonasRoot, "templates"), "conf"));
        copy.addFileset(fileSet);
        copy.setOverwrite(true);
        copy.execute();

        log("Creating logs in the directory '" + destDir, Project.MSG_INFO);
        JMkdir mkdir = new JMkdir();
        JOnASAntTool.configure(this, mkdir);
        mkdir.setDestDir(new File(destDir + File.separator + LOG_DIR_NAME));
        mkdir.execute();

        log("Creating deploy in the directory '" + destDir, Project.MSG_INFO);
        mkdir = new JMkdir();
        JOnASAntTool.configure(this, mkdir);
        mkdir.setDestDir(new File(destDir + File.separator + DEPLOY_DIR_NAME));
        mkdir.execute();

        log("Creating lib/ext in the directory '" + destDir, Project.MSG_INFO);
        mkdir = new JMkdir();
        JOnASAntTool.configure(this, mkdir);
        mkdir.setDestDir(new File(destDir + File.separator + LIB_EXT_DIR_NAME));
        mkdir.execute();
    }

    /**
     * Updates a JONAS_BASE
     * @param destDir destination directory
     */
    private void updateJonasBase(final String destDir) {
        File destDirFile = new File(destDir);
        JOnASAntTool.updateJonasBase(this, jonasRoot, destDirFile, DEPLOYMENT_PLAN_LIST);
    }

    /**
     * Run this task
     * @see org.apache.tools.ant.Task#execute()
     */
    @Override
    public void execute() {
        if (jonasRoot == null) {
            throw new BuildException("jonasRoot element is not set");
        }

        if (destDirPrefix == null) {
            throw new BuildException("destDirPrefix element is not set");
        }

        if (cdDir == null) {
            throw new BuildException("cdDir element is not set");
        }

        if (jonasRoot.getPath().equals(destDirPrefix)) {
            throw new BuildException("jonasRoot and destDirPrefix is the same path !");
        }

        // First, JONAS_BASE creation
        for (int i = 1; i <= webInstNb + ejbInstNb; i++) {

            String destDir = ClusterTasks.getDestDir(destDirPrefix, i);
            createJonasBase(destDir);
        }

        // Creates JONAS_BASE for the cluster daemon
        createJonasBase(cdDir);

        // 2nd, executes the tasks
        for (Task task : tasks) {
            ((BaseTaskItf) task).setJonasRoot(jonasRoot);
            JOnASAntTool.configure(this, task);
            String info = ((BaseTaskItf) task).getLogInfo();
            if (info != null) {
                log(info, Project.MSG_INFO);
            }

            task.execute();
        }

        // Then update JonasBase
        for (int i = 1; i <= webInstNb + ejbInstNb; i++) {
            String destDir = ClusterTasks.getDestDir(destDirPrefix, i);
            updateJonasBase(destDir);
        }

        // Update JONAS_BASE for the cluster daemon
        updateJonasBase(cdDir);
    }


    /**
     * Add tasks for configured object
     * @param subTasks some tasks to do on files
     */
    public void addTasks(final Tasks subTasks) {
        if (subTasks != null) {
            for (Task task : subTasks.getTasks()) {
                tasks.add(task);
            }
        }
    }

    /**
     * Set the destination directory prefix
     * @param destDirPrefix the destination directory prefix
     */
    public void setDestDirPrefix(final String destDirPrefix) {
        this.destDirPrefix = destDirPrefix;
    }

    /**
     * Set the cluster daemon directory
     * @param cdDir the destination directory for the cluster daemon configuration
     */
    public void setCdDir(final String cdDir) {
        this.cdDir = cdDir;
    }

    /**
     * Set the source directory for the replacement
     * @param jonasRoot the source directory
     */
    public void setJonasRoot(final File jonasRoot) {
        this.jonasRoot = jonasRoot;
    }

    /**
     * Set architecture
     * @param arch the architecture
     */
    public void setArch(final String arch) {
        this.arch = arch;
    }

    /**
     * Set the web instances number
     * @param webInstNb number of web instances
     */
    public void setWebInstNb(final int webInstNb) {
        this.webInstNb = webInstNb;
    }

    /**
     * Set the ejb instances number
     * @param ejbInstNb number of ejb instances
     */
    public void setEjbInstNb(final int ejbInstNb) {
        this.ejbInstNb = ejbInstNb;
    }

    /**
     * Add tasks for the common instances (both web & ejb )
     * @param common common tasks to do
     */
    public void addConfiguredCommon(final Common common) {
        common.setRootTask(this);
        common.setDestDirPrefix(destDirPrefix);
        common.setDestDirSuffixIndFirst(1);
        common.setDestDirSuffixIndLast(webInstNb + ejbInstNb);
        common.setArch(arch);
        common.setWebInstNb(webInstNb);
        common.setEjbInstNb(ejbInstNb);
        common.generatesTasks();
        addTasks(common);
    }

    /**
     * Add tasks for the web level instances
     * If chosen architecture is both web and ejb within the same JOnAS,
     * the tasks are added to all instances
     * @param webLevel tasks to do on files
     */
    public void addConfiguredWebLevel(final WebLevel webLevel) {
        webLevel.setRootTask(this);
        webLevel.setDestDirPrefix(destDirPrefix);
        webLevel.setArch(arch);

        if (arch.compareTo(ARCH_BOTH_WEB_EJB) == 0) {
            webLevel.setDestDirSuffixIndFirst(1);
            webLevel.setDestDirSuffixIndLast(webInstNb + ejbInstNb);
        } else if (arch.compareTo(ARCH_DIFF_WEB_EJB) == 0) {
            webLevel.setDestDirSuffixIndFirst(1);
            webLevel.setDestDirSuffixIndLast(webInstNb);
        } else {
            handleErrorOutput("Invalide architecture choice : " + arch);
        }
        webLevel.generatesTasks();
        addTasks(webLevel);
    }

    /**
     * Add tasks for the ejb level instances
     * If chosen architecture is both web and ejb within the same JOnAS,
     * the tasks are added to all instances
     * @param ejbLevel tasks to do on files
     */
    public void addConfiguredEjbLevel(final EjbLevel ejbLevel) {
        ejbLevel.setRootTask(this);
        ejbLevel.setDestDirPrefix(destDirPrefix);
        ejbLevel.setArch(arch);

        if (arch.compareTo(ARCH_BOTH_WEB_EJB) == 0) {
            ejbLevel.setDestDirSuffixIndFirst(1);
            ejbLevel.setDestDirSuffixIndLast(webInstNb + ejbInstNb);
        } else if (arch.compareTo(ARCH_DIFF_WEB_EJB) == 0) {
            ejbLevel.setDestDirSuffixIndFirst(webInstNb + 1);
            ejbLevel.setDestDirSuffixIndLast(webInstNb + ejbInstNb);
        } else {
            handleErrorOutput("Invalide architecture choice : " + arch);
        }
        ejbLevel.generatesTasks();
        addTasks(ejbLevel);
    }

    /**
     * Add tasks for the is
     * @param script Tasks to do on files
     */
    public void addConfiguredScript(final Script script) {
        script.setRootTask(this);
        script.setLogInfo("Script");
        script.setInstNb(webInstNb + ejbInstNb);
        script.setDestDirPrefix(destDirPrefix);
        script.generatesTasks();
        addTasks(script);
    }

    /**
     * Add tasks for the is
     * @param script Tasks to do on files
     */
    public void addConfiguredClusterDaemon(final ClusterDaemon clusterDaemon) {
        clusterDaemon.setRootTask(this);
        clusterDaemon.setLogInfo("ClusterDaemon");
        clusterDaemon.setInstNb(webInstNb + ejbInstNb);
        clusterDaemon.setDestDirPrefix(destDirPrefix);
        clusterDaemon.setJonasRoot(jonasRoot.getAbsolutePath());
        clusterDaemon.generatesTasks();
        addTasks(clusterDaemon);
    }

    /**
     * Set if this is only an update or a new JONAS_BASE
     * @param onlyUpdate If true update, else create and then update
     */
    public void setUpdate(final boolean onlyUpdate) {
        this.onlyUpdate = onlyUpdate;
    }

}
