package com.moex.asts.export;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main implements Runnable {

    final static String DEFAULT_CONFIG_FILE = "config.xml";

    static Options define() {
        Options options = new Options();
        options.addOption("?", "help", false, "print usage help");
        options.addOption(Option.builder("c").longOpt("config").argName("file").hasArg().type(String.class)
                .desc("configuration file (" + DEFAULT_CONFIG_FILE + " by default)").build());
        options.addOption(Option.builder("o").longOpt("output").argName("file").hasArg().type(String.class)
                .desc("destination file, required").required().build());
        options.addOption(Option.builder("t").longOpt("xslt").argName("file").hasArg().type(String.class)
                .desc("transformation template (for xml output only)").build());
        return options;
    }

    static void explain(Options options) {
        HelpFormatter printer = new HelpFormatter();
        printer.printHelp("java " + Main.class.getName(), options, true);
    }

    public static void main(String[] args) {
        final Options options = define();
        CommandLineParser parser = new DefaultParser();
        try {
            final CommandLine line = parser.parse(options, args);
            if (line.hasOption("?")) {
                explain(options);
                return;
            }
            String filename = line.getOptionValue("o");
            Settings settings = Settings.parse(line.getOptionValue("c", DEFAULT_CONFIG_FILE));
            Natives.prepare(settings.parameters);
            new Main(settings, filename, line.getOptionValue("t")).launch();
        } catch (Throwable x) {
            System.err.println(x.getMessage());
            System.err.println();
            explain(options);
        }
    }

    final Logger logger = LoggerFactory.getLogger(getClass());

    final Export export;

    final String template;

    Main(Settings settings, String filename, String template) throws Exception {
        Runtime.getRuntime().addShutdownHook(new Thread(this, "stop"));
        this.export = new Export(settings, filename);
        this.template = template;
    }

    @Override
    public void run() {
        finish();
    }

    private void transform() throws TransformerConfigurationException, IOException, TransformerException {
        if (export.isXML() && template != null) {
            logger.info("XSLT transformation from {}...", template);
            TransformerFactory factory = TransformerFactory.newInstance();
            Source xsl = new StreamSource(new File(template));
            Transformer transformer = factory.newTransformer(xsl);
            Source xml = new StreamSource(export.output);
            File tmp = File.createTempFile("temp", ".xml");
            try {
                StreamResult out = new StreamResult(tmp);
                transformer.transform(xml, out);
                Files.copy(tmp.toPath(), export.output.toPath(), StandardCopyOption.REPLACE_EXISTING);
            } finally {
                tmp.delete();
            }
            logger.info("XSLT transformation complete");
        }
    }

    final void launch() {
        try {
            export.run();
            transform();
        } catch (Throwable x) {
            x.printStackTrace(System.err);
            System.exit(1);
        }
    }

    final void finish() {
        export.cancel();
    }

}
