/*
 * MapperFrame.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
 */

/*
 * BeautiFrame.java
 *
 * (c) 2002-2005 BEAST Development Core Team
 *
 * This package may be distributed under the
 * Lesser Gnu Public Licence (LGPL)
 */
package dr.app.mapper.application;

import dr.app.mapper.application.menus.MapperFileMenuHandler;
import dr.evolution.tree.FlexibleTree;
import dr.evolution.tree.Tree;
import dr.inference.trace.LogFileTraces;
import dr.inference.trace.TraceException;
import dr.util.DataTable;
import dr.xml.XMLParseException;
import jam.framework.DocumentFrame;
import jam.framework.Exportable;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.BorderUIResource;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Andrew Rambaut
 * @version $Id: BeautiFrame.java,v 1.22 2006/09/09 16:07:06 rambaut Exp $
 */
public class MapperFrame extends DocumentFrame implements MapperFileMenuHandler {

    private final JTabbedPane tabbedPane = new JTabbedPane();
    private final JLabel statusLabel = new JLabel("No data loaded");

    private StrainsPanel strainsPanel;
    private MeasurementsPanel measurementsPanel;
    private LocationsPanel locationsPanel;
//    private AnalysisPanel analysisPanel;

    MapperDocument document = new MapperDocument();

    List<Tree> trees = new ArrayList<Tree>();

    public MapperFrame(String title) {
        super();

        setTitle(title);

        getExportDataAction().setEnabled(false);
        getExportPDFAction().setEnabled(false);

        // Prevent the application to close in requestClose()
        // after a user cancel or a failure in beast file generation
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

        getOpenAction().setEnabled(true);
        getSaveAction().setEnabled(false);
        getSaveAsAction().setEnabled(false);

        getFindAction().setEnabled(false);

        getCutAction().setEnabled(false);
        getPasteAction().setEnabled(false);
        getDeleteAction().setEnabled(false);
        getSelectAllAction().setEnabled(false);

        getCopyAction().setEnabled(false);

        getZoomWindowAction().setEnabled(false);
    }

    public void initializeComponents() {

        strainsPanel = new StrainsPanel(this, document);
        measurementsPanel = new MeasurementsPanel(this, document);
        locationsPanel = new LocationsPanel(this, document);
//        analysisPanel = new AnalysisPanel(this, trees.get(0));

        tabbedPane.addTab("Measurements", measurementsPanel);
        tabbedPane.addTab("Strains", strainsPanel);
        tabbedPane.addTab("Locations", locationsPanel);
//        tabbedPane.addTab("Analysis", analysisPanel);

        JPanel panel = new JPanel(new BorderLayout(6, 6));
        panel.setBorder(new BorderUIResource.EmptyBorderUIResource(new Insets(12, 12, 12, 12)));
        panel.add(tabbedPane, BorderLayout.CENTER);

        panel.add(statusLabel, BorderLayout.SOUTH);

        getContentPane().setLayout(new BorderLayout(0, 0));
        getContentPane().add(panel, BorderLayout.CENTER);

        setSize(new Dimension(1024, 768));

        setStatusMessage();
    }

    public void timeScaleChanged() {
//        analysisPanel.timeScaleChanged();
        setStatusMessage();
    }

    public void measurementsChanged() {
        //To change body of created methods use File | Settings | File Templates.
    }


    protected boolean readFromFile(File file) throws IOException {
        Reader reader = new FileReader(file);

        BufferedReader bufferedReader = new BufferedReader(reader);
        String line = bufferedReader.readLine();
        while (line != null && line.length() == 0) {
            line = bufferedReader.readLine();
        }

        boolean isNexus = (line != null && line.toUpperCase().contains("#NEXUS"));

        reader = new FileReader(file);

        Tree tree = null;
        try {
//            if (isNexus) {
//                NexusImporter importer = new NexusImporter(reader);
//                tree = importer.importTree(taxa);
//            } else {
//                NewickImporter importer = new NewickImporter(reader);
//                tree = importer.importTree(taxa);
//            }

//        } catch (Importer.ImportException ime) {
//            JOptionPane.showMessageDialog(this, "Error parsing imported file: " + ime,
//                    "Error reading file",
//                    JOptionPane.ERROR_MESSAGE);
//            ime.printStackTrace();
//            return false;
//        } catch (IOException ioex) {
//            JOptionPane.showMessageDialog(this, "File I/O Error: " + ioex,
//                    "File I/O Error",
//                    JOptionPane.ERROR_MESSAGE);
//            ioex.printStackTrace();
//            return false;
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, "Fatal exception: " + ex,
                    "Error reading file",
                    JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
            return false;
        }


        if (tree == null) {
            JOptionPane.showMessageDialog(this, "The file is not in a suitable format or contains no trees.",
                    "Error reading file",
                    JOptionPane.ERROR_MESSAGE);
            return false;
        }

        FlexibleTree binaryTree = new FlexibleTree(tree, true);
        binaryTree.resolveTree();
        trees.add(binaryTree);
//        if (taxa == null) {
//            taxa = binaryTree;
//        }

        getExportDataAction().setEnabled(true);

        return true;
    }

