import javafx.animation.FadeTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;
import javafx.util.Duration;

import static javafx.scene.paint.Color.*;

/**
 * Displays a triangle with a linear gradient and a fade transition. The corners are
 * selected by the user through mouse clicks. The triangle can be dragged with the mouse.
 *
 * @author Drue Coles
 */
public class GlowingTriangle extends Application {

    private final Polygon triangle = new Polygon();

    // Coordinates of the mouse cursor during drag events.
    private double cursorX;
    private double cursorY;

    @Override
    public void start(Stage primaryStage) {

        // Create an empty black scene and add it to the stage.
        Pane root = new Pane();
        final int size = 500;
        Scene scene = new Scene(root, size, size, PALEGREEN);
        primaryStage.setTitle("Glowing Triangle");
        primaryStage.setScene(scene);
        primaryStage.show();

        // Add a drop shadow
        DropShadow dropShadow = new DropShadow();
        dropShadow.setOffsetY(5.0);
        dropShadow.setOffsetX(5.0);
        dropShadow.setColor(GRAY);
        triangle.setEffect(dropShadow);

        class MouseClickHandler implements EventHandler<MouseEvent> {

            public void handle(MouseEvent event) {

                // Add point to polygon at location of the mouse click.
                double x = event.getX(); 
                double y = event.getY(); 
                triangle.getPoints().addAll(x, y);

                // If fewer than three points have been added, draw a dot at the location
                // of the mouse click. Otherwise, remove the two dots, remove this event
                // handler, and add the triangle to the scene.
                if (triangle.getPoints().size() < 6) {
                    Circle dot = new Circle(x, y, 4);
                    dot.setFill(BLACK);
                    root.getChildren().add(dot);
                } else {
                    root.getChildren().clear();
                    triangle.setFill(getLinearGradient(ORANGE, MAROON));
                    getFadeTransition().play();
                    root.setOnMouseClicked(null);
                    root.getChildren().add(triangle);
                }
            }
        }
        MouseClickHandler mouseHandler = new MouseClickHandler();
        root.setOnMouseClicked(mouseHandler);

        class MouseDragHandler implements EventHandler<MouseEvent> {

            @Override
            public void handle(MouseEvent event) {
                triangle.setTranslateX(event.getSceneX() - cursorX);
                triangle.setTranslateY(event.getSceneY() - cursorY);
            }
        }
        triangle.setOnMouseDragged(new MouseDragHandler());

        class MousePressedHandler implements EventHandler<MouseEvent> {

            @Override
            public void handle(MouseEvent event) {
                triangle.setCursor(Cursor.MOVE);
                cursorX = event.getSceneX() - cursorX;
                cursorY = event.getSceneY() - cursorY;
            }
        }
        triangle.setOnMousePressed(new MousePressedHandler());

        class MouseReleasedHandler implements EventHandler<MouseEvent> {

            @Override
            public void handle(MouseEvent event) {
                triangle.setCursor(Cursor.DEFAULT);
                cursorX = event.getSceneX() - cursorX;
                cursorY = event.getSceneY() - cursorY;
            }
        }
        triangle.setOnMouseReleased(new MouseReleasedHandler());
    }

    /**
     * Returns a fade transition configured for the triangle.
     */
    private FadeTransition getFadeTransition() {
        FadeTransition ft = new FadeTransition(Duration.millis(1250), triangle);
        ft.setFromValue(1.0);
        ft.setToValue(0.5);
        ft.setCycleCount(Timeline.INDEFINITE);
        ft.setAutoReverse(true);
        return ft;
    }

    /**
     * Returns a linear gradient starting and ending with given colors.
     */
    private LinearGradient getLinearGradient(Color startColor, Color endColor) {
        Stop[] stops = new Stop[2];
        stops[0] = new Stop(0, startColor);
        stops[1] = new Stop(1, endColor);

        // (0, 0) and (1, 1) are scaled coordinates with respect to the 
        // bounding box of the shape to which this gradient is applied. 
        return new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE, stops);
    }

    public static void main(String[] args) {
        launch(args);
    }
}