package ur_rna.RNAstructureUI.windows;

import ur_rna.RNAstructureUI.ui.Dialogs;
import ur_rna.Utilities.swing.JActionLabel;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.function.Supplier;

/**
 * Used by the Predict*StructureWindow classes to show prediction results to the user.
 * Prediction results include the files produced as well as a list of drawings or plots that
 * can be derived from the results (possibly along with certain input files).
 */
public class PredictionResultsWindow extends InternalWindow {
    private JPanel pnlFiles;
    private JLabel lblResults;
    private JButton btnClose;
    private JPanel pnlDrawings;
    private JPanel pnlContent;
    private JLabel lblFile;
    private JButton btnShowAbbreviations;

    private JPanel pnlClient = new JPanel();

    private HashMap<JButton, PlotResult> plotResults = new HashMap<>();
    int fileCount, plotRow = -1, plotCol;

    public PredictionResultsWindow() {
        this(null);
    }
    public PredictionResultsWindow(String title) {
        if (title != null) setCaption(title);
        pnlFiles.removeAll();
        pnlDrawings.removeAll();

        btnClose.addActionListener(e -> this.dispose());
        btnShowAbbreviations.addActionListener(e -> showAbbreviations());

        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = gbc.weighty = 1;
        add(pnlClient, gbc);
        pnlClient.setLayout(new GridBagLayout());

        gbc.weighty = 0;
        gbc.insets.set(5, 5, 5, 5);
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.NORTH;
        pnlClient.add(pnlContent, gbc);

        setPreferredSize(new Dimension(700, 600));

        pack();
    }
    {
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
        $$$setupUI$$$();
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private void $$$setupUI$$$() {
        pnlContent = new JPanel();
        pnlContent.setLayout(new GridBagLayout());
        pnlContent.setPreferredSize(new Dimension(700, 500));
        final JLabel label1 = new JLabel();
        label1.setFont(new Font(label1.getFont().getName(), Font.BOLD, 14));
        label1.setText("Results of Structure Prediction Calculation");
        GridBagConstraints gbc;
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 2;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        pnlContent.add(label1, gbc);
        lblResults = new JLabel();
        lblResults.setText("Structure Prediction completed successfully and the following files were produced:");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.insets = new Insets(10, 0, 0, 0);
        pnlContent.add(lblResults, gbc);
        final JLabel label2 = new JLabel();
        label2.setText("<html>Additionally, the following <b>drawings and plots</b> can be produced with the results:");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 3;
        gbc.gridwidth = 2;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.NORTH;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(10, 0, 0, 0);
        pnlContent.add(label2, gbc);
        pnlDrawings = new JPanel();
        pnlDrawings.setLayout(new GridBagLayout());
        pnlDrawings.setAutoscrolls(true);
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 4;
        gbc.gridwidth = 2;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.insets = new Insets(0, 10, 0, 0);
        pnlContent.add(pnlDrawings, gbc);
        final JButton button1 = new JButton();
        button1.setText("MFE (Fold) Structure + Probabilities");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        pnlDrawings.add(button1, gbc);
        final JButton button2 = new JButton();
        button2.setText("MFE (Fold) Structure + Probabilities");
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        pnlDrawings.add(button2, gbc);
        final JButton button3 = new JButton();
        button3.setText("MFE (Fold) Structure + Probabilities");
        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        pnlDrawings.add(button3, gbc);
        btnClose = new JButton();
        btnClose.setText("Close    (Done Generating Drawings)");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 7;
        gbc.gridwidth = 2;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(5, 0, 5, 0);
        pnlContent.add(btnClose, gbc);
        final JLabel label3 = new JLabel();
        label3.setText("The drawings listed above can only be automatically generated while this window is open.");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 5;
        gbc.gridwidth = 2;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.insets = new Insets(10, 0, 0, 0);
        pnlContent.add(label3, gbc);
        final JLabel label4 = new JLabel();
        label4.setText("<html>Once you close it, you can make similar drawings using the <b> Draw </b> and <b> Draw Dot Plot </b> commands on the <b> File </b> menu. SHAPE or Probability colorization can be added to structures using the <b> Annotations </b> menu.");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 6;
        gbc.gridwidth = 2;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        pnlContent.add(label4, gbc);
        btnShowAbbreviations = new JButton();
        btnShowAbbreviations.setText("Abbreviations...");
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.EAST;
        pnlContent.add(btnShowAbbreviations, gbc);
        pnlFiles = new JPanel();
        pnlFiles.setLayout(new GridBagLayout());
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.gridwidth = 2;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.NORTH;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        pnlContent.add(pnlFiles, gbc);
        final JLabel label5 = new JLabel();
        label5.setText("<FILE>");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.insets = new Insets(2, 3, 2, 4);
        pnlFiles.add(label5, gbc);
        final JLabel label6 = new JLabel();
        label6.setText("<DESCRIPTION>");
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(2, 3, 2, 4);
        pnlFiles.add(label6, gbc);
        lblFile = new JLabel();
        lblFile.setFont(new Font(lblFile.getFont().getName(), Font.BOLD, lblFile.getFont().getSize()));
        lblFile.setForeground(new Color(-14667710));
        lblFile.setText("p450_MaxExpect.ct");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.insets = new Insets(2, 3, 2, 4);
        pnlFiles.add(lblFile, gbc);
        final JLabel label7 = new JLabel();
        label7.setText("MEA structure prediction");
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(2, 3, 2, 4);
        pnlFiles.add(label7, gbc);
    }
    /** @noinspection ALL */
    public JComponent $$$getRootComponent$$$() { return pnlContent; }

    private static class PlotResult {
        public PlotResult(final String caption, final Supplier<InternalWindow> plotCreator) {
            this.caption = caption;
            this.plotCreator = plotCreator;
        }
        public final String caption;
        public final Supplier<InternalWindow> plotCreator;
        public InternalWindow window;
    }

    public void addFile(String filename, String description) {
        //Desktop.getDesktop().browse();
        File file = new File(filename);
        JActionLabel lbl = new JActionLabel(file.getName(), filename);
        lbl.setFont(lblFile.getFont());
        lbl.setForeground(lblFile.getForeground());
        lbl.setToolTipText(file.toString());
        lbl.addActionListener(this::browseFile);
        JLabel desc = new JLabel(description);

        GridBagConstraints c = new GridBagConstraints();
        c.gridy = fileCount++;
        c.insets = new Insets(2, 3, 2, 4);
        c.gridx = 0;
        c.anchor = GridBagConstraints.WEST;
        c.weightx = 0;
        pnlFiles.add(lbl, c);

        c.gridx = 1;
        c.weightx = 1.0;
        c.anchor = GridBagConstraints.CENTER;
        c.fill = GridBagConstraints.HORIZONTAL;
        pnlFiles.add(desc, c);
    }
    private void browseFile(final ActionEvent event) {
        String path = event.getActionCommand();
        File dir = new File(path).getParentFile();
        if (dir.exists()) try {
            Desktop.getDesktop().open(dir);
        } catch (IOException ex) {
            Dialogs.showError("The location could not be opened: " + ex.toString());
        }
    }

    public void addPlot(String caption, String description, Supplier<InternalWindow> plotCreator) {
        if (plotRow == -1) plotRow = 0;

        PlotResult pr = new PlotResult(caption, plotCreator);
        JButton button = new JButton(caption);
        button.setActionCommand(caption);
        button.setToolTipText(description);
        button.addActionListener(this::plotButtonHandler);
        //JLabel desc = new JLabel(description);
        plotResults.put(button, pr);

        GridBagConstraints c = new GridBagConstraints();
        c.gridy = plotRow;
        c.gridx = plotCol++;
        c.insets = new Insets(2, 3, 2, 4);
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 1;
        pnlDrawings.add(button, c);

        //c.gridx = 1;
        //c.weightx = 1.0;
        //c.anchor = GridBagConstraints.CENTER;
        //c.fill = GridBagConstraints.HORIZONTAL;
        //pnlDrawings.add(desc, c);
    }

    private final Font headerFont = new Font("Arial", Font.BOLD, 12);
    public void addPlotSeparator() {
        plotRow++;
        plotCol = 0;
    }
    public void addPlotHeader(String header, int groupCount) {
        plotRow++;
        JLabel lbl = new JLabel(header);
        lbl.setFont(headerFont);
        GridBagConstraints c = new GridBagConstraints();
        c.gridy = plotRow;
        c.insets = new Insets(2, 3, 2, 4);
        c.gridx = 0;
        c.gridwidth = 10;
        c.anchor = GridBagConstraints.WEST;
        c.weightx = 0;
        pnlDrawings.add(lbl, c);
        addPlotSeparator();
    }

    public void showAbbreviations(boolean show) {
        btnShowAbbreviations.setVisible(show);
    }

    private void plotButtonHandler(final ActionEvent e) {
        JButton btn = (JButton) e.getSource();
        PlotResult pr = plotResults.get(btn);
        if (pr == null) return;
        btn.setEnabled(false);
        String prevText = btn.getText();
        btn.setText("Loading...");
        SwingUtilities.invokeLater(() -> {
            try {
                if (!showPlotWindow(pr.window))
                    showPlotWindow(pr.window = pr.plotCreator.get());
            } catch (Exception ex) {
                Dialogs.showError(ex.getMessage(), "Error Displaying Result Window");
            } finally {
                btn.setText(prevText);
                btn.setEnabled(true);
            }
        });
    }

    private boolean showPlotWindow(InternalWindow window) {
        if (window == null || window.isClosed()) return false;
        window.toFront();
        window.grabFocus();
        return true;
    }

    private void showAbbreviations() {
        GridBagConstraints gbc;
        final JLabel header = new JLabel();
        final JPanel pnl = new JPanel();
        pnl.setLayout(new GridBagLayout());

        header.setText("Abbreviations:");
        header.setFont(header.getFont().deriveFont(Font.BOLD, 14));
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(0, 3, 0, 4);
        pnl.add(header, gbc);
        final JLabel content = new JLabel();
        content.setText("<html><dl><dt><b>MFE</b></dt><dd><b>Minimum Free Energy</b> -- The structure is produced by using the Nearest Neighbor Rules to calculate the Gibbs free energy change (upon folding) for every possible structure and returning the structure(s) with lowest energy.\n</dd>\n\n<dt><b>MEA</b></dt><dd><b>Maximum Expected Accuracy</b> -- The structure is produced by first determining probabilities for all possible base pairs (and by extension all possible unpaired bases). Then the most probable structure is produced by maximizing the sum of probabilities of all base pairs and unpaired bases.\n</dd>\n</dl>");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(0, 3, 2, 4);
        pnl.add(content, gbc);

        pnl.setPreferredSize(new Dimension(600, 200));
        JOptionPane.showMessageDialog(this, pnl, "Structure Abbreviations", JOptionPane.INFORMATION_MESSAGE);
    }

    @Override
    public void pack() {
        super.pack();
        setSize(new Dimension(getWidth(), pnlContent.getHeight() + 10 + getHeight() - pnlClient.getHeight()));
    }
}
