/*
 * Copyright 2009, 2010 Lari Natri <lari.natri@gmail.com>
 *
 *  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 3 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, see <http://www.gnu.org/licenses/>.
 */


package compid.application;

import net.aavalla.utils.jobpool.JobResultEvent;
import compid.gui.*;
import compid.data.DAOFactory;
import net.aavalla.utils.jobpool.JobPool;
import net.aavalla.utils.jobpool.parameters.FieldInstance;
import net.aavalla.utils.jobpool.JobFinishedListener;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;
import org.jdesktop.application.Application;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;


/**
 * The main class of Compid application
 * @author Lari Natri
 */
public class CompidApp extends SingleFrameApplication {


    private final JobPool jobPool = new JobPool();

    private File compidConfigFile = null;
    private Properties compidProperties = new Properties();
    private final DatasetsHandler datasets = new DatasetsHandler();
    private ResourceMap appResource = null;

    private boolean mascotParserLoaded = false;
    private boolean dbInitialized = false;

    /**
     * {@inheritDoc}
     */
    @Override protected void initialize(String[] args) {

        // Adds specialized Compid Filed type handler to JobPool
        FieldInstance.addFieldTypeHandler(new CompidFieldTypeHandler());
        jobPool.setMaxThreadsForAllExclusivityTypes(1);
        jobPool.setRunImmediateImmediately(true);

        ResourceMap resMap = this.getHardResource();
        String defaultCompidSubdir = resMap.getString("Application.DefaultCompidSubdir"); //NOI18N
        String defaultConfigFilename = resMap.getString("Application.DefaultConfigFilename"); //NOI18N
        String defaultDbSubdir = resMap.getString("Application.DefaultDatabaseSubdir"); //NOI18N
        String msParserDownloadUrl = resMap.getString("Application.MascotParserDownloadURL"); //NOI18N
        String homeDir = System.getProperty("user.home","."); //NOI18N
        String sep = File.separator;

        Properties defaultProperties = new Properties();

        try { // get default configuration options from internal resource file
            String fileName = resMap.getResourcesDir()+"DefaultConfig.properties"; //NOI18N
            InputStream is = resMap.getClassLoader().getResourceAsStream(fileName);
            BufferedInputStream bis = new BufferedInputStream(is);
            defaultProperties.load(is);
            bis.close();
            is.close();
        } catch (Exception ex) {
            System.err.println("WARNING: Cannot load default configuration " +
                    "options from an internal resource");
        }

        defaultProperties.setProperty("db.url","jdbc:derby:" + homeDir +
                sep + defaultCompidSubdir + sep + defaultDbSubdir); //NOI18N

        this.compidProperties = new Properties(defaultProperties);

        System.out.println("DEBUG: Parsing command line arguments");

        // parse arguments
        for (int i = 0; i < args.length; ++i) {
            String arg = args[i];
            System.out.println(arg);
            if (arg.equals("--conf") || arg.equals("-conf")) { // NOI18N
                String configFileName = "";
                for (int j=i+1; j<args.length; ++j) {
                    configFileName+=args[j];
                }
                if (configFileName.startsWith("\"") && configFileName.endsWith("\"")) {
                    configFileName = configFileName.substring(0, configFileName.length()+1);
                }
                compidConfigFile = new File(configFileName);
                i = args.length;
            } else {
                System.err.println("Invalid command line arguments.");
                this.exit();
            }
        }

        if (compidConfigFile==null) { // use default file location
            compidConfigFile = new File(homeDir+sep+defaultCompidSubdir+sep+defaultConfigFilename);
            try {
                new File(compidConfigFile.getParent()).mkdirs();
                if (compidConfigFile.createNewFile()) { // create the file if needed
                    System.out.println("DEBUG: Creating new configuration file: "+compidConfigFile);
                    BufferedWriter bufWriter = new BufferedWriter(new FileWriter(compidConfigFile));
                    defaultProperties.store(bufWriter, "Compid configuration file");
                    bufWriter.close();
                }
            } catch (IOException ex) {
                System.err.println("Cannot create configuration file to location "+compidConfigFile);
                this.exit();
            }
        }


        // load settings from file
        System.out.println("DEBUG: Loading configuration from: "+compidConfigFile);
        try {
            BufferedReader bufReader = new BufferedReader(new FileReader(compidConfigFile));
            compidProperties.load(bufReader);
            bufReader.close();
        } catch (FileNotFoundException ex) {
            System.err.println("Specified configuration file doesn't exist: "+compidConfigFile);
            this.exit();
        } catch (IOException ex) {
            System.err.println("Cannot load configuration file "+compidConfigFile);
            this.exit();
        }
      
    }

