package vismon.plot;

import vismon.data.Indicator;
import vismon.data.Scenario;
import vismon.data.Session;
import vismon.gui.OptionsPanel;
import vismon.gui.TradeoffsPanel;
import vismon.util.Constants;
import vismon.util.Util;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;

public class BarPlotScenario extends JPanel{

    private final File DOWN_FILE = new File("images/arrow_down.png");
    private final File UP_FILE = new File("images/arrow_up.png");
    private final int UP_DOWN_HALF_WIDTH = 5;

    private final int leftBlankSpace = 10;
    private final int bottomBlankSpace = 5;

    int barWidth;

    private int x;
    private int y;
    private int w;
    private int h;

    private int barNum = Session.getIndicatorNum();

    private Scenario scenario;

    public BarPlotScenario(Scenario scenario) {
        this.scenario = scenario;

        x = leftBlankSpace;
        y = leftBlankSpace;
    }

    @Override
    public void paint(Graphics g) {
        if (!scenario.isVisible())
            return;

        w = this.getWidth() - 2 * leftBlankSpace;
        h = this.getHeight() - leftBlankSpace - bottomBlankSpace;

        barWidth = (int) (((double) w) / Indicator.numOfVisibleIndicators/*barNum*/);

        //drawBarBG((Graphics2D) g);
        drawBars((Graphics2D) g);
        drawAxis((Graphics2D) g);
        drawSortByIndicatorBaseLive((Graphics2D) g);
    }

    private void drawAxis(Graphics2D g) {
        g.setColor(Constants.MAIN_COLOR);
        g.drawLine(x, y, x, y+h);
        g.drawLine(x, y+h, x+w, y+h);
    }

    private void drawSortByIndicatorBaseLive(Graphics2D g) {
        if (TradeoffsPanel.sortByIndicator == null)
            return;

        int rank = Session.getIndicatorRank(TradeoffsPanel.sortByIndicator);
        g.setStroke(new BasicStroke(2.0f));
        g.drawRect(x+rank*barWidth, y, barWidth, h);
        g.setStroke(new BasicStroke(1.0f));
    }

    private void drawBars(Graphics2D g) {
        int index = 0;
        for (int i = 0; i < barNum; ++i) {
            Indicator ithIndicator = Session.getIndicator(i);
            if (!ithIndicator.isVisible())
                continue;

            if (TradeoffsPanel.uncertainty == TradeoffsPanel.NO_UNCERTAINTY ||
                    TradeoffsPanel.uncertainty == TradeoffsPanel.ERROR_BAR) {
                drawAverageBar(g, index, ithIndicator);

                if (TradeoffsPanel.uncertainty == TradeoffsPanel.ERROR_BAR)
                    drawErrorBar(g, index, ithIndicator);
            } else if (TradeoffsPanel.uncertainty == TradeoffsPanel.BOX_PLOT) {
                drawBoxPlot(g, index, ithIndicator);
            } else {
                drawDistribution(g, index, ithIndicator);
            }

            try {
                if (ithIndicator.isMaxBest())
                    g.drawImage(ImageIO.read(new File("images/arrow_up.png")), (int)(x+(index+0.5)*barWidth - UP_DOWN_HALF_WIDTH), y, null);
                else
                    g.drawImage(ImageIO.read(new File("images/arrow_down.png")), (int)(x+(index+0.5)*barWidth - UP_DOWN_HALF_WIDTH), y, null);
            } catch (IOException e) {
                e.printStackTrace();
            }
            ++index;
        }
    }

    private void drawAverageBar(Graphics2D g, int i, Indicator indicator) {
        double min = indicator.getMin();
        double max = indicator.getMax();

        double barValue = scenario.getAverageOrMedianValue(indicator, OptionsPanel.contoursBasedOn);
        int barHeight = (int) (h*(barValue-min)/(max-min));

        drawBarBG(g, i, indicator);

        g.setColor(scenario.getColor());
        g.fillRect(x + i * barWidth, y + h - barHeight, barWidth, barHeight);
    }

