package turgen;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import turgen.config.ConfigClass;
import turgen.config.Configuration;
import turgen.generator.SignalGenerator;
import turgen.generator.PulseCache;
import turgen.generator.TapeImageGenerator;
import turgen.output.AudioOutput;
import turgen.output.TapeImageOutput;
import turgen.output.WaveOutput;
import turgen.playlist.PlayList;
import turgen.plugin.Plugin;
import turgen.plugin.PluginManager;
import turgen.plugin.TapeImagePlugin;

/*
 TURGEN SYSTEM, program that converts files to various turbo systems
 Copyright (C) 2006-2014 Michael Kalouš

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */
public class Turgen {

    /**
     * Version string
     */
    public static final String version = "TURGEN SYSTEM 8.3.17";

    /**
     * Directory with Turgen.jar file
     */
    public static String dirPrefix;

    /**
     * Directory with configuration
     */
    public static String configDir;

    /**
     * Owner of all dialogs and frames, takes care about storing/restoring
     * bounds etc.
     */
    public static DialogManager dlm;

    /**
     * Manager of plugins
     */
    public static PluginManager pluginManager;

    /**
     * Playlist
     */
    public static PlayList playList;

    /**
     * Turbo generation progress monitor
     */
    public static ProgressUpdater pt;

    /**
     * Timer used to update elements showing progress
     */
    private static javax.swing.Timer timer;

    /**
     * For synchronizing progress sources
     */
    public static ProgressSynchronizer sync;

    /**
     * Configuration class
     */
    public static ConfigClass cfcTurgen;

    /**
     * Pulse cache
     */
    public static PulseCache pulseCache;

    /*Some global strings*/
    public static String ln = System.getProperty("line.separator");
    public static String sp = System.getProperty("file.separator");
    public static String osName = System.getProperty("os.name");
    public static String programInfoString = null;
    public static Turgen self;

    /*Parameters*/
    public static String[] parameters;

    /**
     * @param args Command line parameters
     */
    public static void main(String[] args) {

        parameters = args;

        try {

            /*Instance of myself*/
            self = new Turgen();

            /*Retrieve or create config directory*/
            retrieveConfigDir();

            /*Redirect stdout and stderr*/
            redirect();
            
            /*Initialize playlist*/
            playList = new PlayList();

            /*Initialize configuration class and entries*/
            cfcTurgen = new ConfigClass("Turgen", "TURGEN SYSTEM");
            Configuration.registerClass(cfcTurgen);
            
            /*Initialize main parts of GUI*/
            dlm = DialogManager.getInstance();
            dlm.initDialogs();

            /*Global system of configuration*/
            Configuration.loadOptions();

            /*Load UI persistence information*/
            dlm.loadBounds();

            /*Where is Turgen.jar*/
            retrieveDirPrefix();

            /*Load pulse cache*/
            loadPulseCache();

            /*Synchronization*/
            sync = new ProgressSynchronizer();

            /*Plugin initialization*/
            loadPlugins(true);

            /*Initialize outputs and generators*/
            WaveOutput.init();
            AudioOutput.init();
            SignalGenerator.init();
            TapeImageOutput.init();
            TapeImageGenerator.init();

            /*Attempt to load playlist*/
            if (args.length > 0) {
                Turgen.loadPlaylist(Turgen.parameters[0]);
            }

            /*Show main window*/
            dlm.frmTurgen.setVisible(true);
            dlm.frmTurgen.toFront();

            /*Start casovace monitorujiciho stav generovani*/
            pt = new ProgressUpdater();
            timer = new javax.swing.Timer(1000, pt);
            timer.start();

        } catch (Exception xxe) {
            xxe.printStackTrace();
            forcedExit();
        }

    }

    /**
     * Provede vsechny potrebne ukoncovaci operace a ukonci program
     */
    static void exit() {

        if (timer != null) {
            timer.stop();
        }

        if (dlm != null) {
            dlm.saveBounds();
        }

        System.exit(0);
    }

