package de.renew.ant;

import java.io.File;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Mkdir;
import org.apache.tools.ant.types.FileSet;


/**
 * Enhances the <code>JavaCC</code> task delivered with Ant to
 * work on a file set. The generated Java source of all processed
 * grammars can be put in a destination directory tree similar to
 * the one where the grammar files are kept.
 *
 * @author "Timo Carl" <a href="6carl@informatik.uni-hamburg.de">6carl@informatik.uni-hamburg.de</a>
 *
 * @see org.apache.tools.ant.taskdefs.optional.javacc.JavaCC
 **/
public abstract class AbstractEnhancedJavaCCTask extends Task {
    /**
     * Home directory of javaCC.
     */
    protected File _javacchome;
    /**
     * The destination directory in which to execute javaCC.
     */
    protected File _destdir;
    /**
     * A fileset of which the home directory should be scanned.
     */
    protected FileSet _fileset;
    /**
     * Directory of the file set.
     */
    protected File _srcbase;
    /**
     * Name of the task.
     */
    protected String _singleTaskName;

    /**
     * Constructor of the class, instantiates a new Object.
     * @param singleTaskName Name of the newly created task.
     */
    public AbstractEnhancedJavaCCTask(String singleTaskName) {
        this._singleTaskName = singleTaskName;
    }

    @Override
    public void init() throws BuildException {
        super.init();
    }

    /**
     * Sets the destination directory to execute javaCC.
     * @param destdir The destination directory.
     */
    public void setDestdir(File destdir) {
        this._destdir = destdir;
    }

    /**
     * Sets the home directory of javaCC.
     * @param javacc The home directory.
     */
    public void setJavacchome(File javacc) {
        this._javacchome = javacc;
    }

    /**
     * Sets the desired fileset and its directory.
     * @param fileset The desired fileset which is to be scanned.
     */
    public void addConfiguredFileset(FileSet fileset) {
        this._fileset = fileset;
        this._srcbase = fileset.getDir(getProject());
    }

    /**
     * Sets the source directory of the scanned fileset independently.
     * @param srcbase The desired directory.
     */
    public void setSrcbase(File srcbase) {
        this._srcbase = srcbase;
    }

    @Override
    public void execute() throws BuildException {
        DirectoryScanner ds = _fileset.getDirectoryScanner(getProject());
        String[] javaccFiles = ds.getIncludedFiles();
        File basedir = ds.getBasedir();
        for (int i = 0; i < javaccFiles.length; i++) {
            File f = new File(basedir.getAbsolutePath() + File.separator + javaccFiles[i]);
            if (!f.isFile()) {
                throw new BuildException("javacc-File " + f + " does not exist!");
            }
            mkdirAndExecuteJavaCC(f);
        }
    }

    /**
     * Creates the necessary directory if not existing and executes the task.
     * @param javaccFile The used javaccFile.
     * @throws BuildException When failing to initialise or execute the task.
     */
    protected void mkdirAndExecuteJavaCC(File javaccFile) throws BuildException {
        File directory = getGenerationDirectory(javaccFile);

        // Create dir if not exist
        if (!directory.isDirectory()) {
            Mkdir mkdir = (Mkdir) getProject().createTask("mkdir");
            mkdir.setDir(directory);
            mkdir.init();
            mkdir.execute();
        }
        executeJavaCC(javaccFile, directory);
    }

    /**
     * Executes the defined task.
     * @param javaccFile The target file for the task execution.
     * @param directory The output directory for the task execution.
     * @throws BuildException When failing to initialise or execute the task.
     */
    protected abstract void executeJavaCC(File javaccFile, File directory) throws BuildException;

    /**
     * Gets the generation directory which is made up og the package directory
     * as a subdirectory of the destination directory.
     * @param javaccFile The used javaccFile.
     * @return The generation directory.
     */
    protected File getGenerationDirectory(File javaccFile) {
        String targetDir = javaccFile.getParent();
        String packageDir = targetDir.substring(_srcbase.getAbsolutePath().length());
        File directory = new File(_destdir.getAbsolutePath() + File.separator + packageDir);
        return directory;
    }
}