package vismon.plot;

import vismon.data.*;
import vismon.util.Constants;
import vismon.util.TickMarkCalculator;
import vismon.util.Util;

import javax.swing.*;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;

public class ScatterPlot extends JPanel {

    private final int DOT_RADIUS = 5;
    private final int DOT_MOUSE_DIF = 5;

    protected final int leftBottomBorderWidth = 30;
    protected final int rightTopBorderWidth = 20;

    private Indicator xIndicator;
    private Indicator yIndicator;

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

    private int previousW;
    private int previousH;

    private Object[] xAxisTickMarks;
    private double xMinLabel;
    private double xMaxLabel;

    private Object[] yAxisTickMarks;
    private double yMinLabel;
    private double yMaxLabel;

    private Map<DataItem, int[]> dotLocations;
    private DataItem mouseOnDataItem;

    public ScatterPlot(Indicator xInd, Indicator yInd) {
        this.xIndicator = xInd;
        this.yIndicator = yInd;

        x = this.getX() + leftBottomBorderWidth;
        y = this.getY() + rightTopBorderWidth;

        dotLocations = new HashMap<DataItem, int[]>();
        mouseOnDataItem = null;
    }

    @Override
    public void paint(Graphics g) {
        previousW = w;
        previousH = h;
        w = this.getWidth() - leftBottomBorderWidth - rightTopBorderWidth;
        h = this.getHeight() - leftBottomBorderWidth - rightTopBorderWidth;

        drawBorder((Graphics2D) g);
        drawScatterDots((Graphics2D) g);
    }

    private void clear(Graphics g) {
        g.clearRect(x, y, w, h);
        g.setColor(Color.WHITE);
        g.fillRect(x, y, w, h);
    }

    private void drawScatterDots(Graphics2D g) {
        dotLocations.clear();
        for (DataItem dataItem : Session.getDataItems()) {
            double xValue = dataItem.getAverageValue(xIndicator);
            double yValue = dataItem.getAverageValue(yIndicator);

            int xLocal = x + (int) (w * (xValue - xMinLabel) / (xMaxLabel - xMinLabel));
            int yLocal = y + h - (int) (h * (yValue - yMinLabel) / (yMaxLabel - yMinLabel));

            if (dataItem.isFiltered()) {
                g.setColor(Color.WHITE);
                g.fillOval(xLocal-DOT_RADIUS, yLocal-DOT_RADIUS, 2*DOT_RADIUS, 2*DOT_RADIUS);
                g.setColor(Color.LIGHT_GRAY);
                g.drawOval(xLocal-DOT_RADIUS, yLocal-DOT_RADIUS, 2*DOT_RADIUS, 2*DOT_RADIUS);
            }

            dotLocations.put(dataItem, new int[]{xLocal, yLocal});
        }

        for (DataItem dataItem : Session.getDataItems()) {
            double xValue = dataItem.getAverageValue(xIndicator);
            double yValue = dataItem.getAverageValue(yIndicator);

            int xLocal = x + (int) (w * (xValue - xMinLabel) / (xMaxLabel - xMinLabel));
            int yLocal = y + h - (int) (h * (yValue - yMinLabel) / (yMaxLabel - yMinLabel));

            if (!dataItem.isFiltered()) {
                g.setColor(Color.LIGHT_GRAY);
                g.fillOval(xLocal-DOT_RADIUS, yLocal-DOT_RADIUS, 2*DOT_RADIUS, 2*DOT_RADIUS);
                g.setColor(Constants.MAIN_COLOR);
                g.drawOval(xLocal-DOT_RADIUS, yLocal-DOT_RADIUS, 2*DOT_RADIUS, 2*DOT_RADIUS);
            }
        }

        // DRAWING THE MOUSE_ON_DATAITEM RED
        if (mouseOnDataItem != null) {
            MngAction mngAction = mouseOnDataItem.getMngAction();
            double xValue = mouseOnDataItem.getAverageValue(xIndicator);
            double yValue = mouseOnDataItem.getAverageValue(yIndicator);

            int xLocal = x + (int) (w * (xValue - xMinLabel) / (xMaxLabel - xMinLabel));
            int yLocal = y + h - (int) (h * (yValue - yMinLabel) / (yMaxLabel - yMinLabel));

            g.setColor(Constants.MAIN_COLOR);
            g.fillOval(xLocal - DOT_RADIUS, yLocal - DOT_RADIUS, 2 * DOT_RADIUS, 2 * DOT_RADIUS);
            g.drawOval(xLocal-DOT_RADIUS, yLocal-DOT_RADIUS, 2*DOT_RADIUS, 2*DOT_RADIUS);

            setToolTipText("(" + Util.roundToOneHundredth(mngAction.getMngValue(Session.getMngOption(0))) + ", " +
                           Util.roundToOneHundredth(mngAction.getMngValue(Session.getMngOption(1))) + ")");
        } else {
            setToolTipText("");
        }

        for (Scenario scenario : Session.getScenarios()) {
            double xValue = scenario.getAverageValue(xIndicator);
            double yValue = scenario.getAverageValue(yIndicator);

            int xLocal = x + (int) (w * (xValue - xMinLabel) / (xMaxLabel - xMinLabel));
            int yLocal = y + h - (int) (h * (yValue - yMinLabel) / (yMaxLabel - yMinLabel));

            g.setColor(scenario.getColor());
            g.fillOval(xLocal-DOT_RADIUS, yLocal-DOT_RADIUS, 2*DOT_RADIUS, 2*DOT_RADIUS);
            g.setColor(Constants.MAIN_COLOR);
            g.drawOval(xLocal-DOT_RADIUS, yLocal-DOT_RADIUS, 2*DOT_RADIUS, 2*DOT_RADIUS);
        }
    }

