/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-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
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
*/
package org.ow2.jonas.ant;

import java.io.File;

import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.types.Path;

/**
 * Launch WsGen from Ant.
 *
 * @author Guillaume Sauthier
 */
public class WsGenTask extends MatchingTask {

    /** Bootstrap class name. */
    private static final String BOOTSTRAP_CLASS = "org.ow2.jonas.client.boot.Bootstrap";

    /** WsGen class name. */
    private static final String WSGEN_CLASS = "org.ow2.jonas.generators.wsgen.WsGen";

    /** destination directory. */
    private File destination   = null;

    /** source directory. */
    private File source        = null;

    /** validation of XML files ? */
    private boolean validation = true;

    /** name of javac command. */
    private String javac       = null;

    /** list of javac options. */
    private String javacOpts  = null;

    /** will WsGen keep already generated files ? */
    private boolean keepGenerated   = false;

    /** Will WsGen create configuration files ? */
    private boolean noConfig  = false;

    /** verbose mode. */
    private boolean verbose    = false;

    /** debug mode. */
    private boolean debug      = false;

    /** JVM debug mode. */
    private boolean jvmDebug      = false;

    /** $JONAS_ROOT. */
    private File jonasRoot    = null;

    /** $JONAS_BASE. */
    private File jonasBase    = null;

    /**
     * Set the output Directory.
     * @param d output dir
     */
    public void setDestdir(final File d) {
        destination = d;
    }

    /**
     * Set where source jar files are located.
     * @param s sources dir
     */
    public void setSrcdir(final File s) {
        source = s;
    }

    /**
     * Set XML Validation on/off.
     * @param v on/off
     */
    public void setValidation(final boolean v) {
        validation = v;
    }

    /**
     * Set the javac command line.
     * @param j javac to use
     */
    public void setJavac(final String j) {
        javac = j;
    }

    /**
     * Set Java Compiler Options.
     * @param opts Options
     */
    public void setJavacopts(final String opts) {
        javacOpts = opts;
    }

    /**
     * Set keep Generated mode to on/off.
     * @param k on/off
     */
    public void setKeepgen(final boolean k) {
        keepGenerated = k;
    }

    /**
     * Set noConfig mode to on/off.
     * @param c on/off
     */
    public void setNoconfig(final boolean c) {
        noConfig = c;
    }

    /**
     * Set WSDL2Java verbose mode to on/off.
     * @param v on/off
     */
    public void setVerbose(final boolean v) {
        verbose = v;
    }

    /**
     * Set WSDL2Java debug mode to on/off.
     * @param b on/off
     */
    public void setDebug(final boolean b) {
        debug = b;
    }

    /**
     * Set debug mode on/off. Used only by developers that wants to Debug WsGen.
     * @param d boolean
     */
    public void setJvmdebug(final boolean d) {
        jvmDebug = d;
    }

    /**
     * Set the JONAS_ROOT to use.
     * @param jr JONAS_ROOT
     */
    public void setJonasroot(final File jr) {
        jonasRoot = jr;
    }

    /**
     * Set the JONAS_BASE to use.
     * @param jb JONAS_BASE
     */
    public void setJonasbase(final File jb) {
        jonasBase = jb;
    }

    /**
     * Execute the WsGen Ant Task.
     * @throws BuildException if wsgen cannot finish
     */
    public void execute() throws BuildException {

        // init the wsgen task
        initWsGenTask();

        // execute
        DirectoryScanner ds = getDirectoryScanner(source);
        ds.scan();
        String[] files = ds.getIncludedFiles();

        for (int i = 0; i < files.length; i++) {

            Java wsgen = createWsGen(source + File.separator + files[i]);

            // calling WsGen task
            log("Calling  WsGen task for '" + source + File.separator + files[i] + "'.", Project.MSG_VERBOSE);

            if (wsgen.executeJava() != 0) {
                throw new BuildException("WsGen reported an error.");
            }
        }

    }

