/*
 * TimeDensityDialog.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.tracer.analysis;

import dr.app.gui.components.RealNumberField;
import dr.app.gui.util.LongTask;
import dr.inference.trace.TraceList;
import dr.stats.Variate;
import dr.util.FrequencyDistribution;
import jam.panels.OptionsPanel;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

public class TimeDensityDialog {

    private JFrame frame;

    private JComboBox traceCombo;
    private String timeTrace = "None selected";

    private RealNumberField ageOfYoungestField = new RealNumberField();

    private OptionsPanel optionPanel;

    public TimeDensityDialog(JFrame frame) {
        this.frame = frame;

        traceCombo = new JComboBox();

        ageOfYoungestField.setValue(0.0);
        ageOfYoungestField.setColumns(12);

        optionPanel = new OptionsPanel(12, 12);
    }

    private int findArgument(JComboBox comboBox, String argument) {
        for (int i = 0; i < comboBox.getItemCount(); i++) {
            String item = ((String) comboBox.getItemAt(i)).toLowerCase();
            if (item.indexOf(argument) != -1) return i;
        }
        return -1;
    }

    public int showDialog(TraceList traceList, TemporalAnalysisFrame temporalAnalysisFrame) {

        setArguments();

        for (int j = 0; j < traceList.getTraceCount(); j++) {
            String statistic = traceList.getTraceName(j);
            traceCombo.addItem(statistic);
        }
        int index = findArgument(traceCombo, timeTrace);
        if (index == -1) index = findArgument(traceCombo, "root");
        if (index == -1) index = findArgument(traceCombo, "height");
        if (index == -1) index = findArgument(traceCombo, "time");
        if (index == -1) index = 0;
        traceCombo.setSelectedIndex(index);

        final JOptionPane optionPane = new JOptionPane(optionPanel,
                JOptionPane.QUESTION_MESSAGE,
                JOptionPane.OK_CANCEL_OPTION,
                null,
                null,
                null);
        optionPane.setBorder(new EmptyBorder(12, 12, 12, 12));

        final JDialog dialog = optionPane.createDialog(frame, "Time Density Analysis");
        dialog.pack();

        int result = JOptionPane.CANCEL_OPTION;

        dialog.setVisible(true);

        Integer value = (Integer) optionPane.getValue();
        if (value != null && value != -1) {
            result = value;
        }


        return result;
    }

    private void setArguments() {
        optionPanel.removeAll();

        optionPanel.addComponentWithLabel("Select the trace:", traceCombo);

        optionPanel.addSeparator();
        optionPanel.addComponentWithLabel("Age of youngest tip:", ageOfYoungestField);
        JLabel label3 = new JLabel(
                "<html>You can set the age of sampling of the most recent tip in<br>" +
                        "the tree. If this is set to zero then the plot is shown going<br>" +
                        "backwards in time, otherwise forwards in time.</html>");
        label3.setFont(label3.getFont().deriveFont(((float) label3.getFont().getSize() - 2)));
        optionPanel.addSpanningComponent(label3);
    }

    Timer timer = null;

    public void addToTemporalAnalysis(TraceList traceList, TemporalAnalysisFrame frame) {

        final AnalyseTimeDensityTask analyseTask = new AnalyseTimeDensityTask(traceList, frame);

        final ProgressMonitor progressMonitor = new ProgressMonitor(frame,
                "Analysing Time-Density",
                "", 0, analyseTask.getLengthOfTask());
        progressMonitor.setMillisToPopup(0);
        progressMonitor.setMillisToDecideToPopup(0);

        timer = new Timer(1000, new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                progressMonitor.setProgress(analyseTask.getCurrent());
                if (progressMonitor.isCanceled() || analyseTask.done()) {
                    progressMonitor.close();
                    analyseTask.stop();
                    timer.stop();
                }
            }
        });

        analyseTask.go();
        timer.start();
    }

    class AnalyseTimeDensityTask extends LongTask {

        TraceList traceList;
        TemporalAnalysisFrame frame;
        double ageOfYoungest;
        int binCount;
        double minTime;
        double maxTime;

        int stateCount;

        private int lengthOfTask = 0;
        private int current = 0;

        public AnalyseTimeDensityTask(TraceList traceList, TemporalAnalysisFrame frame) {
            this.traceList = traceList;
            this.frame = frame;

            this.binCount = frame.getBinCount();

            lengthOfTask = traceList.getStateCount() + binCount;

            ageOfYoungest = ageOfYoungestField.getValue();

            stateCount = traceList.getStateCount();

        }

        public int getCurrent() {
            return current;
        }

        public int getLengthOfTask() {
            return lengthOfTask;
        }

        public String getDescription() {
            return "Calculating Density...";
        }

        public String getMessage() {
            return null;
        }

        public Object doWork() {

            List<Double> times = traceList.getValues(traceList.getTraceIndex((String) traceCombo.getSelectedItem()));

            minTime = frame.getMinTime();
            maxTime = frame.getMaxTime();

            double delta = (maxTime - minTime) / (binCount - 1);

            FrequencyDistribution frequency = new FrequencyDistribution(minTime, binCount, delta);

            for (int i = 0; i < times.size(); i++) {
                frequency.addValue(getTime(times.get(i)));
            }

            Variate.D xData = new Variate.D();
            Variate.D yData = new Variate.D();

            double x = frequency.getLowerBound() - frequency.getBinSize();
            xData.add(x + (frequency.getBinSize() / 2.0));
            yData.add(0.0);
            x += frequency.getBinSize();

            for (int i = 0; i < frequency.getBinCount(); i++) {
                xData.add(x + (frequency.getBinSize() / 2.0));
                double density = frequency.getFrequency(i) / frequency.getBinSize() / times.size();
                yData.add(density);
                x += frequency.getBinSize();
            }

            xData.add(x + (frequency.getBinSize() / 2.0));
            yData.add(0.0);

            frame.addDensity("Density: " + traceList.getName(), xData, yData);

            return null;
        }

        private double getTime(double height) {
            if (ageOfYoungest > 0.0) {
                return ageOfYoungest - height;
            } else {
                return ageOfYoungest;
            }
        }


    }
}