package vismon.gui;

import info.clearthought.layout.TableLayout;
import info.clearthought.layout.TableLayoutConstants;
import vismon.data.Indicator;
import vismon.data.Scenario;
import vismon.data.Session;
import vismon.plot.*;
import vismon.util.Constants;

import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;

public class TradeoffsPanel extends JPanel {

    //private int plotHeight = (int)(Toolkit.getDefaultToolkit().getScreenSize().getHeight()*0.15);

    public static int plotType;
    public final static int BAR_PLOT = 0;
    public final static int STAR_GLYPH = 1;
    public final static int MULTIPODES = 2;

    public static int groupBy;
    public final static int GROUP_BY_INDICATOR = 0;
    public final static int GROUP_BY_SCENARIO = 1;

    public static Indicator sortByIndicator;

    public static int uncertainty;
    public final static int NO_UNCERTAINTY = 0;
    public final static int ERROR_BAR = 1;
    public final static int BOX_PLOT = 2;
    public final static int DISTRIBUTION = 3;

    private JPanel northPanel;
    private JScrollPane southPanel;
    private JPanel plotPanel;
    private JComboBox sortByIndComboBox;
    private JComboBox uncertaintyComboBox;
    private String[] uncertaintyOptions;

    private List<BarPlotIndicator> barPlotIndicatorsList;
    private List<BarPlotScenario> barPlotScenariosList;
    private List<StarGlyphIndicator> starGlyphIndicatorsList;
    private List<StarGlyphScenario> starGlyphScenariosList;
    private List<MultipodesIndicator> multipodesIndicatorsList;
    private List<MultipodesScenario> multipodesScenariosList;

    private boolean needsRepaintInMax;

    //private boolean maximized;

    TradeoffsPanel() {
        super();

        barPlotIndicatorsList = new ArrayList<BarPlotIndicator>();
        barPlotScenariosList = new ArrayList<BarPlotScenario>();
        starGlyphIndicatorsList = new ArrayList<StarGlyphIndicator>();
        starGlyphScenariosList = new ArrayList<StarGlyphScenario>();
        multipodesIndicatorsList = new ArrayList<MultipodesIndicator>();
        multipodesScenariosList = new ArrayList<MultipodesScenario>();

        plotType = BAR_PLOT;
        groupBy = GROUP_BY_INDICATOR;
        sortByIndicator = null;
        uncertainty = NO_UNCERTAINTY;
        init();
    }

    protected void init() {
        setBackground(Color.WHITE);

        JPanel mainPanel = new JPanel();
        double mainLayout[][] = {{TableLayoutConstants.FILL},           // COLUMNS
                {25, TableLayoutConstants.FILL}};       // ROWS
        mainPanel.setLayout(new TableLayout(mainLayout));

        northPanel = new JPanel();

        plotPanel = new JPanel();
        plotPanel.setBackground(Color.WHITE);
        /*southPanel = new JScrollPane(plotPanel, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        southPanel.setBorder(BorderFactory.createEmptyBorder());*/

        mainPanel.add(northPanel, "0,0");
        //mainPanel.add(southPanel, "0,1");
        mainPanel.add(plotPanel, "0,1");

        String plotTypeLabel = " Plot type";
        String groupByLabel = "  Group by";
        String sortByIndLabel = "  Sort scenarios by";
        String uncertaintyLabel = "  Uncertainty";

        double northLayout[][] = {{60, TableLayoutConstants.FILL,
                                   68, TableLayoutConstants.FILL,
                                   118, TableLayoutConstants.FILL,
                                   80, TableLayoutConstants.FILL},                           // COLUMNS
                {TableLayoutConstants.FILL}};                                                                 // ROWS
        northPanel.setLayout(new TableLayout(northLayout));
        northPanel.add(new JLabel(plotTypeLabel), "0,0");
        addPlotTypeComboBox();

        northPanel.add(new JLabel(groupByLabel), "2,0");
        addGroupByComboBox();

        northPanel.add(new JLabel(sortByIndLabel), "4,0");

        northPanel.add(new JLabel(uncertaintyLabel), "6,0");
        addUncertaintyComboBox();

        this.setLayout(new BorderLayout());
        this.add(mainPanel);
    }

