/*
 *  Copyright 2006-2009 Mikhail Kryshen
 *
 *  This file is part of Tema.
 *
 *  Tema 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 3 of the
 *  License, or (at your option) any later version.
 *
 *  Tema 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 Tema.  
 *  If not, see <http://www.gnu.org/licenses/>.
 */

package kryshen.tema;

import java.awt.EventQueue;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.sql.SQLException;
import kryshen.tema.demo.DemoFrame;
import kryshen.tema.io.TemplateReader;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

/**
 * Tema main class.
 *
 * @author Mikhail Kryshen
 */
public class Tema {
    public static final String TITLE = "Tema";
    public static final String VERSION = "0.3+";
    public static final String AUTHOR = "Mikhail Kryshen";    
    public static final String COPYRIGHT = 
            "Copyright 2009 Mikhail Kryshen <mikhail@kryshen.net>";
    public static final String LICENSE = 
            "This is free software. " + 
            "You may redistribute copies of it under the terms of" + 
            System.getProperty("line.separator") + 
            "the GNU Lesser General Public License version 3 or later" + 
            System.getProperty("line.separator") + 
            "<http://gnu.org/licenses/lgpl.html>.";
        
    private static final String DEFAULT_CHARSET =
            Charset.defaultCharset().name();
    
    private static String inputEncoding = DEFAULT_CHARSET;
    private static String outputEncoding = DEFAULT_CHARSET;
    
    public static void main(String[] args)
    throws IOException, SQLException,
            ClassNotFoundException, InstantiationException,
            IllegalAccessException, InvocationTargetException {
              
        Options options = new Options();
        
        options.addOption(null, "demo", false, "demo mode");
        options.addOption("v", "version", false,
                "print the version information and exit");
        options.addOption("h", "help", false,
                "print this help message");
        options.addOption("o", "output", true,
                "output file");
        options.addOption(null, "log", true,
                "log all error messages to file");
        options.addOption(null, "input-encoding", true,
                "input encoding");
        options.addOption(null, "output-encoding", true,
                "output encoding");
        
        CommandLineParser parser = new GnuParser();
        CommandLine line;
        
        try {
            line = parser.parse(options, args );
        } catch (ParseException ex) {
            System.err.println(ex.getMessage());
            System.exit(1);
            return; // to avoid "not initialized" compile-time errors.
        }
        
        if (line.hasOption("version")) {
            System.err.println(TITLE + " " + VERSION);
            System.err.println(COPYRIGHT);
            System.err.println(LICENSE);
            return;
        }
        
        if (line.hasOption("help")) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp("tema [options] [files]", options);
            return;
        }       
        
        if (line.hasOption("input-encoding")) {
            inputEncoding = line.getOptionValue("input-encoding");
        }
        
        if (line.hasOption("output-encoding")) {
            outputEncoding = line.getOptionValue("output-encoding");
        }
        
        if (line.hasOption("log")) {
            System.setErr(new PrintStream
                    (new FileOutputStream(line.getOptionValue("log"))));
        }
        
        if (line.hasOption("demo")) {
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    // Open the demo console.
                    DemoFrame df = new DemoFrame();
                    df.pack();
                    df.setVisible(true);
                }
            });
            return;
        }
        
        Writer out;
        
        if (line.hasOption("output")) {
            File outfile = new File(line.getOptionValue("output"));
            out = createFileWriter(outfile);
        } else {
            out = new OutputStreamWriter(System.out, outputEncoding) {
                @Override
                public void write(int c) throws IOException {
                    super.write(c);
                    if (c == '\r' || c == '\n')
                        flush();
                }
                
                @Override
                public void write(char cbuf[], int off, int len)
                throws IOException {
                    super.write(cbuf, off, len);
                    flush();
                }
                
                @Override
                public void write(String str, int off, int len)
                throws IOException {
                    super.write(str, off, len);
                    flush();
                }
            };
        }
        
        String[] files = line.getArgs();
        
        if (files.length == 0) {
            TemplateReader in = createTemplateReader(System.in);
            if (!process(in, out)) {
                System.exit(1);
            }
        }
        
        for (String file: files) {
            TemplateReader in = createTemplateReader(new File(file));
            if (!process(in, out)) {
                System.exit(1);
            }
        }
        
        out.close();
    }
    
    private static boolean process(TemplateReader in, Writer out)
    throws IOException {
        
        TemplateParser p = new TemplateParser();
        TemplateException ex = null;
                        
        p.getContext().set("input_encoding",  inputEncoding);
        p.getContext().set("output_encoding", outputEncoding);
        
        try {
            p.parse(in, out);
        } catch (TemplateException e) {
            ex = e;
        } finally {
            in.close();
        }
        
        if (ex != null) {
            System.err.println(ex.getMessage());
            if (ex.getCause() != null) {
                System.err.println("Caused by " + ex.getCause());
            }
            return false;
        }
        
        return true;
    }
    
    public static BufferedWriter createFileWriter(File file)
    throws IOException {
        
        OutputStream os = new FileOutputStream(file);
        return new BufferedWriter
                (new OutputStreamWriter(os, Tema.outputEncoding));
    }
    
    public static BufferedReader createFileReader(File file)
    throws IOException {
        
        InputStream is = new FileInputStream(file);
        return new BufferedReader
                (new InputStreamReader(is, Tema.inputEncoding));
    }
    
    public static TemplateReader createTemplateReader(File file)
    throws IOException {
        
        return new TemplateReader(createFileReader(file), file.getPath());
    }
    
    public static TemplateReader createTemplateReader(InputStream in)
    throws IOException {
        
        return new TemplateReader(
                new InputStreamReader(in, Tema.inputEncoding));
    }
}