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.swing.*;
import java.awt.*;
import java.util.List;

public class BarPlotIndicator extends JPanel {

    private final int leftBlankSpace = 25;
    private final int bottomBlankSpace = 10;
    public final static int MAX_BAR_WIDTH = 40;

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

    private int barWidth;

    private Indicator indicator;

    private double min;
    private double max;

    public BarPlotIndicator(Indicator indicator) {
        this.indicator = indicator;

        min = indicator.getMin();
        max = indicator.getMax();

        x = leftBlankSpace;
        y = bottomBlankSpace;
    }

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

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

        drawBars((Graphics2D) g);
        drawAxis((Graphics2D) g);
        drawBorder((Graphics2D) g);
    }

    private void drawAxis(Graphics2D g) {
        g.setColor(Constants.MAIN_COLOR);
        g.setFont(Constants.getPlotFont(9));

        int xAxisLength = Session.getScenarios().size() * MAX_BAR_WIDTH;
        if (xAxisLength == 0)
            return;

        int tickMarkNum = 5;

        double min = indicator.getMin();
        double max = indicator.getMax();
        double tickDif = (max - min) / (tickMarkNum - 1);
        for (int i = 0; i < tickMarkNum; ++i) {
            int tickXLocal = x - 3;
            int tickYLocal = y + h - (int) ((double) i * h / (tickMarkNum - 1));
            Util.drawStringHorRight(g, getTickMark(min + i * tickDif), tickXLocal, tickYLocal);
            g.drawLine(x, tickYLocal, x - 2, tickYLocal);
        }

        if (xAxisLength > w)
            xAxisLength = w;

        g.drawLine(x, y, x, y + h);
        g.drawLine(x, y + h, x + xAxisLength, y + h);
    }

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

        if (indicator.equals(TradeoffsPanel.sortByIndicator)) {
            g.setColor(Constants.MAIN_COLOR);
            g.setStroke(new BasicStroke(2.0f));
            g.drawRect(x, y, w, h);
            g.setStroke(new BasicStroke(1.0f));
        }
    }

    private String getTickMark(double value) {
        return (int) (value) + "";
    }

    private void drawBars(Graphics2D g) {
        int barNum = Scenario.numOfVisibleScenarios;//Session.getScenarios().size();
        barWidth = MAX_BAR_WIDTH;
        if (barNum * barWidth > w)
            barWidth = (int) (w * 1.0 / barNum);

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

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

            } else if (TradeoffsPanel.uncertainty == TradeoffsPanel.BOX_PLOT) {
                drawBoxPlot(g, index, ithScenario);
            } else {
                drawDistribution(g, index, ithScenario);
            }
            ++index;
        }
    }

    private void drawAverageBar(Graphics2D g, int i, Scenario ithScenario) {
        double barValue = ithScenario.getAverageOrMedianValue(indicator, OptionsPanel.contoursBasedOn);
        int barHeight = (int) (h * (barValue - min) / (max - min));

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

    private void drawErrorBar(Graphics2D g, int i, Scenario ithScenario) {
        int percent = Session.getErrorBarPercentage();
        double[] errors = ithScenario.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 middleLineX = x + (int) ((i + 0.5) * barWidth);
        g.setStroke(new BasicStroke(2.0f));
        g.drawLine(middleLineX, y + h - barMax, middleLineX, y + h - barMin);
        g.setStroke(new BasicStroke(1.0f));
    }

    private void drawBoxPlot(Graphics2D g, int i, Scenario ithScenario) {
        double scnMin = ithScenario.getMinValue(indicator);
        double scnMax = ithScenario.getMaxValue(indicator);
        double scnAvg = ithScenario.getAverageValue(indicator);
        double scnMed = ithScenario.getMedianValue(indicator);
        double[] scnQuan = ithScenario.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);

        g.setColor(ithScenario.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, Scenario ithScenario) {
        List<Double> pdfValues = ithScenario.getPdfValues().get(indicator);
        double maxValue = ithScenario.getPdfMax().get(indicator);
        int binNum = pdfValues.size();
        double[] scenarioMinMax = ithScenario.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(ithScenario.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);
        }
    }

    public void showToolTip(int xMouse, int yMouse) {
        String toolTipText = "";
        int index = 0;
        for (int i = 0; i < Session.getScenarios().size(); ++i) {
            Scenario ithScenario = Session.getSortedScenarios(TradeoffsPanel.sortByIndicator).get(i);
            if (!ithScenario.isVisible())
                continue;

            if (y <= yMouse && yMouse <= y + h &&
                    x + index * barWidth <= xMouse && xMouse < x + (index + 1) * barWidth) {
                toolTipText += ithScenario.getName() + ": " +
                        ithScenario.getInfo(indicator);
                break;
            }

            ++index;
        }

        setToolTipText(toolTipText);
    }

}
