package de.duehl.swing.ui.elements.arrowselector;

/*
 * Copyright 2016 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;

import javax.swing.JPanel;
import javax.swing.JRadioButton;

import static de.duehl.swing.ui.elements.arrowselector.ArrowSelector.MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE;

/**
 * Diese Klasse stellt einen JRadioButton dar, der sich als Pfeildarstellung
 * präsentiert.
 *
 * Siehe http://forum.byte-welt.net/threads/12503-Eigene-Checkbox-erstellen
 * den Post von Timothy_Truckle.
 *
 * @version 1.01     2014-07-24
 * @author Christian Dühl
 */

public class ArrowButton extends JRadioButton {

    private static final long serialVersionUID = -8818232289745150208L;

    /** Unterteilung der Höhe für das Rechteck und die Dreiecke. */
    private static final int HEIGHT_DIVISOR = 4;

    /** Unterteilung der Breite für das Rechteck. */
    private static final int WIDTH_DIVISOR = 4;

    /** Abstand der Pfeildarstellung vom Rand in X-Richtung. */
    private static final int ARROW_DISTANCE = 2;

    /** Das Panel, in dem dieser ArrowButton platziert wird. */
    private final JPanel surroundingPanel;

    /**
     * Platz oder Position des ArrowButtons. 0 bezeichnet die Mitte, negative
     * Werte Plätze links der Mitte, positive Werte rechts der Mitte.
     */
    private final int place;

    /** Farbe für selektierte Pfeile. */
    private final Color selectedColor;

    /** Farbe für nicht selektierte Pfeile. */
    private final Color notSelectedColor;

    /**
     * Konstruktor.
     *
     * @param surroundingPanel
     *            Das Panel, in dem dieser ArrowButton platziert wird.
     * @param place
     *            Platz oder Position des ArrowButtons. 0 bezeichnet die Mitte,
     *            negative Werte Plätze links der Mitte, positive Werte rechts
     *            der Mitte.
     * @param selectedColor
     *            Farbe für ausgewählte Pfeile.
     * @param notSelectedColor
     *            Farbe für nicht ausgewählte Pfeile.
     */
    public ArrowButton(final JPanel surroundingPanel, final int place,
            final Color selectedColor, final Color notSelectedColor) {
        super();
        this.surroundingPanel = surroundingPanel;
        this.place = place;
        this.selectedColor = selectedColor;
        this.notSelectedColor = notSelectedColor;
    }

    /**
     * Stellt den ArrowButton dar, falls das Objekt sichtbar ist.
     *
     * @param graphics
     *            Grafikobjekt.
     */
    @Override
    public void paint(final Graphics graphics) {
        if (isShowing()) {
            paintTheArrow((Graphics2D) graphics);
        }
    }

    /**
     * Stellt den ArrowButton dar.
     *
     * @param graphics2d
     *            Grafikobjekt.
     */
    private void paintTheArrow(final Graphics2D graphics2d) {
        Dimension size = getSize();
        graphics2d.setBackground(surroundingPanel.getBackground());
        graphics2d.clearRect(0, 0, size.width, size.height);

        Shape shape = null;
        if (0 == place) {
            shape = createRectangle(size);
        }
        else {
            shape = createArrow(size);
        }
        graphics2d.setColor(isSelected()
                ? selectedColor
                : notSelectedColor);
        graphics2d.fill(shape);
    }

    /**
     * Erzeugt das Rechteck. Die Größe wird übergeben und nicht nochmal
     * abgefragt, damit während einer Größenveränderung nicht bei mehrfachen
     * Abfragen der Werte unterschiedliche Größen verwendet werden.
     *
     * @param size
     *            Größe des Buttons.
     * @return Erzeugtes Rechteck.
     */
    private Rectangle createRectangle(final Dimension size) {
        int x = size.width / WIDTH_DIVISOR;
        int y = size.height / HEIGHT_DIVISOR;
        int width = size.width / 2;
        int height = size.height / 2;
        return new Rectangle(x, y, width, height);
    }

    /**
     * Erzeugt einen Pfeil. Die Größe wird übergeben und nicht nochmal
     * abgefragt, damit während einer Größenveränderung nicht bei mehrfachen
     * Abfragen der Werte unterschiedliche Größen verwendet werden.
     *
     * Links zeigt die Spitze nach links, rechts zeigt sie nach rechts.
     *
     * @param size
     *            Größe des Buttons.
     * @return Erzeugter Pfeil.
     */
    private Polygon createArrow(final Dimension size) {
        /* Minimale Höhe, entspricht der des Rechtecks (1/4): */
        int minHeight = size.height / HEIGHT_DIVISOR;

        /* Maximale Höhe (3/4): */
        int maxHeight = minHeight * (HEIGHT_DIVISOR - 1);

        /* Höhendifferenz oben (oder unten, also 1/4): */
        int totalHeightDifference = (maxHeight - minHeight) / 2;

        /* Höhendifferenz für einen Schritt: */
        int heightDifferenceStep = totalHeightDifference /
                MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE;

        /* Höhendifferenz für den Platz: */
        int heightDifference = (MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE -
                Math.abs(place)) * heightDifferenceStep;

        /* Benötigte x-Koordinaten: */
        int leftX = ARROW_DISTANCE;
        int rightX = size.width - ARROW_DISTANCE;

        /* Benötigte y-Koordinaten: */
        int upperY = heightDifference;
        int middleY = size.height / 2;
        int lowerY = size.height - heightDifference;

        /* x-Koordinatentausch für die linken Dreiecke: */
        if (place < 0) {
            int temp = rightX;
            rightX = leftX;
            leftX = temp;
        }

        /* Dreieck erzeugen: */
        Polygon polygon = new Polygon();
        polygon.addPoint(leftX, upperY);
        polygon.addPoint(rightX, middleY);
        polygon.addPoint(leftX, lowerY);

        return polygon;
    }

}