package vismon.plot;

import vismon.gui.OverviewPanel;
import vismon.util.Constants;
import vismon.util.Util;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class HistogramPlot extends JPanel{

    protected final int pdfBinNumber = 25;

    protected double min;
    protected double max;

    protected List<Integer> binHeights;
    protected List<Integer> onTopBinHeights;
    protected Color mainColor;
    protected Color onTopColor;

    protected int x;
    protected int y;
    protected int w;
    protected int h;

    protected static boolean logPlot = false;

    public HistogramPlot() {
        binHeights = new ArrayList<Integer>();
        onTopBinHeights = null;

        mainColor = Color.LIGHT_GRAY;

        x = 0;
        y = 0;
    }

    @Override
    public void paint(Graphics g) {
        h = this.getHeight();
        w = this.getWidth();

        // histogram
        drawHistogram((Graphics2D)g, binHeights, mainColor, x, y, w, h);
        drawHistogram((Graphics2D)g, onTopBinHeights, onTopColor, x, y, w, h);

        // baseline
        g.setColor(Constants.MAIN_COLOR);
        g.drawLine(x, y + h - 1, x + w, y + h - 1);
    }

    public void drawHistogram(Graphics2D g, List<Integer> binHeights, Color color, int x, int y, int w, int h) {
        if (binHeights == null)
            return;

        int binNum = binHeights.size();

        double max = Integer.MIN_VALUE;
        for (int i = 0; i < binNum; ++i)
            if (binHeights.get(i) > max)
                max = binHeights.get(i);
        if (logPlot)
            max = Math.log10(max);

        int binWidth = (int) ((double) w / binNum);
        for (int i = 0; i < binNum; ++i) {
            double value = (double) binHeights.get(i);
            if (logPlot)
                value = Math.log10(value);

            int binHeight = (int) (h * value / max);
            g.setColor(color);
            g.fillRect(x + (int) (i * (double) w / binNum), y + h - binHeight, binWidth, binHeight);
            g.setColor(Util.getDarkerColor(color));
            g.drawRect(x + (int) (i * (double) w / binNum), y + h - binHeight, binWidth, binHeight);
        }
    }

    public void mouseMove(int xMouse, int yMouse) {
        int binNum = binHeights.size();

        int max = Integer.MIN_VALUE;
        for (int i = 0; i < binNum; ++i)
            if (binHeights.get(i) > max) {
                max = binHeights.get(i);
            }

        String toolTipText = "";
        for (int i = 0; i < binNum; ++i) {
            if (x + (int) (i * (double) w / binNum) <= xMouse &&
                    xMouse <= x + (int) ((i+1) * (double) w / binNum)) {
                if (OverviewPanel.getDensityFunc() == OverviewPanel.PDF)
                    toolTipText += binHeights.get(i);
                else
                    toolTipText += Util.format((double) binHeights.get(i) / max, 2);
                break;
            }
        }

        setToolTipText(toolTipText);
    }

    public List<Integer> getPDFBinHeights(List<Double> valuesList) {
        double[] values = new double[valuesList.size()];
        for (int i = 0; i < values.length; ++i)
            values[i] = valuesList.get(i);
        Arrays.sort(values);

        double binRange = (max - min) / pdfBinNumber;

        List[] valuesBins = new List[pdfBinNumber];
        for (int i = 0; i < pdfBinNumber; ++i)
            valuesBins[i] = new ArrayList<Double>();

        for (double v : values) {
            for (int i = 0; i < pdfBinNumber - 1; ++i) {
                double minBin = min + i * binRange;
                double maxBin = min + (i + 1) * binRange;
                if (minBin <= v && v < maxBin) {
                    valuesBins[i].add(v);
                    break;
                }
            }
        }
        valuesBins[valuesBins.length - 1].add(max);

        List<Integer> binsHeight = new ArrayList<Integer>();
        for (int i = 0; i < valuesBins.length; ++i)
            binsHeight.add(valuesBins[i].size());

        return binsHeight;
    }

    protected List<Integer> getCDFBinHeights(List<Integer> pdfBinHeights) {
        List<Integer> cdfBinHeights = new ArrayList<Integer>();
        int sum = 0;
        for (int binHeight : pdfBinHeights) {
            sum += binHeight;
            cdfBinHeights.add(sum);
        }
        return cdfBinHeights;
    }

}