    private void drawErrorBar(Graphics2D g, int i, Indicator indicator) {
        double min = indicator.getMin();
        double max = indicator.getMax();

        int percent = Session.getErrorBarPercentage();
        double[] errors = scenario.getErrorBar(indicator,  percent);

        int barMin = (int) (h*(errors[0]-min)/(max-min));
        int barMax = (int) (h*(errors[1]-min)/(max-min));

        g.setColor(Constants.MAIN_COLOR);
        int errorLineX = x + (int) ((i + 0.5) * barWidth);
        g.setStroke(new BasicStroke(2.0f));
        g.drawLine(errorLineX, y + h - barMax, errorLineX, y + h - barMin);
        g.setStroke(new BasicStroke(1.0f));
    }

    private void drawBoxPlot(Graphics2D g, int i, Indicator indicator) {
        double min = indicator.getMin();
        double max = indicator.getMax();

        double scnMin = scenario.getMinValue(indicator);
        double scnMax = scenario.getMaxValue(indicator);
        double scnAvg = scenario.getAverageValue(indicator);
        double scnMed = scenario.getMedianValue(indicator);
        double[] scnQuan = scenario.getQuartiles(indicator);

        int minH = (int) (h * (scnMin - min) / (max - min));
        int maxH = (int) (h * (scnMax - min) / (max - min));
        int avgH = (int) (h * (scnAvg - min) / (max - min));
        int medH = (int) (h * (scnMed - min) / (max - min));
        int q1H = (int) (h * (scnQuan[0] - min) / (max - min));
        int q2H = (int) (h * (scnQuan[1] - min) / (max - min));

        int middleLineX = x + (int) ((i + 0.5) * barWidth);

        drawBarBG(g, i, indicator);

        g.setColor(scenario.getColor());
        g.fillRect(x + i * barWidth, y + h - q2H, barWidth, (q2H - q1H));

        g.setStroke(new BasicStroke(2.0f));
        g.setColor(Constants.MAIN_COLOR);
        g.drawLine(x + i * barWidth+1, y + h - medH, x + (i + 1) * barWidth-1, y + h - medH);
        g.drawLine(middleLineX, y + h - minH, middleLineX, y + h - maxH);
        g.fillOval(middleLineX - 3, y + h - avgH - 3, 6, 6);
        g.setStroke(new BasicStroke(1.0f));
    }

    private void drawDistribution(Graphics2D g, int i, Indicator indicator) {
        double min = indicator.getMin();
        double max = indicator.getMax();

        java.util.List<Double> pdfValues = scenario.getPdfValues().get(indicator);
        double maxValue = scenario.getPdfMax().get(indicator);
        int binNum = pdfValues.size();
        double[] scenarioMinMax = scenario.getErrorBar(indicator, 100);
        for (int j = 0; j < binNum-1; ++j) {
            //int grayScale = (int)(255*(1-pdfValues.get(j)/maxValue));
            //Color color = new Color(grayScale, grayScale, grayScale);

            double grayScale = 1.0 - pdfValues.get(j) / maxValue;
            Color color = Util.getBrighterColor(scenario.getColor(), grayScale);

            double barMinValue = scenarioMinMax[0] + (j*1.0/binNum) * (scenarioMinMax[1]-scenarioMinMax[0]);
            double barMaxValue = scenarioMinMax[0] + ((j+1)*1.0/binNum) * (scenarioMinMax[1]-scenarioMinMax[0]);

            int barMin = (int) (h*(barMinValue-min)/(max-min));
            int barMax = (int) (h*(barMaxValue-min)/(max-min));

            g.setColor(color);
            g.fillRect(x+i*barWidth, y+h-barMax, barWidth, barMax-barMin);
        }
    }

    private void drawBarBG(Graphics2D g, int i, Indicator indicator) {
        g.setColor(Util.getBarBGColor(Session.getIndicatorRank(indicator) / Session.getIndicatorColNum()));
        g.fillRect(x + i * barWidth, y, barWidth, h);
    }

    public void setScenario(Scenario scenario) {
        this.scenario = scenario;
    }

    public void showToolTip(int xMouse, int yMouse) {
        String toolTipString = "";
        int index = 0;
        for (int i = 0; i < barNum; ++i) {
            Indicator ithIndicator = Session.getIndicator(i);
            if (!ithIndicator.isVisible())
                continue;

            if (y <= yMouse && yMouse <= y+h &&
                x+index*barWidth <= xMouse && xMouse < x+(index+1)*barWidth) {
                toolTipString += Session.getIndicator(i).getShortName();
                break;
            }
            ++index;
        }

        setToolTipText(toolTipString);
    }

}
