package vismon.component;

import vismon.util.Constants;
import vismon.util.Util;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.IOException;


public class MonoSlider extends JComponent
        implements MouseListener, MouseMotionListener, ChangeListener {

    protected static int              PICK_WIDTH = 3;
    protected Font                    sliderFont;

    //EVENT HANDLING
    protected static final int        PICK_NONE = 0;
    protected static final int        PICK_HANDLE = 1;

    protected double                  value;
    protected double                  minimum;
    protected String                  minimumString;
    protected double                  maximum;
    protected String                  maximumString;

    protected boolean                 enabled = true;

    //PAINT METHOD
    protected int                     pick;
    protected int                     mouseX;

    protected Color                   bgColor = Color.WHITE;
    protected Color                   acceptableColor = Constants.MAIN_COLOR;
    protected Color                   unacceptableColor = Color.LIGHT_GRAY;

    protected int                     totalWidth;
    protected int                     totalHeight;

    protected int                     startX;
    protected int                     startY;
    protected int                     sliderWidth;
    protected int                     sliderHeight;

    //protected Indicator               indicator;
    protected boolean                 isMinBest;

    /**
     * Constructs a new range slider.
     *
     * @param min   - the minimum value of the range.
     * @param max   - the maximum value of the range.
     * @param isMinBest  - min/max preference of indicator's value
     */
    public MonoSlider(double min, double max, boolean isMinBest, int w, int h) {
        minimum = min;
        maximum = max;
        this.isMinBest = isMinBest;
        if (isMinBest)
            this.value = max;
        else
            this.value = min;

        if (min % 1 == 0.0)
            minimumString = (int)(min) + "";
        else
            minimumString = min + "";

        if (max % 1 == 0.0)
            maximumString = (int)(max) + "";
        else
            maximumString = max + "";

        sliderFont = new Font(Constants.OVERVIEW_FONT.getFamily(),
                              Constants.OVERVIEW_FONT.getStyle(),
                              Constants.OVERVIEW_FONT.getSize() - 2);

        addMouseListener(this);
        addMouseMotionListener(this);

        totalWidth = w;
        totalHeight = h;
        setSize(w, h);
    }

    public double getValue() {
        return value;
    }

    public double getMinimum() {
        return minimum;
    }

    public double getMaximum() {
        return maximum;
    }

    public void setValue(double v) {
        double value = v;

        if (v > getMaximum())
            value = getMaximum();
        else if (v < getMinimum())
            value = getMinimum();

        setRangeProperties(value, getMinimum(), getMaximum());
    }

    public void setRangeProperties(double value, double min, double max) {
        this.value = value;
        minimum = min;
        maximum = max;
    }


    protected Rectangle getInBounds() {
        Dimension d = getSize();
        Insets insets = getInsets();
        return new Rectangle(insets.left, insets.top, d.width - insets.left - insets.right, d.height - insets.top - insets.bottom);
    }

    @Override
    protected void paintComponent(Graphics g) {
        Rectangle rect = getInBounds();

        startX = rect.x;
        startY = rect.y;
        sliderWidth = rect.width;
        sliderHeight = rect.height;

        int barTopY = startY + sliderHeight;
        int baseLineY = startY + (int)(sliderHeight * 0.5);


        g.setColor(bgColor);
        g.fillRect(rect.x, rect.y, rect.width, rect.height);

        int valueX = toScreenX(getValue());

        //DRAWING THE BASE LINE
        if (isMinBest)
            g.setColor(acceptableColor);
        else
            g.setColor(unacceptableColor);
        g.drawLine(startX, baseLineY, valueX, baseLineY);

        if (isMinBest)
            g.setColor(unacceptableColor);
        else
            g.setColor(acceptableColor);
        g.drawLine(valueX, baseLineY, startX + sliderWidth, baseLineY);

        //DRAWING THE VERTICAL LINES FOR MIN AND MAX VALUES
        g.setColor(acceptableColor);
        g.drawLine(valueX, startY, valueX, barTopY);
        g.drawLine(valueX + 1, startY, valueX + 1, barTopY);
        g.drawLine(valueX + 2, startY, valueX + 2, barTopY);

        drawMinimumValue(g, startX, startY + sliderHeight);
        drawMaximumValue(g, startX + sliderWidth, startY + sliderHeight);

        try {
            if (isMinBest)
                g.drawImage(ImageIO.read(new File("images/arrow_left.png")), valueX - 4, startX, null);
            else
                g.drawImage(ImageIO.read(new File("images/arrow_right.png")), valueX + 2, startX, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    protected void drawMinimumValue(Graphics g, int x, int y) {
        g.setFont(sliderFont);
        g.drawString(minimumString, x + 4, y);
    }

    protected void drawMaximumValue(Graphics g, int x, int y) {
        int textWidth = g.getFontMetrics().stringWidth(maximumString);
        g.drawString(maximumString, x - textWidth - 4, y);
    }

    protected void drawTickMarks(Graphics g, int startX, int endX, int y) {
        int tickMarkNum = 3;

        for (double i = 1.0; i <= tickMarkNum; ++i) {
            int value = (int) (i*(maximum - minimum) / (tickMarkNum+1));
            String text = Util.format(value, 0);
            int textWidth = g.getFontMetrics().stringWidth(text);

            int xLocal = (int) (startX + i*(endX - startX)/(tickMarkNum+1) - textWidth*0.5);
            g.drawString(text, xLocal, y);
        }
    }

    // Converts from screen coordinates to a range value.
    protected double toLocalX(int x) {
        double xScale = (sliderWidth - 3) / (double) (getMaximum() - getMinimum());
        return (x / xScale) + getMinimum();
    }

    protected int toScreenX(double x) {
        double xScale = (sliderWidth - 3) / (double) (getMaximum() - getMinimum());
        int screenX = (int) ((x - getMinimum()) * xScale);
        return screenX;
    }

    protected int pickHandle(int x) {
        int valueX = toScreenX(getValue());

        int p = 0;

        if (Math.abs(x - valueX) < PICK_WIDTH)
            p = PICK_HANDLE;

        return p;
    }

    public void mousePressed(MouseEvent e) {
        if (enabled == false)
            return;

        pick = pickHandle(e.getX());
        mouseX = e.getX();
    }

    public void mouseDragged(MouseEvent e) {
        if (enabled == false)
            return;

        double x = toLocalX(e.getX());

        if (x < getMinimum())
            x = getMinimum();

        if (x > getMaximum())
            x = getMaximum();

        if (pick == PICK_HANDLE) {
            if (x >= getMinimum() && x <= getMaximum())
                setValue(x);
        }

        repaint();
    }

    public void mouseReleased(MouseEvent e) {

    }

    protected void setCurs(int c) {
        Cursor cursor = Cursor.getPredefinedCursor(c);

        if (getCursor() != cursor)
            setCursor(cursor);
    }

    public void mouseMoved(MouseEvent e) {
        if (enabled == false)
            return;

        switch (pickHandle(e.getX())) {
            case PICK_HANDLE:
                setCurs(Cursor.W_RESIZE_CURSOR);
                break;
            case PICK_NONE:
                setCurs(Cursor.MOVE_CURSOR);
                break;
        }
    }

    public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() == 2) {
            if (isMinBest)
                value = maximum;
            else
                value = minimum;
            repaint();
        }
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
        setCurs(Cursor.DEFAULT_CURSOR);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(totalWidth, totalHeight);
    }

    @Override
    public void setEnabled(boolean v) {
        enabled = v;
        repaint();
    }

    public void stateChanged(ChangeEvent e) {
        repaint();
    }

    public void setBGColor(Color bgColor) {
        this.bgColor = bgColor;
    }

}