    /**
     * At startup create and show the main frame of the application.
     */
    @Override protected void startup() {
        // don't use bold fonts in Metal UI Style
        UIManager.put("swing.boldMetal", Boolean.FALSE); //NOI18N

        // do not 'gray out' disabled check boxes and combo boxes
        UIManager.put("CheckBox.disabledText", UIManager.get("CheckBox.foreground")); //NOI18N
        UIManager.put("ComboBox.disabledForeground", UIManager.get("ComboBox.foreground")); //NOI18N


        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) { //NOI18N
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (UnsupportedLookAndFeelException e) {
            // handle exception
        } catch (ClassNotFoundException e) {
            // handle exception
        } catch (InstantiationException e) {
            // handle exception
        } catch (IllegalAccessException e) {
            // handle exception
        }

//        // if available, set GTK LAF instead of Metal on systems defaulting to Metal
//        String systemLAF = UIManager.getSystemLookAndFeelClassName();
//        if (systemLAF.equals("javax.swing.plaf.metal.MetalLookAndFeel")) {
//            LookAndFeelInfo[] lafInfos = UIManager.getInstalledLookAndFeels();
//            for (LookAndFeelInfo lafinfo : lafInfos) {
//                if (lafinfo.getClassName().equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel")) {
//                    try {
//                        UIManager.setLookAndFeel(lafinfo.getClassName());
//                    } catch (Exception ex) {}
//                    break;
//                }
//            }
//        }

        // create the UI and show it
        show(new CompidView(this));
    }

    @Override protected void ready() {
        final MascotParserLoader mascotLoader = new MascotParserLoader(this);
        mascotLoader.addJobFinishedListener(new JobFinishedListener() {
            @Override public void jobFinished(JobResultEvent result) {
                synchronized(CompidApp.this) {
                    if (result.failed()) {
                        CompidApp.this.mascotParserLoaded = false;
                    } else {
                        CompidApp.this.mascotParserLoaded = (Boolean)result.getResult();
                    }
                }
            }

        });
        this.getJobPool().addJob(mascotLoader);

        final DatabaseInitializer dbInitializer = new DatabaseInitializer(this);
        dbInitializer.addJobFinishedListener(new JobFinishedListener() {
            @Override public void jobFinished(JobResultEvent result) {
                synchronized(CompidApp.this) {
                    if (result.failed()) {
                        CompidApp.this.dbInitialized = false;
                    } else {
                        CompidApp.this.dbInitialized = (Boolean)result.getResult();
                    }
                }
            }

        });
        this.getJobPool().addJob(dbInitializer);

    }

    @Override protected void shutdown() {
        System.out.print("DEBUG: Shutdown started...");
        super.shutdown();
        System.out.print(" disconnecting DB...");
        DAOFactory.disconnectDB();
        System.out.println(" DONE.");
    }

    /**
     * This method is to initialize the specified window by injecting resources.
     * Windows shown in our application come fully initialized from the GUI
     * builder, so this additional configuration is not needed.
     */
    @Override protected void configureWindow(java.awt.Window root) {
    }

    /**
     * A convenient static getter for the application instance.
     * @return the instance of CompidApp
     */
    public static CompidApp getApplication() {
        return Application.getInstance(CompidApp.class);
    }

    /**
     * Main method launching the application.
     */
    public static void main(String[] args) {
//        // begin test
//
//        // ambiguous = B, Z, X
//        int maxAlt = 1;
//        BoundedAligner alignerNew = new BoundedAligner(maxAlt);
//        NeedlemanWunschAligner alignerOld = new NeedlemanWunschAligner(AminoAcid.getSimilarityMatrix(AminoSMType.SIMPLE));
//        AminoSequence seqA = new AminoSequence("ABZZZZ");
//        AminoSequence seqB = new AminoSequence("AZ");
//        try {
//            Alignment alOld = alignerOld.align(seqA, seqB);
//            Alignment alNew = alignerNew.align(seqA, seqB);
//            System.out.println("OLD: score="+alOld.getScore()+", substs="+alOld.getSubstitutionCount());
//            System.out.println(" OLD: "+alOld.asAlignedString(0));
//            System.out.println(" OLD: "+alOld.asAlignedString(1));
//            System.out.println("NEW: score="+alNew.getScore()+", substs="+alNew.getSubstitutionCount());
//            System.out.println(" NEW: "+alNew.asAlignedString(0));
//            System.out.println(" NEW: "+alNew.asAlignedString(1));
//
//        } catch (Exception ex) {
//            System.out.println(ex);
//            ex.printStackTrace();
//        } finally {
//            System.exit(0);
//        }
//        // end test

        launch(CompidApp.class, args);
    }

    public JobPool getJobPool() {
        return this.jobPool;
    }

    public DatasetsHandler getDatasetsHandler() {
        return this.datasets;
    }

    public synchronized boolean isMascotParserLoaded() {
        return this.mascotParserLoaded;
    }

    public synchronized boolean isDatabaseInitialized() {
        return this.dbInitialized;
    }


    public synchronized Properties getSoftProperties() {
        return (Properties)this.compidProperties.clone();
    }

    public synchronized ResourceMap getHardResource() {
        if (this.appResource == null ) {
            this.appResource = getInstance(compid.application.CompidApp.class).getContext().getResourceMap(CompidApp.class);
        }
        return this.appResource;
    }

}
