import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.control.Slider;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.text.Font;
import javafx.scene.transform.Affine;
import javafx.scene.image.Image;
import javafx.geometry.Pos;

/**
 * This program demonstrates Transforms.  Transforms controlled by sliders
 * are applied to a canvas in the order scale, shear, rotate, translate.
 * There is also a final translation that moves the origin to the center
 * of the canvas, so that the transformations specified by the sliders 
 * are applied with the origin in the center.  That is, the center of
 * the canvas is the center for scaling, shear, and rotation; those
 * transformations leave the center fixed.  This program requires an
 * image resource file named face-smile.png.
 */
public class TransformDemo extends Application {

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


    private Canvas canvas;  // The canvas where the figures are drawn.

    private Slider scaleXSlider;  // Controls scaling in horizontal direction.
    private Slider scaleYSlider;  // Controls scaling in vertical direction.
    private Slider shearSlider;  // Controls horizontal shear.
    private Slider rotateSlider;  // Controls angle of rotation.
    private Slider translateXSlider;  // Controls horizontal translation.
    private Slider translateYSlider;  // Controls vertical translation.
    
    private Button resetButton;  // Resets all sliders to default value.
                                 // Transformation is the identity.
    
    private LinearGradient gradient;  // A linear gradient paint.
    
    private Image smiley;  // A small smiley face (from Gnome desktop)
    
    
    /**
     * Set up GUI and event handling.
     */
    public void start(Stage stage) {

        smiley = new Image("face-smile.png");
        gradient = new LinearGradient(0,0,50,20,false,CycleMethod.REFLECT,
                new Stop(0,Color.BLACK), new Stop(1,Color.LIGHTGRAY));
        
        scaleXSlider = new Slider(-2,2,1);
        scaleXSlider.setBlockIncrement(0.1);
           // (Note:  The block increment is the amount by which the value
           //  will change, if the user hits a left/right arrow key while
           //  the slider has the input focus.)
        scaleYSlider = new Slider(-2,2,1);
        scaleYSlider.setBlockIncrement(0.1);
        shearSlider = new Slider(-1,1,0);
        shearSlider.setBlockIncrement(0.05);
        rotateSlider = new Slider(-180,180,0);
        translateXSlider = new Slider(-200,200,0);
        translateYSlider = new Slider(-150,150,0);
        
        resetButton = new Button("Reset Tranforms");
        resetButton.setOnAction( e -> {
            scaleXSlider.setValue(1);
            scaleYSlider.setValue(1);
            shearSlider.setValue(0);
            rotateSlider.setValue(0);
            translateXSlider.setValue(0);
            translateYSlider.setValue(0);
        });
        
        VBox bottom = new VBox(10,
                makeInput("X-Scale:    ", scaleXSlider),
                makeInput("Y-Scale:    ", scaleYSlider),
                makeInput("X-Shear:    ", shearSlider),
                makeInput("Rotate:     ", rotateSlider),
                makeInput("X-Translate:", translateXSlider),
                makeInput("Y-Translate:", translateYSlider),
                resetButton
            );
        bottom.setStyle("-fx-padding: 10px; -fx-border-color: #444; -fx-border-width: 4px 0 0 0");
        bottom.setAlignment(Pos.CENTER);
        
        canvas = new Canvas(800,600);
        drawCanvas();  // (must be done after making sliders)
        
        BorderPane root = new BorderPane(canvas);
        root.setStyle("-fx-border-color: #444; -fx-border-width: 4px");
        root.setBottom(bottom);

        stage.setScene(new Scene(root));
        stage.setResizable(false);
        stage.setTitle("Transform Demo");
        stage.show();
    }
    
    
    /**
     * Creates an HBox containing a slider and a label that displays the
     * value of that slider.  An event handler is added to the slider to
     * redraw the canvas when the slider value changes.
     */
    private HBox makeInput(String text, Slider slider) {
        Label label = new Label();
        label.setStyle("-fx-font: 14pt monospace");
        label.textProperty().bind(slider.valueProperty().asString(text + "%8.2f"));
        slider.setPrefWidth(300);
        slider.valueProperty().addListener( e -> drawCanvas() );
        HBox box = new HBox(10,slider,label);
        box.setAlignment(Pos.CENTER);
        return box;
    }

    
    /**
     *  Fill the canvas with white, then draw some stuff, transformed
     *  by the transforms specified by the sliders.
     */
    private void drawCanvas() {
        GraphicsContext g = canvas.getGraphicsContext2D();
        g.save();  // Save the current state, before applying transforms.
        g.setFill(Color.WHITE);
        g.fillRect(0,0,canvas.getWidth(),canvas.getHeight());
        
        g.translate( canvas.getWidth()/2, canvas.getHeight()/2 );  // move origin to center
        
        /*  Apply transforms specified by the sliders, with the origin as the center
         *  point for scaling, rotation and shear. */
        
        g.translate( translateXSlider.getValue(), translateYSlider.getValue() );
        g.rotate( rotateSlider.getValue() );
        g.transform( new Affine(1, shearSlider.getValue(), 0, 0, 1, 0) ); // a shear transform
        g.scale( scaleXSlider.getValue(), scaleYSlider.getValue());
        
        /* Draw some objects, subject to the transforms. */
        
        g.setFont(Font.font(48));
        g.setFill(Color.BLACK);
        g.fillText("Hello World",0,0);
        g.setLineWidth(5);
        g.setStroke(Color.RED);
        g.strokeRect(-200,-150,150,100);
        g.setFill(gradient);
        g.fillOval(-200,50,150,100);
        g.setLineWidth(2);
        g.setStroke(Color.BLACK);
        g.strokeOval(-200,50,150,100);
        g.setFill(Color.GREEN);
        g.fillRoundRect(20, 20, 200, 100, 20, 20);
        g.setFill(Color.BLUE);
        g.fillRect(50,-175,50,100);
        g.drawImage(smiley,-100,-20);
        g.restore();  // Restore the previous, untransformed state.
    }

    

} // end class TransformDemo

