package view.panels;

import model.Line;
import model.Point;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.DateRange;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.util.ShapeUtilities;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
 *
 * @Author: Armin Wolf
 * @Email: a_wolf28@uni-muenster.de
 * @Date: 28.05.2017.
 */
public class DualityPanel extends JPanel {

    private LinkedList<Line> lines;
    private LinkedList<Point> points;
    private JFreeChart chart;
    private ChartPanel panel;
    private XYSeriesCollection dataset;
    private JScrollPane scrollPane;
    private double domainMin, domainMax;
    private double rangeMin, rangeMax;
    private ValueAxis domain;
    private ValueAxis range;
    private Double delta = 1d;


    private Boolean ctrlPressed = false;
    private Boolean shiftPressed = false;

    public DualityPanel() {
        super();
        this.setPreferredSize(new Dimension(800, 800));
        this.setMinimumSize(new Dimension(800, 800));
        this.setLayout(new BorderLayout());
    }

    public void setPrameters(LinkedList<Line> lines, ArrayList<Point> points, Double xmin, Double xmax, Double ymin, Double ymax) {
        this.lines = new LinkedList<>(lines);
        this.points = new LinkedList<>(points);
        this.domainMin = xmin;
        this.domainMax = xmax;
        this.rangeMin = ymin;
        this.rangeMax = ymax;
    }

    public void createArrangement() {
        dataset = new XYSeriesCollection();

        for (Line p : lines) {
            XYSeries series = new XYSeries(p.getId() + "#" + p.getX1() + "---" + p.getY1());
            series.add(p.getX1(), p.getY1());
            series.add(p.getX2(), p.getY2());
            dataset.addSeries(series);
        }

        XYSeries intersections = new XYSeries("intersections");
        for (Point p : points) {
            intersections.add(p.getX(), p.getY());
        }

        dataset.addSeries(intersections);

        chart = ChartFactory.createXYLineChart(null, null, null, dataset,
                PlotOrientation.HORIZONTAL, false, false, false);

        chart.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON));
        chart.setAntiAlias(true);

        final XYPlot plot = chart.getXYPlot();
        domain = plot.getDomainAxis();
        range = plot.getRangeAxis();

        domain.setRange(domainMin - 1, domainMax + 1);
        range.setRange(rangeMin - 1, rangeMax + 1);

        plot.setBackgroundPaint(Color.WHITE);
        plot.setDomainGridlinePaint(Color.white);
        plot.setRangeGridlinePaint(Color.white);

        final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesLinesVisible(dataset.indexOf(intersections), false);
        plot.setRenderer(renderer);

        for (int i = 0; i < lines.size(); i++) {
            // Setze die Shapes für die Schnittpunkte
            renderer.setSeriesPaint(i, Color.BLUE);
            renderer.setBaseSeriesVisible(true);
        }

        Shape shape = ShapeUtilities.createDiagonalCross(4, 1);
        renderer.setSeriesPaint(lines.size(), Color.BLACK);
        renderer.setSeriesShape(lines.size(), shape);

        panel = new ChartPanel(chart);
        panel.setPreferredSize(new Dimension(800, 800));

        addKeyListener(this);
        addZooming(panel);
        this.setFocusable(true);

        JTextArea info = new JTextArea();
        info.setText("Um in dem Plot hineinzuzoomen kann das Mausrad verwendett werden. \nUm sich anschließen vertikal bzw. horizontal zu bewegen kann die Kombination (Umschalt/Steuerung)\nund Mausrad verwendett werden.");
        Font font = new Font("Serif", Font.ITALIC, 12);
        info.setFont(font);
        info.setForeground(Color.DARK_GRAY);
        info.setEditable(false);
        info.setWrapStyleWord(true);

        this.add(info, BorderLayout.SOUTH);
        this.add(panel, BorderLayout.CENTER);
    }

    public void addKeyListener(JComponent component) {
        component.addKeyListener(new KeyListener() {
            @Override
            public void keyReleased(KeyEvent e) {
                ctrlPressed = false;
                shiftPressed = false;
            }

            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyPressed(KeyEvent e) {
                if (e.isControlDown()) {
                    ctrlPressed = true;
                }

                if (e.isShiftDown()) {
                    shiftPressed = true;
                }
            }
        });
    }

    protected void addZooming(ChartPanel chartPanel) {

        chartPanel.addMouseWheelListener(new MouseWheelListener() {

            public void mouseWheelMoved(MouseWheelEvent e) {

                Double min;
                Double max;
                Double val = e.getPreciseWheelRotation() * -1;

                if (ctrlPressed == true) {
                    //System.out.println("CTRL + ZOOM");
                    min = range.getLowerBound();
                    max = range.getUpperBound();
                    DateRange dateRangeX = move(val, min, max);
                    range.setRange(dateRangeX);
                } else if (shiftPressed == true) {
                    //System.out.println("SHIFT + ZOOM");
                    min = domain.getLowerBound();
                    max = domain.getUpperBound();
                    DateRange dateRangeY = move(val, min, max);
                    domain.setRange(dateRangeY);
                } else {
                    Double x = Double.valueOf(e.getY());
                    Double y = Double.valueOf(e.getY());
                    if (e.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL)
                        return;
                    if (e.getWheelRotation() < 0)
                        increaseZoom((ChartPanel) e.getComponent(), true, x, y);
                    else
                        decreaseZoom((ChartPanel) e.getComponent(), true, x, y);
                }

            }

            private DateRange move(Double val, Double min, Double max) {
                Double minimum = min;
                Double maximum = max;
                delta = Math.abs(maximum - minimum) * 0.01 * val;

                minimum = minimum + delta;
                maximum = maximum + delta;

                return new DateRange(minimum, maximum);
            }

            public synchronized void increaseZoom(JComponent chart, boolean saveAction, Double x, Double y) {
                ChartPanel ch = (ChartPanel) chart;
                zoomChartAxis(ch, true, x, y);
            }

            public synchronized void decreaseZoom(JComponent chart, boolean saveAction, Double x, Double y) {
                ChartPanel ch = (ChartPanel) chart;
                zoomChartAxis(ch, false, x, y);
            }

            private void zoomChartAxis(ChartPanel chartP, boolean increase, Double x, Double y) {
                int width = chartP.getMaximumDrawWidth() - chartP.getMinimumDrawWidth();
                int height = chartP.getMaximumDrawHeight() - chartP.getMinimumDrawWidth();
                if (increase) {
                    chartP.zoomInBoth(width / 2, height / 2);
                } else {
                    chartP.zoomOutBoth(width / 2, height / 2);
                }

            }
        });
    }

    public void clear() {
        if (dataset != null)
            dataset.removeAllSeries();
    }

}