/*
 * BEASTGen.java
 *
 * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST 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
 * of the License, or (at your option) any later version.
 *
 *  BEAST 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 BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.app.beastgen;

import dr.app.beauti.options.DateGuesser;
import dr.app.util.Arguments;
import dr.evolution.io.Importer;
import freemarker.template.*;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BEASTGen {

    public BEASTGen(DateGuesser guesser, Map argumentMap, String treeFileName, String templateFileName, String inputFileName, String outputFileName) throws IOException {

        Configuration cfg = new Configuration();
        cfg.setObjectWrapper(new DefaultObjectWrapper());
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
        cfg.setIncompatibleImprovements(new Version(2, 3, 20));  // FreeMarker 2.3.20

        Map root = null;
        try {
            root = constructDataModel(inputFileName, treeFileName, guesser);
        } catch (Importer.ImportException ie) {
            System.err.println("Error importing file: " + ie.getMessage());
            System.exit(1);
        }

        root.putAll(argumentMap);

        Template template = cfg.getTemplate(templateFileName);

        try {
            Writer out = (outputFileName != null ?
                    new FileWriter(new File(outputFileName)) :
                    new OutputStreamWriter(System.out));

            template.process(root, out);
        } catch (TemplateException te) {
            System.err.println("Error processing template, " + templateFileName + ": " + te.getMessage());
            System.exit(1);
        }
    }

    private Map constructDataModel(String inputFileName, String treeFileName, DateGuesser guesser) throws IOException, Importer.ImportException {
        DataModelImporter importer = new DataModelImporter(guesser);

        Map root = importer.importFromFile(new File(inputFileName));

        if (treeFileName != null) {
            importer.importFromTreeFile(treeFileName, root);
        }

        return root;
    }

    public static void centreLine(String line, int pageWidth) {
        int n = pageWidth - line.length();
        int n1 = n / 2;
        for (int i = 0; i < n1; i++) {
            System.out.print(" ");
        }
        System.out.println(line);
    }

    public static void printTitle() {
        System.out.println();
        centreLine("BEASTGen v1.0.1, 2013-2014", 60);
        centreLine("BEAST input file generator", 60);
        centreLine("Andrew Rambaut, University of Edinburgh", 60);
        System.out.println();
    }

    public static void printUsage(Arguments arguments) {

        arguments.printUsage("beastgen", "<template-file-name> <input-file-name> [<output-file-name>]");
        System.out.println();
        System.out.println("  Example: beastgen template.beast test.nex test.xml");
        System.out.println("  Example: beastgen -help");
        System.out.println();
    }

    public static void main(String[] args) {

        // There is a major issue with languages that use the comma as a decimal separator.
        // To ensure compatibility between programs in the package, enforce the US locale.
        Locale.setDefault(Locale.US);

        Arguments arguments = new Arguments(
                new Arguments.Option[]{
                        new Arguments.IntegerOption("date_order", "The order of the date field (negative numbers from last)"),
                        new Arguments.StringOption("date_prefix", "prefix", "A string that is the prefix to the date field"),
                        new Arguments.StringOption("date_regex", "regex", "A string that gives the regular expression to match the date"),
                        new Arguments.StringOption("date_format", "format", "A string that gives the date format for parsing"),
                        new Arguments.Option("date_precision", "Specifies the date is a variable precision yyyy-MM-dd format"),
                        new Arguments.StringOption("tree", "tree-file-name", "Read a tree from a file"),
                        new Arguments.StringOption("D", "\"key=value,key=value...\"", "Properties for exchange in templates"),
                        new Arguments.Option("version", "Print the version and credits and stop"),
                        new Arguments.Option("help", "Print this information and stop"),
                });


        try {
            arguments.parseArguments(args);
        } catch (Arguments.ArgumentException ae) {
            printTitle();
            System.out.println();
            System.out.println(ae.getMessage());
            System.out.println();
            printUsage(arguments);
            System.exit(1);
        }

        DateGuesser guesser = new DateGuesser();
//        guesser.guessDates = false;
//        guesser.guessType = DateGuesser.GuessType.ORDER;
//        guesser.fromLast = false;
//        guesser.order = 0;
//        guesser.prefix = null;
//        guesser.regex = null;
//        guesser.offset = 0.0;
//        guesser.unlessLessThan = 0.0;
//        guesser.offset2 = 0.0;
//        guesser.parseCalendarDates = false;
//        guesser.parseCalendarDatesAndPrecision = false;
//        guesser.calendarDateFormat = "yyyy-MM-dd";

        if (arguments.hasOption("date_order")) {
            guesser.guessDates = true;
            int order = arguments.getIntegerOption("date_order");
            if (order < 0) {
                guesser.order = 1 + order;
                guesser.fromLast = true;
            } else if (order > 0) {
                guesser.order = order - 1;
                guesser.fromLast = false;
            } else {
                guesser.order = 0;
                guesser.fromLast = false;
            }
        }

        if (arguments.hasOption("date_prefix")) {
            guesser.guessDates = true;
            guesser.prefix = arguments.getStringOption("date_prefix");
            guesser.guessType = DateGuesser.GuessType.PREFIX;
        }

        if (arguments.hasOption("date_regex")) {
            guesser.guessDates = true;
            guesser.regex = arguments.getStringOption("date_regex");
            guesser.guessType = DateGuesser.GuessType.REGEX;
        }

        if (arguments.hasOption("date_format")) {
            guesser.guessDates = true;
            guesser.calendarDateFormat = arguments.getStringOption("date_format");
            guesser.parseCalendarDates = true;
        }

        if (arguments.hasOption("date_precision")) {
            guesser.guessDates = true;
            guesser.parseCalendarDatesAndPrecision = true;
        }

        String treeFileName = null;
        if (arguments.hasOption("tree")) {
            treeFileName = arguments.getStringOption("tree");
        }


        Map argumentMap = new HashMap();

        if (arguments.hasOption("D")) {
            String properties = arguments.getStringOption("D");

            for (String property : properties.split("\\s*,\\s*")) {
                String[] keyValue = property.split("=");
                if (keyValue.length != 2) {
                    System.err.println("Properties should take the form: key=value");
                    System.exit(1);
                }
                String key = keyValue[0].trim();
                String value = keyValue[1].trim();
                if (key.isEmpty()) {
                    System.err.println("Properties should take the form: key=value");
                    System.exit(1);
                }
                argumentMap.put(key, value);
            }
        }

        if (arguments.hasOption("help")) {
            printTitle();
            printUsage(arguments);
            System.exit(0);
        }

        if (arguments.hasOption("version")) {
            printTitle();
        }


        String[] args2 = arguments.getLeftoverArguments();

        if (args2.length < 1) {
            printTitle();
            printUsage(arguments);
            System.exit(0);
        }

        if (args2.length < 2 || args2.length > 3) {
            System.err.println("Unknown option: " + args2[0]);
            System.err.println();
            printUsage(arguments);
            System.exit(1);
        }

        try {
            new BEASTGen(guesser, argumentMap, treeFileName, args2[0], args2[1], (args2.length == 3 ? args2[2] : null));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

    }
}