    private void addPlotTypeComboBox() {
        String[] types = new String[]{"Bar charts", "Star glyphs", "Multipodes plot"};
        final JComboBox plotTypeComboBox = new JComboBox(types);
        plotTypeComboBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                plotType = plotTypeComboBox.getSelectedIndex();
                uncertaintyComboBox.setEnabled(plotType != STAR_GLYPH);

                addPlots();
                needsRepaintInMax = true;
                Vismon.getVismonInstance().repaintTradeoffsPanel();
            }
        });

        northPanel.add(plotTypeComboBox, "1,0");
    }

    private void addGroupByComboBox() {
        String[] groups = new String[]{"Indicator", "Scenario"};
        final JComboBox groupByComboBox = new JComboBox(groups);
        groupByComboBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                groupBy = groupByComboBox.getSelectedIndex();
                addPlots();
                needsRepaintInMax = true;
                Vismon.getVismonInstance().repaintTradeoffsPanel();
            }
        });

        northPanel.add(groupByComboBox, "3, 0");
    }

    public void addSortByIndComboBox() {
        String[] indNames = new String[Session.getIndicatorNum() + 1];
        indNames[0] = "None";
        for (int i = 1; i < indNames.length; ++i)
            indNames[i] = Session.getIndicator(i-1).getName();

        sortByIndComboBox = new JComboBox(indNames);
        sortByIndComboBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                if (sortByIndComboBox.getSelectedIndex() == 0)
                    sortByIndicator = null;
                else
                    sortByIndicator = Session.getIndicator(sortByIndComboBox.getSelectedIndex()-1);
                addPlots();
                needsRepaintInMax = false;
                Vismon.getVismonInstance().repaintTradeoffsPanel();
            }
        });

        northPanel.add(sortByIndComboBox, "5, 0");
    }

    private void addUncertaintyComboBox() {
        uncertaintyOptions = new String[]{"None", "Error bars (" + Session.getErrorBarPercentage() + "% prob. int.)", "Box Plot", "Shaded distribution"};
        uncertaintyComboBox = new JComboBox(uncertaintyOptions);
        uncertaintyComboBox.setSelectedIndex(uncertainty);
        uncertaintyComboBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                uncertainty = uncertaintyComboBox.getSelectedIndex();

                needsRepaintInMax = false;
                Vismon.getVismonInstance().repaintTradeoffsPanel();
            }
        });

        northPanel.add(uncertaintyComboBox, "7, 0");
    }

    public void setUncertaintyComboBoxSelected() {
        boolean enabled = false;
        if (Session.getSimulationNum() > 1) {
            uncertainty = ERROR_BAR;
            enabled = true;
        }
        uncertaintyComboBox.setSelectedIndex(uncertainty);
        uncertaintyComboBox.setEnabled(enabled);
    }

    public void createIndicatorPlots() {
        barPlotIndicatorsList.clear();
        starGlyphIndicatorsList.clear();
        multipodesIndicatorsList.clear();

        for (Indicator indicator : Session.getIndicators()) {
            final BarPlotIndicator ithBarPlotIndicator = new BarPlotIndicator(indicator);
            ithBarPlotIndicator.addMouseMotionListener(new MouseMotionListener() {
                public void mouseDragged(MouseEvent mouseEvent) {
                }

                public void mouseMoved(MouseEvent mouseEvent) {
                    ithBarPlotIndicator.showToolTip(mouseEvent.getX(), mouseEvent.getY());
                }
            });
            barPlotIndicatorsList.add(ithBarPlotIndicator);

            final StarGlyphIndicator ithStarGlyphIndicator = new StarGlyphIndicator(indicator);
            ithStarGlyphIndicator.addMouseMotionListener(new MouseMotionListener() {
                public void mouseDragged(MouseEvent mouseEvent) {
                }

                public void mouseMoved(MouseEvent mouseEvent) {
                    ithStarGlyphIndicator.showToolTip(mouseEvent.getX(), mouseEvent.getY());
                }
            });
            starGlyphIndicatorsList.add(ithStarGlyphIndicator);

            final MultipodesIndicator ithMultipodesIndicator = new MultipodesIndicator(indicator);
            ithMultipodesIndicator.addMouseMotionListener(new MouseMotionListener() {
                public void mouseDragged(MouseEvent mouseEvent) {
                }

                public void mouseMoved(MouseEvent mouseEvent) {
                    ithMultipodesIndicator.showToolTip(mouseEvent.getX(), mouseEvent.getY());
                }
            });
            multipodesIndicatorsList.add(ithMultipodesIndicator);
        }
    }

    public void createScenarioPlots() {
        barPlotScenariosList.clear();
        starGlyphScenariosList.clear();
        multipodesScenariosList.clear();
        for (Scenario scenario : Session.getScenarios()) {
            final BarPlotScenario ithBarPlotScenario = new BarPlotScenario(scenario);
            ithBarPlotScenario.addMouseMotionListener(new MouseMotionListener() {
                public void mouseDragged(MouseEvent mouseEvent) {
                }

                public void mouseMoved(MouseEvent mouseEvent) {
                    ithBarPlotScenario.showToolTip(mouseEvent.getX(), mouseEvent.getY());
                }
            });
            barPlotScenariosList.add(ithBarPlotScenario);

            final StarGlyphScenario ithStarGlyphScenario = new StarGlyphScenario(scenario);
            ithStarGlyphScenario.addMouseMotionListener(new MouseMotionListener() {
                public void mouseDragged(MouseEvent mouseEvent) {
                }

                public void mouseMoved(MouseEvent mouseEvent) {
                    ithStarGlyphScenario.showToolTip(mouseEvent.getX(), mouseEvent.getY());
                }
            });
            starGlyphScenariosList.add(ithStarGlyphScenario);

            final MultipodesScenario ithMultipodesScenario = new MultipodesScenario(scenario);
            ithMultipodesScenario.addMouseMotionListener(new MouseMotionListener() {
                public void mouseDragged(MouseEvent mouseEvent) {
                }

                public void mouseMoved(MouseEvent mouseEvent) {
                    ithMultipodesScenario.showToolTip(mouseEvent.getX(), mouseEvent.getY());
                }
            });
            multipodesScenariosList.add(ithMultipodesScenario);
        }
    }

    public void addPlots() {
        if (groupBy == GROUP_BY_INDICATOR)
            addIndicatorPanels();
        else {
            createScenarioPlots();
            addScenarioPanels();
        }
    }

    /**
     * Adds a plot and its label to a table. It assumes that the table's layout is created using the
     * createTableLayout.
     */
    private void addLabelAndPlot(int c, JLabel label, JPanel plot) {
        plotPanel.add(label, (2 * c + 1) + ",1");
        if (plot != null)
            plotPanel.add(plot, (2 * c + 1) + ",2");
    }

    private TableLayout createTableLayout(int topGap, int labelHeight, int vertGap, int horizGap,
                                          int colNum) {
        int rowNum = 1;

        double[][] layout = new double[2][];
        layout[0] = new double[2 * colNum + 1];
        layout[1] = new double[3 * rowNum + 1];
        for (int i = 0; i < colNum; ++i) {
            layout[0][2 * i] = horizGap;
            layout[0][2 * i + 1] = TableLayoutConstants.FILL;
        }
        layout[0][2 * colNum] = horizGap;

        layout[1][0] = topGap;
        for (int i = 0; i < rowNum; ++i) {
            layout[1][3 * i + 1] = labelHeight;
            layout[1][3 * i + 2] = TableLayoutConstants.FILL;
            layout[1][3 * i + 3] = vertGap;
        }
        return new TableLayout(layout);
    }

    public void addIndicatorPanels() {
        int vGap, hGap, colNum;
        colNum = Indicator.numOfVisibleIndicators;//Session.getIndicatorNum();
        hGap = 5;
        vGap = 1;
        TableLayout tableLayout = createTableLayout(5, 15, vGap, hGap, colNum);
        plotPanel.setLayout(tableLayout);
        //keep the proper indicators list in a local variable
        List<? extends JPanel> indicatorsList = null;
        if (plotType == BAR_PLOT) {
            indicatorsList = barPlotIndicatorsList;
        } else if (plotType == STAR_GLYPH) {
            indicatorsList = starGlyphIndicatorsList;
        } else {
            indicatorsList = multipodesIndicatorsList;
        }

        List<Indicator> visibleIndicators = Session.getVisibleIndicators();
        for (int c = 0; c < colNum; ++c) {
            Indicator ithIndicator = visibleIndicators.get(c);//Session.getIndicator(index);
            int indicatorIndex = Session.getIndicatorRank(ithIndicator);

            JLabel label = new JLabel(ithIndicator.getShortName(), SwingConstants.CENTER);
            if (ithIndicator.isMaxBest())
                label.setIcon(new ImageIcon("images/arrow_up.png"));
            else
                label.setIcon(new ImageIcon("images/arrow_down.png"));
            label.setFont(Constants.OVERVIEW_TITLE_FONT);
            label.setForeground(Constants.MAIN_COLOR);
            JPanel plot = indicatorsList.get(indicatorIndex);

            addLabelAndPlot(c, label, plot);
        }
    }

    public void addScenarioPanels() {
        int scnNum = Scenario.numOfVisibleScenarios;//Session.getScenarios().size();

        int colNum, vGap, hGap;
        colNum = scnNum;
        hGap = 2;
        vGap = 5;

        TableLayout tableLayout = createTableLayout(5, 15, vGap, hGap, colNum);
        plotPanel.setLayout(tableLayout);

        //keep the proper scenario list in a local variable
        List<? extends JPanel> scenariosList;
        if (plotType == BAR_PLOT)
            scenariosList = barPlotScenariosList;
        else if (plotType == STAR_GLYPH)
            scenariosList = starGlyphScenariosList;
        else
            scenariosList = multipodesScenariosList;

        int index = 0;
        for (int c = 0; c < Session.getScenarios().size(); ++c) {
            Scenario scenario = Session.getSortedScenarios(sortByIndicator).get(c);
            if (!scenario.isVisible())
                continue;

            barPlotScenariosList.get(index).setScenario(scenario);
            starGlyphScenariosList.get(index).setScenario(scenario);
            multipodesScenariosList.get(index).setScenario(scenario);

            JLabel label = new JLabel(scenario.getName(), SwingConstants.CENTER);
            label.setFont(Constants.OVERVIEW_TITLE_FONT);
            label.setForeground(Constants.MAIN_COLOR);
            JPanel plot = scenariosList.get(index);
            addLabelAndPlot(index, label, plot);
            ++index;
        }
    }

    public void updateUncertaintyOptions() {
        uncertaintyOptions = new String[]{"None", "Error bars (" + Session.getErrorBarPercentage() + "% prob. int.)", "Shaded distribution"};
        this.repaint();
    }

    public boolean needsRepaintingInMax() {
        return needsRepaintInMax;
    }
}
