/*
 * BayesFactorsFrame.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.inference.trace.MarginalLikelihoodAnalysis;
import jam.framework.AuxilaryFrame;
import jam.framework.DocumentFrame;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.BorderUIResource;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

public class BayesFactorsFrame extends AuxilaryFrame {

    private List<MarginalLikelihoodAnalysis> marginalLikelihoods = new ArrayList<MarginalLikelihoodAnalysis>();
    private JPanel contentPanel;
    private JComboBox transformCombo;

    private BayesFactorsModel bayesFactorsModel;
    private JTable bayesFactorsTable;

    public BayesFactorsFrame(DocumentFrame frame, String title, boolean hasErrors, boolean isAICM) {

        super(frame);

        setTitle(title);

        bayesFactorsModel = new BayesFactorsModel(hasErrors, isAICM);
        bayesFactorsTable = new JTable(bayesFactorsModel);

        JScrollPane scrollPane1 = new JScrollPane(bayesFactorsTable,
                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

        contentPanel = new JPanel(new BorderLayout(0, 0));
        contentPanel.setOpaque(false);
        contentPanel.setBorder(new BorderUIResource.EmptyBorderUIResource(
                new java.awt.Insets(0, 0, 6, 0)));

        JToolBar toolBar = new JToolBar();
        toolBar.setLayout(new FlowLayout(FlowLayout.LEFT));
        toolBar.setFloatable(false);

        JPanel panel1 = new JPanel(new BorderLayout(0, 0));
        panel1.setOpaque(false);

        JLabel label = new JLabel("<html>Models compared by marginal likelihood (S.E. estimated from bootstrap " +
                "replicates). Differences between log marginal likelihoods (specifically, log Bayes factors) are " +
                "reported.  Positive values indicate better relative model fit of the rows's model compared to the " +
                "column's model.</html>");
        if (isAICM) {
            label = new JLabel("<html>Models compared by AICM (S.E. estimated from bootstrap replicates). " +
                    "Lower AICM values indicate better model fit.  Differences between AICM estimates are reported. " +
                    "Positive values indicate better relative model fit of the row's model compared to the column's " +
                    "model.</html>");
             label.setFont(UIManager.getFont("SmallSystemFont"));
        }
        label.setFont(UIManager.getFont("SmallSystemFont"));
        label.setBorder(new EmptyBorder(5, 5, 5, 5));

        panel1.add(label, BorderLayout.NORTH);
        panel1.add(scrollPane1, BorderLayout.CENTER);

        contentPanel.add(panel1, BorderLayout.CENTER);

        label = new JLabel("<html>Marginal likelihood estimated using the smoothed harmonic mean estimator following " +
                "Newton & Raftery (1994, <i>J. Roy. Statist. Soc. B.</i> <b>56</b>: 3-48) " +
                "with the modifications proprosed by Suchard et al. (2001, <i>MBE</i> <b>18</b>: 1001-1013)</html>");
        if (isAICM) {
            label = new JLabel("<html>AICM estimated using the method-of-moments estimator. Please cite: Baele, " +
                    "Lemey, Bedford, Rambaut, Suchard and Alekseyenko. Improving the accuracy of demographic and " +
                    "molecular clock model comparison while accommodating phylogenetic uncertainty. " + 
                    "Mol. Biol. Evol. 29(9), 2157-2167.</html>");
             label.setFont(UIManager.getFont("SmallSystemFont"));
        }
        label.setFont(UIManager.getFont("SmallSystemFont"));
        label.setBorder(new EmptyBorder(5, 5, 5, 5));

        contentPanel.add(label, BorderLayout.SOUTH);

        setContentsPanel(contentPanel);

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

        getCutAction().setEnabled(false);
        getCopyAction().setEnabled(true);
        getPasteAction().setEnabled(false);
        getDeleteAction().setEnabled(false);
        getSelectAllAction().setEnabled(false);
        getFindAction().setEnabled(false);

        getZoomWindowAction().setEnabled(false);
    }

    public void addMarginalLikelihood(MarginalLikelihoodAnalysis marginalLikelihood) {
        this.marginalLikelihoods.add(marginalLikelihood);
        bayesFactorsModel.fireTableStructureChanged();
        bayesFactorsTable.repaint();
    }

    public void initializeComponents() {

        setSize(new Dimension(640, 480));
    }

    public boolean useExportAction() {
        return true;
    }

    public JComponent getExportableComponent() {
        return contentPanel;
    }

    public void doCopy() {
        java.awt.datatransfer.Clipboard clipboard =
                Toolkit.getDefaultToolkit().getSystemClipboard();

        java.awt.datatransfer.StringSelection selection =
                new java.awt.datatransfer.StringSelection(bayesFactorsModel.toString());

        clipboard.setContents(selection, selection);
    }


    class BayesFactorsModel extends AbstractTableModel {

        String[] columnNames = {"Trace", "ln P(data | model)", "S.E."};
        boolean isAICM = false;

        private DecimalFormat formatter2 = new DecimalFormat("####0.###");

        private int columnCount;

        public BayesFactorsModel(boolean hasErrors, boolean isAICM) {
            this.columnCount = (hasErrors ? 3 : 2);
            this.isAICM = isAICM;
            if (isAICM) {
                columnNames[1] = "AICM";
            }
        }

        public int getColumnCount() {
            return columnCount + marginalLikelihoods.size();
        }

        public int getRowCount() {
            return marginalLikelihoods.size();
        }

        public Object getValueAt(int row, int col) {

            if (col == 0) {
                return marginalLikelihoods.get(row).getTraceName();
            }

            if (col == 1) {
                return formatter2.format(marginalLikelihoods.get(row).getLogMarginalLikelihood());
            } else if (columnCount > 2 && col == 2) {
                return " +/- " + formatter2.format(marginalLikelihoods.get(row).getBootstrappedSE());
            } else {
                if (col - columnCount != row) {
                    double lnML1 = marginalLikelihoods.get(row).getLogMarginalLikelihood();
                    double lnML2 = marginalLikelihoods.get(col - columnCount).getLogMarginalLikelihood();
                    double lnRatio = lnML1 - lnML2;
                    if (isAICM) {
                        lnRatio = lnML2 - lnML1;
                    }
                    return formatter2.format(lnRatio);
                } else {
                    return "-";
                }
            }
        }

        public String getColumnName(int column) {
            if (column < columnCount) {
                return columnNames[column];
            }
            return marginalLikelihoods.get(column - columnCount).getTraceName();
        }

        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        public String toString() {
            StringBuffer buffer = new StringBuffer();

            buffer.append(getColumnName(0));
            for (int j = 1; j < getColumnCount(); j++) {
                buffer.append("\t");
                buffer.append(getColumnName(j));
            }
            buffer.append("\n");

            for (int i = 0; i < getRowCount(); i++) {
                buffer.append(getValueAt(i, 0));
                for (int j = 1; j < getColumnCount(); j++) {
                    buffer.append("\t");
                    buffer.append(getValueAt(i, j));
                }
                buffer.append("\n");
            }

            return buffer.toString();
        }
    }
}