    /**
     * Create and configure the WsGen Java task.
     * @param filename input file name to be processed by WsGen
     * @return the configured Java Ant Task
     * @throws BuildException if the Java Task cannot be created
     */
    private Java createWsGen(final String filename) throws BuildException {

        Java wsgenTask = null;  // WsGen task

        wsgenTask = (Java) getProject().createTask("java");
        wsgenTask.setTaskName("wsgen");
        wsgenTask.setFork(true);

        // jonas root
        wsgenTask.addSysproperty(JOnASAntTool.createVariable("jonas.root", jonasRoot));

        // jonas base
        wsgenTask.addSysproperty(JOnASAntTool.createVariable("jonas.base", jonasBase));

        // Endorsed directory
        File endorsedDir = new File(new File(jonasRoot, "lib"), "endorsed");
        wsgenTask.addSysproperty(JOnASAntTool.createVariable("java.endorsed.dirs", endorsedDir));


        // java policy file
        String jonasConfigDir = jonasRoot + File.separator + "conf";
        File javaPolicyFile = new File(jonasConfigDir, "java.policy");
        if (javaPolicyFile.exists()) {
            wsgenTask.addSysproperty(JOnASAntTool.createVariable("java.security.policy", javaPolicyFile));
        }

        // JVM Debug
        if (jvmDebug) {
            this.log("Launching in debug mode on port 12345, waiting for connection ...", Project.MSG_INFO);
            wsgenTask.createJvmarg().setLine("-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,address=12345,suspend=y");
        }

        // The bootstrap class must launch the GenIC class
        wsgenTask.createArg().setValue(WSGEN_CLASS);

        wsgenTask.createArg().setValue("-d");
        wsgenTask.createArg().setFile(destination);

        // add ow_jonas_bootstrap.jar
        String bootJar = jonasRoot + File.separator + "lib" + File.separator + "bootstrap"
            + File.separator + "client-bootstrap.jar";
        Path classpath = new Path(getProject(), bootJar);

        log("Using classpath: " + classpath.toString(), Project.MSG_VERBOSE);
        wsgenTask.setClasspath(classpath);

        if (!checkBootstrapClassName(classpath)) {
            log("Cannot find bootstrap class in classpath.", Project.MSG_ERR);
            throw new BuildException("Bootstrap class not found, please check the classpath.");
        } else {
            wsgenTask.setClassname(BOOTSTRAP_CLASS);
        }

        // keepgenerated
        if (keepGenerated) {
            wsgenTask.createArg().setValue("-keepgenerated");
        }

        // noconfig
        if (noConfig) {
            wsgenTask.createArg().setValue("-noconfig");
        }

        // novalidation
        if (!validation) {
            wsgenTask.createArg().setValue("-novalidation");
        }

        // javac
        if (javac != null) {
            wsgenTask.createArg().setValue("-javac");
            wsgenTask.createArg().setLine(javac);
        }

        // javacopts
        if (javacOpts != null && !javacOpts.equals("")) {
            wsgenTask.createArg().setValue("-javacopts");
            wsgenTask.createArg().setLine(javacOpts);
        }

        // verbose
        if (verbose) {
            wsgenTask.createArg().setValue("-verbose");
        }

        // debug
        if (debug) {
            wsgenTask.createArg().setValue("-debug");
        }

        // input file to process by GenIC
        wsgenTask.createArg().setValue(filename);

        return wsgenTask;

    }

    /**
     * Initialize build properties.
     * @throws BuildException if required properties are not set
     */
    private void initWsGenTask() throws BuildException {
        // try to define jonasRoot if not set
        if (jonasRoot == null) {
            // get ant property
            String jr = getProject().getProperty("jonas.root");
            jonasRoot = new File(jr);

            if (jr == null) {
                throw new BuildException("attribute jonasroot is necessary.");
            }
        }

        // use jonasRoot as jonasBase if not set
        if (jonasBase == null) {
            String jb = getProject().getProperty("jonas.base");
            if (jb == null) {
                jonasBase = jonasRoot;
            } else {
                jonasBase = new File(jb);
            }

        }

        // default destination : project directory
        if (destination == null) {
            destination = getProject().getBaseDir();
        }

        // javac javacOpts
        // by default WsGen already handle them

    }

    /**
     * Check the bootstrap class name to use in the given classpath.
     *
     * @param classpath classpath where the boostrap class must be searched.
     * @return true if the bootstrap is available in the classpath
     */
    private boolean checkBootstrapClassName(final Path classpath) {
        log("Looking for bootstrap class in classpath: " + classpath.toString(), Project.MSG_VERBOSE);
        AntClassLoader cl = new AntClassLoader(classpath.getProject(), classpath);
        try {
            cl.loadClass(BOOTSTRAP_CLASS);
            log("Found Bootstrap class '" + BOOTSTRAP_CLASS
                + "' in classpath.", Project.MSG_VERBOSE);
        } catch (ClassNotFoundException cnf1) {
            log("Bootstrap class '" + BOOTSTRAP_CLASS
                + "' not found in classpath.", Project.MSG_VERBOSE);
            return false;
        }
        return true;
    }

}