    public final void doImport() {
        final JFileChooser chooser = new JFileChooser();
        chooser.setMultiSelectionEnabled(true);

        FileNameExtensionFilter filter = new FileNameExtensionFilter("Tab or Comma delimited tables", "csv", "txt");
        chooser.setFileFilter(filter);

        final int returnVal = chooser.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File[] files = chooser.getSelectedFiles();
            importMeasurementFiles(files);
        }
    }

    void importMeasurementFiles(File[] files) {
        List<DataTable<String[]>> dataTables = new ArrayList<DataTable<String[]>>();

        for (int i = 0; i < files.length; i++) {
            String fileName = files[i].getName();

            DataTable<String[]> dataTable;
            try {
                dataTable = DataTable.Text.parse(new FileReader(files[i]), true, false);
                dataTables.add(dataTable);
            } catch (IOException e) {
                JOptionPane.showMessageDialog(this, "Unable to read measurements from file, " + fileName,
                        "Error reading file",
                        JOptionPane.ERROR_MESSAGE);
            }
        }

        for (DataTable<String[]> dataTable : dataTables) {
            document.addTable(dataTable);
        }
    }


    public final void doImportLocations() {
        final JFileChooser chooser = new JFileChooser();
        chooser.setMultiSelectionEnabled(true);

        FileNameExtensionFilter filter = new FileNameExtensionFilter("BEAST log (*.log) Files", "log", "txt");
        chooser.setFileFilter(filter);

        final int returnVal = chooser.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File[] files = chooser.getSelectedFiles();
            importLocationFiles(files);
        }
    }

    void importLocationFiles(File[] files) {
        LogFileTraces[] traces = new LogFileTraces[files.length];

        for (int i = 0; i < files.length; i++) {
            traces[i] = new LogFileTraces(files[i].getName(), files[i]);
        }

        processTraces(traces);
    }

    protected void processTraces(final LogFileTraces[] tracesArray) {

        final JFrame frame = this;

        if (tracesArray.length == 1) {
            try {
                final LogFileTraces traces = tracesArray[0];

                final String fileName = traces.getName();
                final ProgressMonitorInputStream in = new ProgressMonitorInputStream(
                        this,
                        "Reading " + fileName,
                        new FileInputStream(traces.getFile()));
                in.getProgressMonitor().setMillisToDecideToPopup(0);
                in.getProgressMonitor().setMillisToPopup(0);

                final Reader reader = new InputStreamReader(in);

                Thread readThread = new Thread() {
                    public void run() {
                        try {
                            traces.loadTraces(reader);

                            EventQueue.invokeLater(
                                    new Runnable() {
                                        public void run() {
//                                            analyseTraceList(traces);
//                                            addTraceList(traces);
                                        }
                                    });

                        } catch (final TraceException te) {
                            EventQueue.invokeLater(
                                    new Runnable() {
                                        public void run() {
                                            JOptionPane.showMessageDialog(frame, "Problem with trace file: " + te.getMessage(),
                                                    "Problem with tree file",
                                                    JOptionPane.ERROR_MESSAGE);
                                        }
                                    });
                        } catch (final InterruptedIOException iioex) {
                            // The cancel dialog button was pressed - do nothing
                        } catch (final IOException ioex) {
                            EventQueue.invokeLater(
                                    new Runnable() {
                                        public void run() {
                                            JOptionPane.showMessageDialog(frame, "File I/O Error: " + ioex.getMessage(),
                                                    "File I/O Error",
                                                    JOptionPane.ERROR_MESSAGE);
                                        }
                                    });
//                    } catch (final Exception ex) {
//                        EventQueue.invokeLater (
//                                new Runnable () {
//                                    public void run () {
//                                        JOptionPane.showMessageDialog(frame, "Fatal exception: " + ex.getMessage(),
//                                                "Error reading file",
//                                                JOptionPane.ERROR_MESSAGE);
//                                    }
//                                });
                        }

                    }
                };
                readThread.start();

            } catch (FileNotFoundException fnfe) {
                JOptionPane.showMessageDialog(this, "Unable to open file: File not found",
                        "Unable to open file",
                        JOptionPane.ERROR_MESSAGE);
            } catch (IOException ioex) {
                JOptionPane.showMessageDialog(this, "File I/O Error: " + ioex,
                        "File I/O Error",
                        JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(this, "Fatal exception: " + ex,
                        "Error reading file",
                        JOptionPane.ERROR_MESSAGE);
            }

        } else {
            Thread readThread = new Thread() {
                public void run() {
                    try {
                        for (final LogFileTraces traces : tracesArray) {
                            final Reader reader = new FileReader(traces.getFile());
                            traces.loadTraces(reader);

                            EventQueue.invokeLater(
                                    new Runnable() {
                                        public void run() {
//                                            analyseTraceList(traces);
//                                            addTraceList(traces);
                                        }
                                    });
                        }

                    } catch (final TraceException te) {
                        EventQueue.invokeLater(
                                new Runnable() {
                                    public void run() {
                                        JOptionPane.showMessageDialog(frame, "Problem with trace file: " + te.getMessage(),
                                                "Problem with tree file",
                                                JOptionPane.ERROR_MESSAGE);
                                    }
                                });
                    } catch (final InterruptedIOException iioex) {
                        // The cancel dialog button was pressed - do nothing
                    } catch (final IOException ioex) {
                        EventQueue.invokeLater(
                                new Runnable() {
                                    public void run() {
                                        JOptionPane.showMessageDialog(frame, "File I/O Error: " + ioex.getMessage(),
                                                "File I/O Error",
                                                JOptionPane.ERROR_MESSAGE);
                                    }
                                });
//                    } catch (final Exception ex) {
//                        EventQueue.invokeLater (
//                                new Runnable () {
//                                    public void run () {
//                                        JOptionPane.showMessageDialog(frame, "Fatal exception: " + ex.getMessage(),
//                                                "Error reading file",
//                                                JOptionPane.ERROR_MESSAGE);
//                                    }
//                                });
                    }

                }
            };
            readThread.start();

        }
    }

    protected boolean writeToFile(File file) throws IOException {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    protected void doExportData() {
        FileDialog dialog = new FileDialog(this,
                "Export Data File...",
                FileDialog.SAVE);

        dialog.setVisible(true);
        if (dialog.getFile() != null) {
            File file = new File(dialog.getDirectory(), dialog.getFile());

            Writer writer = null;
            try {
                writer = new PrintWriter(file);
//                analysisPanel.writeDataFile(writer);
                writer.close();
            } catch (IOException ioe) {
                JOptionPane.showMessageDialog(this, "Error writing data file: " + ioe.getMessage(),
                        "Export Error",
                        JOptionPane.ERROR_MESSAGE);
            }

        }
    }

    private void setStatusMessage() {
//        Tree tree = treesPanel.getTree();
//        if (tree != null) {
//            String message = "";
//            message += "Tree loaded, " + tree.getTaxonCount() + " taxa";
//
//            TemporalRooting tr = treesPanel.getTemporalRooting();
//            if (tr.isContemporaneous()) {
//                message += ", contemporaneous tips";
//            } else {
//                NumberFormatter nf = new NumberFormatter(3);
//                message += ", dated tips with range " + nf.format(tr.getDateRange());
//            }
//            statusLabel.setText(message);
//        }
    }

    public JComponent getExportableComponent() {

        JComponent exportable = null;
        Component comp = tabbedPane.getSelectedComponent();

        if (comp instanceof Exportable) {
            exportable = ((Exportable) comp).getExportableComponent();
        } else if (comp instanceof JComponent) {
            exportable = (JComponent) comp;
        }

        return exportable;
    }

    @Override
    public void doCopy() {
        StringWriter writer = new StringWriter();
        PrintWriter pwriter = new PrintWriter(writer);

//        for (String tip : treesPanel.getSelectedTips()) {
//            pwriter.println(tip);
//        }

        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringSelection selection = new StringSelection(writer.toString());
        clipboard.setContents(selection, selection);
    }

    @Override
    public Action getImportAction() {
        return getImportMeasurementsAction();
    }

    @Override
    public Action getImportMeasurementsAction() {
        return importMeasurementsAction;
    }

    @Override
    public Action getImportLocationsAction() {
        return importLocationsAction;
    }

    @Override
    public Action getImportTreesAction() {
        return importTreesAction;
    }

    public Action getDeleteItemAction() {
        return getDeleteAction();
    }

    @Override
    public Action getExportAction() {
        return getExportDataAction();
    }

    @Override
    public Action getExportDataAction() {
        return exportDataAction;
    }

    @Override
    public Action getExportPDFAction() {
        return exportPDFAction;
    }

    protected AbstractAction importMeasurementsAction = new AbstractAction("Import Measurements...") {
        public void actionPerformed(ActionEvent ae) {
            doImport();
        }
    };

    protected AbstractAction importLocationsAction = new AbstractAction("Import Locations...") {
        public void actionPerformed(ActionEvent ae) {
//            doImportLocations();
        }
    };

    protected AbstractAction importTreesAction = new AbstractAction("Import Trees...") {
        public void actionPerformed(ActionEvent ae) {
//            doImportTrees();
        }
    };

    protected AbstractAction exportDataAction = new AbstractAction("Export Data...") {
        public void actionPerformed(ActionEvent ae) {
            doExportData();
        }
    };

    protected AbstractAction exportPDFAction = new AbstractAction("Export PDF...") {
        public void actionPerformed(ActionEvent ae) {
//            doExportPDF();
        }
    };

}