/*
 */

package au.gov.ansto.bragg.nexus.tool;
import au.gov.ansto.bragg.forward.All_;
import au.gov.ansto.bragg.forward.JavaFactory;
import au.gov.ansto.bragg.nexus.base.NXtypes;
import au.gov.ansto.bragg.forward.metamodel.classes.kernel.Package;
import au.gov.ansto.bragg.forward.metamodel.classes.kernel.Class;
import java.net.MalformedURLException;

import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.FileWriter;
import java.io.File;
import java.net.URL;
import java.util.Iterator;
import java.util.StringTokenizer;

/** Implementation of Darren Kelly's NeXus XML template to NeXusBean converter.
 *
 * <p>
 * Includes {@link #main()} with command line arg for generating NeXusBeans
 * directly from NeXus SVN repository of templates to a given project output directory.
 * </p>
 *
 * </p>
 * <p>
 * This implementor merely offers services for managing the project,  the XML template files,
 * and the conversion, and some flags for configuration and testing.
 * The NeXusBean construction is delegated to a {@link NeXusFactory},
 * which in turn uses a UML-metamodel-driven Bragg Insitute 'forward'-engineering project
 * for encapsulating and writing Java classes.
 * </p>
 *
 * @author drck
 */
public class NeXusXmlTemplateToNeXusBean_
        extends All_
        implements NeXusXmlTemplateToNeXusBean {
        
    /** Default project root for testing by Darren Kelly (dke): 
     * other users should always override this with command line args 
     */
    static final public String BASE = "/home/dke/Documents/project/nexus/";

    static final public String LOCAL_XML = "xml/meta_dtd/";
    
    static public enum Mode {
        
        /* Process all XML MetaDTD streamed from NeXus SVN (usual user mode) */
        process_all_remote_xml,

        /** Process a named XML MetaDTD stream from the NeXus SVN (test only) */
        process_one_remote_xml,
        
        /** Process recursively all XML MetaDTD stored locally (test only) */        
        process_all_local_xml,
        
        /** Process one XML MetaDTD stored locally (test only) */
        process_one_local_xml
    }
        
    static public Mode mode = Mode.process_all_remote_xml;
    
    /** The current test file */
    //static public String TEST_ONE_XML = "NXattenuator.xml";
    static public String TEST_ONE_XML = "NXmonitor.xml";
    
    /** Generates NeXusBeans from remote NeXus SVN repository of XML MetaDTD templates.
     *
     * <p>The base directory for project output must already exist,
     * and so must all directories under it with the following deliberately fixed
     * structure:
     * </p>
     *
     * <blockquote>
     * $base/src/au/gov/ansto/bragg/nexus/auto
     * </blockquote>
     *
     * <p>
     * Current policy is to deliberately NOT create the directory structure for you,
     * you must create it by hand first !
     * </p>
     * 
     *<p>To execute from the command line use:</p>
     * <blockquote>
     * java NeXusXmlTemplateToNeXusBean_ <base>
     * </blockquote>
     *
     * <p>You must have the following in your classpath (or versioned equivalents):</p>
     * <ul>
     * <li>au.gov.ansto.bragg.forward.jar (supplied with this release) </li>
     * <li>au.gov.ansto.bragg.nexus.jar (this release)</li>
     * <li>jnexus.jar (use the version supplied with this release please)</li>
     * <li>dom4j.jar (1.6.1 or greater)</li>
     * </ul>
     *
     * @param args First argument is taken as the project output base directory (does NOT include 'src' directory).
     */
    public static void main(String[] args) {
        
        try {
            NeXusXmlTemplateToNeXusBean_.DO_WRITE_JAVA = true; //TODO command line flag
            
            String $base;
            if (args.length > 0) {
                $base = args[0];
                System.out.println("Taking project root:"+$base);
                //System.exit(0);
            } 
            else $base = BASE;
            
            Project project = null;
            switch (mode) {
                
                case process_all_remote_xml:
                    project = new SvnProject_($base);
                break;

                case process_one_remote_xml:
                    project = new Project_($base);
                    project.addXmlTemplate(SvnProject_.$URL_SVN_BASE+TEST_ONE_XML);
                break;
                
                case process_one_local_xml:
                    project = new Project_($base);
                    project.addXmlTemplate($base+LOCAL_XML+TEST_ONE_XML);
                break;

                case process_all_local_xml:
                    project = new Project_($base);
                    project.addXmlTemplateDirectory($base+LOCAL_XML);
                break;
            }
            NeXusXmlTemplateToNeXusBean converter = new NeXusXmlTemplateToNeXusBean_(project);
            converter.convert();
        } 
        catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
    
    /** Constructs a converter for the given project.
     * @param project A non-null project of NeXus XML files.
     */
    public NeXusXmlTemplateToNeXusBean_(
            Project project
            ) {
        if (project==null) error("null Project");
        else project_ = project;
    }
    
    private Package owningPackage_;
    /** The package that will own the NeXusBeans */
    protected Package owningPackage() {
        if (owningPackage_==null)
            owningPackage_ = neXusFactory().newNeXusPackage(getProject());
        return owningPackage_;
    }
    
    private Project project_;
    /** @inheritDoc */
    public Project getProject() {
        return project_;
    }
    
    private NeXusFactory neXusFactory_;
    /** The {@link NeXusFactory} that performs the conversion from XML to Java */
    protected NeXusFactory neXusFactory() {
        if (neXusFactory_==null) {
            //neXusFactory_ = new NeXusFactory_(getProject());
            neXusFactory_ = new NeXusFactory_();
        }
        return neXusFactory_;
    }
    
    /** The {@link NeXusFactory} also acts as a general {@link JavaFactory} */
    protected JavaFactory javaFactory() {return neXusFactory();}
    
    /** Parses an XML file - retrieved from the given path - into a dom4j Document.
     * @param path_xml The path of the XML file to parse.
     */
    public Document parse(final String path_xml) {
        try {
            URL url = new URL("file:"+path_xml);
            return parse(url);
        } catch (Exception e) {
            error(e);
            return null;
        }
    }
    
    /** Parses an XML file given by URL into a dom4j Document.
     * @param url_xml The URL of the XML to parse.
     */
    public Document parse(final URL url_xml) {
        try {
            SAXReader reader = new SAXReader();
            reader.setValidation(false);
            Document document = reader.read(url_xml);
            return document;
        } catch (Exception e) {
            error(e);
            return null;
        }
    }
    
    
    /** Test toggle; whether Java should actually be written to file (otherwise just echo to System out) */
    static public boolean DO_WRITE_JAVA = false;
    
    /** Writes Java as a string to the given full path.
     *
     * @param java A string that represents compilable Java
     * @param path The full path of the file to write (including .java suffix)
     */
    static public void writeJava(
            String java,
            String path) {
        if (!DO_WRITE_JAVA) {
            INFO(java);
            return;
        }
        try {
            INFO("Writing Java to:"+path);
            FileWriter f = new FileWriter(path);
            f.write(java);
            f.close();
        } catch (Exception e) {
            ERROR(e);
        }
    }
    
    /** @inheritDoc */
    public void convert() {
        for (XmlTemplate source : getProject().getXmlTemplates()) {
            Document document = parse(source.getURL());
            
            //Class clazz = buildNeXusBean(s,owningPackage);
            Class clazz = neXusFactory().newNeXusBean(
                    source.getURL()+"",
                    document,
                    owningPackage()
                    );
            clazz.setFilename(clazz.getName()+".java");
            String $out = owningPackage().$directory()+clazz.getFilename();
            debug("$out",$out);
            writeJava(clazz+"",$out);//TODO
        }
    }
    
}
