package org.hamcrest.generator.config;

import org.hamcrest.generator.HamcrestFactoryWriter;
import org.hamcrest.generator.QDoxFactoryReader;
import org.hamcrest.generator.QuickReferenceWriter;
import org.hamcrest.generator.ReflectiveFactoryReader;
import org.hamcrest.generator.SugarConfiguration;
import org.hamcrest.generator.SugarGenerator;
import org.hamcrest.generator.QDox;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class XmlConfigurator {

    private final SugarConfiguration sugarConfiguration;
    private final ClassLoader classLoader;
    private final SAXParserFactory saxParserFactory;
    private final QDox qdox;

    public XmlConfigurator(SugarConfiguration sugarConfiguration, ClassLoader classLoader) {
        this.sugarConfiguration = sugarConfiguration;
        this.classLoader = classLoader;
        saxParserFactory = SAXParserFactory.newInstance();
        saxParserFactory.setNamespaceAware(true);
        qdox = new QDox();
    }

    public void addSourceDir(File sourceDir) {
        qdox.addSourceTree(sourceDir);
    }

    public void load(InputSource inputSource)
            throws ParserConfigurationException, SAXException, IOException {
        SAXParser saxParser = saxParserFactory.newSAXParser();
        saxParser.parse(inputSource, new DefaultHandler() {
            @Override
            public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                if (localName.equals("factory")) {
                    String className = attributes.getValue("class");
                    try {
                        addClass(className);
                    } catch (ClassNotFoundException e) {
                        throw new SAXException("Cannot find Matcher class : " + className);
                    }
                }
            }
        });
    }

    private void addClass(String className) throws ClassNotFoundException {
        Class<?> cls = classLoader.loadClass(className);
        sugarConfiguration.addFactoryMethods(
                new QDoxFactoryReader(new ReflectiveFactoryReader(cls), qdox, className));
    }


    public static void main(String[] args) throws Exception {

        if (args.length != 4) {
            System.err.println("Args: config-file source-dir generated-class output-dir");
            System.err.println("");
            System.err.println("    config-file : Path to config file listing matchers to generate sugar for.");
            System.err.println("                  e.g. path/to/matchers.xml");
            System.err.println("");
            System.err.println("    source-dir  : Path to Java source containing matchers to generate sugar for.");
            System.err.println("                  May contain multiple paths, separated by commas.");
            System.err.println("                  e.g. src/java,src/more-java");
            System.err.println("");
            System.err.println("generated-class : Full name of class to generate.");
            System.err.println("                  e.g. org.myproject.MyMatchers");
            System.err.println("");
            System.err.println("     output-dir : Where to output generated code (package subdirs will be");
            System.err.println("                  automatically created).");
            System.err.println("                  e.g. build/generated-code");
            System.exit(-1);
        }

        String configFile = args[0];
        String srcDirs = args[1];
        String fullClassName = args[2];
        File outputDir = new File(args[3]);

        String fileName = fullClassName.replace('.', File.separatorChar) + ".java";
        int dotIndex = fullClassName.lastIndexOf(".");
        String packageName = dotIndex == -1 ? "" : fullClassName.substring(0, dotIndex);
        String shortClassName = fullClassName.substring(dotIndex + 1);

        if (!outputDir.isDirectory()) {
            System.err.println("Output directory not found : " + outputDir.getAbsolutePath());
            System.exit(-1);
        }

        File outputFile = new File(outputDir, fileName);
        outputFile.getParentFile().mkdirs();

        SugarGenerator sugarGenerator = new SugarGenerator();
        try {
            sugarGenerator.addWriter(new HamcrestFactoryWriter(
                    packageName, shortClassName, new FileWriter(outputFile)));
            sugarGenerator.addWriter(new QuickReferenceWriter(System.out));

            XmlConfigurator xmlConfigurator
                    = new XmlConfigurator(sugarGenerator, XmlConfigurator.class.getClassLoader());

            if (srcDirs.trim().length() > 0) {
                for (String srcDir : srcDirs.split(",")) {
                    xmlConfigurator.addSourceDir(new File(srcDir));
                }
            }
            xmlConfigurator.load(new InputSource(configFile));

            System.out.println("Generating " + fullClassName);
            sugarGenerator.generate();
        } finally {
            sugarGenerator.close();
        }
    }
}