    static void forcedExit() {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    Turgen.exit();
                }
            });
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        } catch (InvocationTargetException e2) {
            e2.printStackTrace();
        }
    }

    /**
     * Stdout and stderr redirection
     */
    static void redirect() {
        try {
            /*Check size*/
            File f = new File(configDir + "turgen.log");

            /*If the file is larger than 4 MB then wrap around*/
            if (f.exists() && f.isFile() && f.length() > 4 * 1024 * 1024) {
                f.delete();
            }

            FileOutputStream fw = new FileOutputStream(configDir + "turgen.log", true);
            PrintStream os = new PrintStream(fw);
            System.setOut(os);
            System.setErr(os);
            System.out.println(new Date(System.currentTimeMillis()).toString());
        } catch (Exception e) {
            /*No remedy*/
            e.printStackTrace();
        }
    }

    /**
     * Plugins loading and initialization
     */
    static void loadPlugins(boolean usesgui) {

        Vector p_classes = new Vector();
        try {
            FileReader fr = new FileReader(dirPrefix + "plugins.list");
            BufferedReader br = new BufferedReader(fr);

            String line = null;

            while ((line = br.readLine()) != null) {
                line = line.trim();

                /*Comments are ignored*/
                if (line.startsWith("#") || line.length() == 0) {
                    continue;
                }

                /*Classes are collected in the vector*/
                p_classes.add(line);
                
            }

            br.close();

        } catch (Exception e) {
            ParallelGUIUpdater.showMessage(Utils.getTitledExceptionMessage("Unable to load list of plugins", e));
        }


        try {

            int l = p_classes.size();
            Vector plugins = new Vector();
            String s2;

            /*Create Plugin implementators*/
            for (int i = 0; i < l; i++) {
                s2 = (String) p_classes.elementAt(i);
                Plugin pgn = (Plugin) (Class.forName(s2).newInstance());
                plugins.add(pgn);
            }

            /*Create plugin manager*/
            pluginManager = new PluginManager(plugins);

            /* Initialize all plugins*/
            Iterator it = plugins.iterator();
            while (it.hasNext()) {

                Plugin pgn = (Plugin) it.next();

                ParallelGUIUpdater.initPlugin(pgn);
                ParallelGUIUpdater.setupPluginGUI(pgn);
                pgn.setInOutProvider(dlm.dlgRecord);
            }

            /*Special initialization of TapeImagePlugin*/
            TapeImagePlugin tip = (TapeImagePlugin) pluginManager.getPlugin("turgen.plugin.TapeImagePlugin");
            if (tip!=null) {
                tip.mapPlugins(plugins);
            }

        } catch (Exception e) {
            ParallelGUIUpdater.showMessage(Utils.getTitledExceptionMessage("Plugins cannot be properly initialized. Check plugins.properties file.", e));
            e.printStackTrace();
            forcedExit();
        }

    }

    /**
     * Save playlist
     */
    static void savePlaylist(String fspec) {

        FileOutputStream fos;
        ObjectOutputStream oos;

        try {
            fos = new FileOutputStream(fspec);
            oos = new ObjectOutputStream(fos);

            oos.writeObject(Turgen.playList.data);

        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, Utils.getTitledExceptionMessage("Unable to save playlist", e), "ERROR", JOptionPane.ERROR_MESSAGE);
        }

    }

    /**
     * Load playlist
     */
    static void loadPlaylist(String fname) {

        ArrayList tal;
        String fn = fname;

        FileInputStream fos;
        ObjectInputStream oos;

        try {
            fos = new FileInputStream(fn);
            oos = new ObjectInputStream(fos);

            tal = (ArrayList) oos.readObject();

        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, Utils.getTitledExceptionMessage("Unable to load playlist", e), "ERROR", JOptionPane.ERROR_MESSAGE);
            return;

        }
        /*Adding playlist*/
        if (tal != null) {
            Turgen.playList.data = tal;
            /*Table is surely changed*/
            Turgen.playList.fireTableDataChanged();
        }

    }

    /**
     * Where is Turgen.jar ?
     */
    static void retrieveDirPrefix() throws Exception {
        File jarFile;
        /*From JAR file*/
        try {
            URL u = self.getClass().getProtectionDomain().getCodeSource().getLocation();
            URI i = new URI(u.toString());
            String s = i.getPath();

            jarFile = new File(s);
            dirPrefix = jarFile.getParentFile().getAbsolutePath().concat(System.getProperty("file.separator"));

            File f = new File(dirPrefix + "turgen.id");
            if (f.exists() && f.isFile()) {
                return;
            }
        } catch (URISyntaxException e) {
            /*Intentionally blank*/
        }

        /*From user's working directory*/
        dirPrefix = System.getProperty("user.dir") + System.getProperty("file.separator");
        final File f = new File(dirPrefix + "turgen.id");

        /*Ask user*/
        if (!(f.exists())) {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    JOptionPane.showMessageDialog(null, "Unable to locate turgen.id in: " + f.getAbsolutePath());
                }
            });
            System.exit(-1);
        }

    }

    /**
     * Retrieve or create configuration directory
     */
    static void retrieveConfigDir() {

        configDir = System.getProperty("user.home");
        configDir += sp + ".turgen82";
        /*Create directory if necessary*/
        File f = new File(configDir);
        if (f.exists() == false) {
            f.mkdir();
        }
        /*configDir string will contain trailing directory separator*/
        configDir += sp;

    }

    private static void loadPulseCache() {

        pulseCache = new PulseCache();
        try {
            pulseCache.load(Turgen.dirPrefix + "pulses" + sp + "pulses.list");
        } catch (Exception e) {
            e.printStackTrace();
            pulseCache.dump();
            String msg = Utils.getTitledExceptionMessage("Unable to load information about pulses", e);
            JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE);
        }
    }

    /**
     * Returns basic information about Turgen System
     * @return Information about Turgen System
     */
    public static String getProgramInfoString() {

        if (programInfoString != null) {
            return programInfoString;
        }

        StringBuilder retVal = new StringBuilder();
        retVal.append("<HTML>" + version + "<BR><BR>");
        retVal.append("Program that converts files to various turbo systems.<BR>");
        retVal.append("Copyright (C) 2006-2014 Michael Kalouš<BR><BR>");
        retVal.append("This program is free software distrubuted under the terms of the<BR>");
        retVal.append("GNU General Public License version 2<BR><BR>");
        retVal.append("Operating system: ");
        retVal.append(osName).append("<BR>");
        retVal.append("Program directory: ");
        retVal.append(dirPrefix.substring(0, dirPrefix.length() - 1)).append("<BR>");
        retVal.append("Directory with configuration: ");
        retVal.append(configDir.substring(0, configDir.length() - 1)).append("<BR>");
        retVal.append("File separator: ");
        retVal.append(sp);
        retVal.append("<BR>");
        retVal.append("Java version: ");
        retVal.append(System.getProperty("java.version"));
        retVal.append("</HTML>");

        programInfoString = retVal.toString();
        return programInfoString;

    }

}
