package cz.fidentis.analyst.gui;

import cz.fidentis.analyst.mesh.core.MeshFacet;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;


/**
 * @author Daniel Sokol
 * <p>
 * Class for computing histogram and modifing distances
 */

public class HistogramComponent extends JPanel {
    private final DrawCanvas drawnHistogram = new DrawCanvas();
    private final JSpinner minSpinner = new JSpinner();
    private final JSpinner maxSpinner = new JSpinner();

    private Map<MeshFacet, List<Double>> values;
    private Double minValue = 1.0;
    private Double maxValue = 10.0;
    private Double oneStep = 1.0;
    private final Map<Double, Integer> frequencies = new TreeMap<>();

    private int maxFrequency = 1;
    private int intervalsCount = 20;



    public HistogramComponent() {
        setupComponents();
    }

    /**
     * Add swing components to frame
     */
    private void setupComponents() {
        setBackground(new Color(0, 174, 163));
        setLayout(new BoxLayout(this,BoxLayout.PAGE_AXIS));

        add(new JLabel("Number of intervals:"));
        JSlider intervalSlider = new JSlider(JSlider.HORIZONTAL, 0, 200, intervalsCount);
        intervalSlider.setMajorTickSpacing(20);
        intervalSlider.setMinorTickSpacing(5);
        intervalSlider.setPaintTicks(true);
        intervalSlider.setPaintLabels(true);
        intervalSlider.setBackground(new Color(0, 174, 163));
        add(intervalSlider);

        drawnHistogram.setMinimumSize(new Dimension(getMinimumSize().width, 330));
        drawnHistogram.setMaximumSize(new Dimension(getMaximumSize().width, 330));
        drawnHistogram.setPreferredSize(new Dimension(getPreferredSize().width, 330));
        add(drawnHistogram);
        Box tempBox = new Box(BoxLayout.LINE_AXIS);

        tempBox.add(new JLabel("Select minimum value of distances"));
        tempBox.add(minSpinner);
        tempBox.add(new JLabel("Select maximum value of distances"));
        tempBox.add(maxSpinner);

        JButton newButton = new JButton("Set and Compute");

        newButton.addActionListener(e -> {
            intervalsCount = intervalSlider.getValue();
            minValue = (Double) minSpinner.getValue();
            maxValue = (Double) maxSpinner.getValue();
            setSpinners();
            cutValues();
            computeFrequencies();
            drawnHistogram.repaint();

        });
        tempBox.add(newButton);
        add(tempBox);

    }

    /**
     * Set spinners to new min a max values
     */
    private void setSpinners() {

        SpinnerModel minModel = new SpinnerNumberModel((double) minValue,
                (double) minValue, (double) maxValue, (double) oneStep);
        minSpinner.setModel(minModel);

        SpinnerModel maxModel = new SpinnerNumberModel((double) maxValue,
                (double) minValue, (double) maxValue, (double) oneStep);
        maxSpinner.setModel(maxModel);
    }


    public Map<MeshFacet, List<Double>> getValues() {
        return values;
    }

    /**
     * Set new distances and compute their frequencies
     *
     * @param values Values
     */
    public void setValues(Map<MeshFacet, List<Double>> values) {
        this.values = values;
        findMinMax();
        computeFrequencies();
        drawnHistogram.repaint();
        setSpinners();
    }

    public void computeFrequencies() {

        oneStep = maxValue - minValue;
        if (maxValue < 0) {
            oneStep = Math.abs(maxValue) - Math.abs(minValue);
        }
        oneStep /= intervalsCount;
        //Remove old values in frequencies
        frequencies.clear();

        for (Double i = minValue; i < maxValue; i += oneStep) {
            int currFrequency = 0; //Number of values in current interval
            for (List<Double> facetValues : values.values() ){
                for (Double temp : facetValues) {
                    if (temp >= i && temp < i + oneStep) {
                        currFrequency++;
                    }
                }
            }
            if (currFrequency > maxFrequency) {
                maxFrequency = currFrequency;
            }
            //Add beginning of interval and number of values in it
            frequencies.put(i, currFrequency);
        }
    }

    private void findMinMax() {
        minValue = Double.MAX_VALUE;
        maxValue = Double.MIN_VALUE;
        for (List<Double> facetValues : values.values() ){
            for (Double temp : facetValues) {
                if (temp < minValue) {
                    minValue = temp;
                }
                if (temp > maxValue) {
                    maxValue = temp;
                }
            }
        }
    }

    /**
     * Cut of distances that are before minimum or after maximum distance selected by user
     */
    private void cutValues() {
        for (List<Double> double_values : values.values() ){
            for (int i = 0; i < values.size(); ++i) {
                if (double_values.get(i) < minValue) {
                    double_values.set(i, minValue);
                } else if (double_values.get(i) > maxValue) {
                    double_values.set(i, maxValue);
                }
            }
        }

    }

    public Double getMinValue() {
        return minValue;
    }

    public Double getMaxValue() {
        return maxValue;
    }

    /**
     * @author Daniel Sokol
     * <p>
     * Internal class for drawing histogram
     */
    private class DrawCanvas extends JPanel {
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            setBackground(new Color(0, 174, 163));

            int place = 0;

            int moveStep = this.getWidth() / intervalsCount;
            double heightModificator = (double) this.getHeight() / maxFrequency;

            Graphics2D g2 = (Graphics2D) g;
            Font font = new Font("Arial Narrow", Font.BOLD, 12);

            AffineTransform affineTransform = new AffineTransform();
            affineTransform.rotate(Math.toRadians(90), 0, 0);
            g2.setFont(font.deriveFont(affineTransform));

            for (Map.Entry<Double, Integer> entry : frequencies.entrySet()) {

                g.setColor(Color.ORANGE);
                int height = (int) (entry.getValue() * heightModificator);
                g2.fillRect(place, 300 - height, moveStep - 2, height);

                g.setColor(Color.BLACK);
                String value = String.format("%.4f", entry.getKey());
                g2.drawString(value, place, this.getHeight() - 50);
                g2.drawString(entry.getValue().toString(), place, 50);
                place += moveStep;
            }
        }
    }
}