    private void drawBorder(Graphics2D g) {
        g.setColor(Constants.MAIN_COLOR);
        g.drawRect(x, y, w, h);

        int labelAxisDistance = 4;
        drawXAxisLabels(g, labelAxisDistance);
        drawYAxisLabels(g, labelAxisDistance);
    }

    private void drawXAxisLabels(Graphics2D g, int labelAxisDistance) {
        int xTickNum = 6;
        int xFontSize = Constants.PLOT_FONT.getSize();
        if (xAxisTickMarks == null || previousW != w)
            xAxisTickMarks = TickMarkCalculator.getTickMarkValues(xIndicator.getMin(), xIndicator.getMax(),
                    xTickNum, xFontSize, w, leftBottomBorderWidth, g);
        xMinLabel = (Double) xAxisTickMarks[0];
        xMaxLabel = (Double) xAxisTickMarks[1];
        double xStep = (Double) xAxisTickMarks[2];
        int xFormat = (Integer) xAxisTickMarks[3];
        xFontSize = (Integer) xAxisTickMarks[4];
        int xOrientation = (Integer) xAxisTickMarks[5];

        g.setFont(Constants.getPlotFont(xFontSize));
        xTickNum = (int) ((xMaxLabel - xMinLabel) / xStep);
        for (int i = 0; i <= xTickNum; ++i) {
            String label = TickMarkCalculator.labelFormatter(xMinLabel + i*xStep, xFormat);

            int tickXPosition = x + (int) ((double)w * i /xTickNum);
            int tickYPosition = y + h + labelAxisDistance;

            g.drawLine(tickXPosition, y + h, tickXPosition, tickYPosition);
            if (xOrientation == TickMarkCalculator.HOR)
                Util.drawStringHorCentre(g, label, tickXPosition, tickYPosition + labelAxisDistance);
            else
                Util.drawStringVerTop(g, label, tickXPosition, tickYPosition);
        }
    }

    private void drawYAxisLabels(Graphics2D g, int labelAxisDistance) {
        int yTickNum = 6;
        int yFontSize = Constants.PLOT_FONT.getSize();
        if (yAxisTickMarks == null || previousH != h)
            yAxisTickMarks = TickMarkCalculator.getTickMarkValues(yIndicator.getMin(), yIndicator.getMax(),
                    yTickNum, yFontSize, h, leftBottomBorderWidth, g);
        yMinLabel = (Double) yAxisTickMarks[0];
        yMaxLabel = (Double) yAxisTickMarks[1];
        double yStep = (Double) yAxisTickMarks[2];
        int yFormat = (Integer) yAxisTickMarks[3];
        yFontSize = (Integer) yAxisTickMarks[4];
        int yOrientation = (Integer) yAxisTickMarks[5];

        g.setFont(Constants.getPlotFont(yFontSize));
        yTickNum = (int) ((yMaxLabel - yMinLabel) / yStep);
        for (int i = 0; i <= yTickNum; ++i) {
            String label = TickMarkCalculator.labelFormatter(yMinLabel + i*yStep, yFormat);

            int tickXPosition = x - labelAxisDistance;
            int tickYPosition = y + h - (int) ((double)h * i /yTickNum);

            g.drawLine(x, tickYPosition, tickXPosition, tickYPosition);
            if (yOrientation == TickMarkCalculator.VER)
                Util.drawStringHorRight(g, label, tickXPosition, tickYPosition);
            else
                Util.drawStringVerCentreRight(g, label, tickXPosition - labelAxisDistance, tickYPosition);
        }
    }

    public void setMouseOnDataItem(int xMouse, int yMouse) {
        mouseOnDataItem = getMouseOnDataItem(xMouse, yMouse);
    }

    public void mouseClicked(int xMouse, int yMouse) {
        DataItem clickedDataItem = getMouseOnDataItem(xMouse, yMouse);
        if (clickedDataItem == null)
            return;

        MngAction clickedMngAction = clickedDataItem.getMngAction();
        Session.createScenario(clickedMngAction.getMngValue(Session.getMngOption(0)),
                               clickedMngAction.getMngValue(Session.getMngOption(1)));
    }

    public DataItem getMouseOnDataItem(int xMouse, int yMouse) {
        for (DataItem dataItem : Session.getDataItems()) {
            int[] location = dotLocations.get(dataItem);
            int xDot = location[0];
            int yDot = location[1];
            if (xDot-DOT_MOUSE_DIF <= xMouse && xMouse <= xDot+DOT_MOUSE_DIF &&
                yDot-DOT_MOUSE_DIF <= yMouse && yMouse <= yDot+DOT_MOUSE_DIF) {
                return dataItem;
            }
        }

        return null;
    }



    public Indicator getXIndicator() {
        return xIndicator;
    }

    public Indicator getYIndicator() {
        return yIndicator;
    }

    public void setMouseOnDataItem(DataItem mouseOnDataItem) {
        this.mouseOnDataItem = mouseOnDataItem;
    }
}
