/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2005 Bull S.A.
 * Contact: jonas-team@objectweb.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.objectweb.jonas.ant;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
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.objectweb.jonas.ant.cluster.Common;
import org.objectweb.jonas.ant.cluster.ClusterDaemon;
import org.objectweb.jonas.ant.cluster.ClusterTasks;
import org.objectweb.jonas.ant.cluster.EjbLevel;
import org.objectweb.jonas.ant.cluster.Script;

import org.objectweb.jonas.ant.cluster.WebLevel;

import org.objectweb.jonas.ant.jonasbase.BaseTaskItf;
import org.objectweb.jonas.ant.jonasbase.Tasks;

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

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

    /**
     * Archictecture 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 tasks = null;


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

    /**
     * Creates a new JONAS_BASE or update it
     * @param destDir destination directory
     */
    private void createJonasBase(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);

                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();
    }
    
    /**
     * Updates a JONAS_BASE
     * @param destDir destination directory
     */
    private void updateJonasBase(String destDir) {
        File destDirFile = new File(destDir);
        JOnASAntTool.updateJonasBase(this, jonasRoot, destDirFile);
    }
    
    /**
     * Run this task
     * @see org.apache.tools.ant.Task#execute()
     */
    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 (Iterator it = tasks.iterator(); it.hasNext();) {
            BaseTaskItf task = (BaseTaskItf) it.next();
            task.setJonasRoot(jonasRoot);
            JOnASAntTool.configure(this, (Task) task);
            String info = 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(Tasks subTasks) {
        if (subTasks != null) {
            for (Iterator it = subTasks.getTasks().iterator(); it.hasNext();) {
                tasks.add(it.next());
            }
        }
    }

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

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

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

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

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

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

    /**
     * Add tasks for the web level instances
     * If choosen 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(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 choosen 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(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(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(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(boolean onlyUpdate) {
        this.onlyUpdate = onlyUpdate;
    }

}