package photoflockr;

import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.animation.Interpolator;
import javafx.scene.CustomNode;
import javafx.scene.Node;
import javafx.scene.Group;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.transform.Transform;

import java.lang.Math;

/**
 * This class is used to display a picture when it moves,
 * when it is selected or expanded to full screen.
 */
public class Sprite extends CustomNode {
    override var blocksMouse = true;
    override var hover on replace {
        if (hover and not fullScreen) {
            onEnter();
        } else {
            onLeave();
        }
    }

    public var boid: Boid;
    public var screenHeight: Number;
    public var screenWidth: Number;
    public var fullScreen: Boolean;
    public var clickAction: function(tag: String): Void;
    public var heading: Number;
    
    var largeImageUrl: String = bind boid.largeImageUrl;
    var imageUrl: String = bind boid.imageUrl on replace {
        if (imageUrl != null and not imageUrl.equals(image.url)) {
            image = Image {
                placeholder: image
                url: imageUrl
                backgroundLoading: true
            }
        }
    }
    var image: Image;
    var largeImage: Image;
    var screenAlpha: Number = 1.0;
    var alpha: Number = 1.0;
    var textAlpha: Number = 0.0;
    var spriteHeading: Number;
    var spriteX: Number = bind if (not selected) boidx else x;
    var spriteY: Number =  bind if (not selected) boidy else y;
    var boidx: Number = bind boid.loc.x;
    var boidy: Number = bind boid.loc.y;
    var x: Number;
    var y: Number;
    var boidHeading: Number;
    var selected: Boolean = false;
    var radius: Number = bind boid.radius;
    var largeImageFade: Number = 0.0;
    var tagList = TagList {
        translateX: bind radius * 2 * alpha
        height: 150
        width: 150
        textOpacity: bind textAlpha
        clickAction: function(s: String): Void {
            this.doClick(s);
        }
    }
    var boidSelected: Boolean = bind boid.selected on replace {
        if (not boid.selected and fullScreen) {
            fullScreenTimeline.stop();
            fadeTimeline.stop();
            largeImage = null;
            largeImageFade = 0;
            x = boidx;
            y = boidy;
            nonFullScreenTimeline.play();

        } else if (boid.selected and sizeof boid.tags > 0) {
            tagList.tags = boid.tags[0..30]; // update tags
        }
    }

    // Start the picture-fading effect when a large image loading is completed.
    var largeImageProgress = bind largeImage.progress on replace {
        if (largeImage != null and largeImageProgress == 100) {
            fadeTimeline.play();
        }
    }

    var fadeTimeline = Timeline {
        keyFrames:
            [ at (0s) { largeImageFade => 0.0 },
              at (1s) { largeImageFade => 1.0 } ]
    }

    // When the picture is selected it should be smoothly double-sized
    // and rotated (aligned along the X and Y axis).
    var scaleTimeline = Timeline {
        keyFrames:
            [KeyFrame {
                time: 0s
                values:
                    [alpha => 1.0,
                    spriteHeading => boidHeading,
                    textAlpha => 0.0]
            },
            KeyFrame {
                time: 0.5s
                values:
                    [alpha => 2.0 tween Interpolator.EASEOUT,
                    spriteHeading => 0.0 tween Interpolator.EASEOUT,
                    textAlpha => 0.0]
                action: function() {
                    // hack
                    spriteHeading = 0.0;
                    alpha = 2.0;
                }
            },
            KeyFrame {
                time: 1s
                values: textAlpha => 1.0 tween Interpolator.EASEOUT
            }]
    }

    // When the picture is deselected it should smoothly return to "flocking"
    // mode.
    var unscaleTimeline = Timeline {
        keyFrames:
            [KeyFrame {
                time: 0s
                values:
                    [alpha => 2.0,
                    spriteHeading => 0.0];
            },
            KeyFrame {
                time: 0.5s
                values:
                    [alpha => 1.0 tween Interpolator.EASEOUT,
                    spriteHeading => boidHeading tween Interpolator.EASEOUT];
                action: function() {
                    fullScreen = false;
                    selected = false;
                    textAlpha = 0;
                    alpha = 1.0;
                    spriteHeading = boidHeading;
                    boid.returnToFlocking();
                }
            }]
    }

    // Starts when the picture is clicked.
    var fullScreenTimeline = Timeline {
        keyFrames:
            [KeyFrame {
                time: 0s
                values:
                    [alpha => 2.0,
                    x => boidx,
                    y => boidy]
            },
            KeyFrame {
                time: 0.5s
                values:
                    [alpha => screenAlpha tween Interpolator.EASEOUT,
                    x => 0.0 tween Interpolator.EASEOUT,
                    y => 0.0 tween Interpolator.EASEOUT]
            }]
    }

    // Return to "flocking" mode.
    var nonFullScreenTimeline = Timeline {
        keyFrames:
            [KeyFrame {
                time: 0s
                values:
                    [alpha => screenAlpha,
                    spriteHeading => 0.0,
                    x => 0.0,
                    y => 0.0]
            },
            KeyFrame {
                time: 0.5s
                values:
                    [alpha => 1.0 tween Interpolator.EASEOUT,
                    x => boidx tween Interpolator.EASEOUT,
                    y => boidy tween Interpolator.EASEOUT,
                    spriteHeading => boidHeading tween Interpolator.EASEOUT]
                action: function() {
                    fullScreen = false;
                    selected = false;
                    textAlpha = 0;
                    boid.returnToFlocking();
                }
           }]
    }

    function doClick(s: String): Void {
        clickAction(s);
        onLeave();
    }

    public function doSelect(): Void {
        if (not fullScreen and alpha == 2.0) {
            nonFullScreenTimeline.stop();
            var sx = screenWidth / (radius * 2);
            var sy = screenHeight / (radius * 2);
            fullScreen = true;
            screenAlpha = Math.max(sx, sy);
            fullScreenTimeline.play();
            if (largeImageUrl != null) {
                fadeTimeline.stop();
                largeImage = Image {
                    url: largeImageUrl
                    backgroundLoading: true
                }
                largeImageFade = 0.0;
            }
        } else if (fullScreen) {
            fadeTimeline.stop();
            largeImage = null;
            largeImageFade = 0;
            fullScreenTimeline.stop();
            x = boidx;
            y = boidy;
            nonFullScreenTimeline.play();
        }
    }

    public function onEnter(): Void {
        unscaleTimeline.stop();
        boidHeading = heading;
        spriteHeading = heading;
        x = spriteX;
        y = spriteY;
        textAlpha = 0;
        selected = true;
        boid.selectYourself();
        scaleTimeline.play();
    }

    public function onLeave(): Void {
        if (not fullScreen) {
            scaleTimeline.stop();
            textAlpha = 0;
            unscaleTimeline.play();
        }
    }

    override public function create(): Node {
        Group {
            transforms: bind Transform.translate(spriteX, spriteY)
            content:
                [Group {
                    transforms: bind [ Transform.scale(alpha, alpha),
                                       Transform.rotate(if (not selected) heading
                                                        else spriteHeading, radius, radius) ]
                    content: [
                        //
                        // The picture's background black rectangle.
                        //
                        Rectangle {
                            height: radius*2
                            width: radius*2
                            fill: Color.BLACK
                            smooth: false
                        },
                        //
                        // The small image of the picture.
                        //
                        ImageView {
                            opacity: bind 1.0 - largeImageFade
                            image: bind image
                            smooth: true
                            onMouseClicked: function(e) {
                                this.doSelect();
                            }
                        },
                        //
                        // The picture's gray frame.
                        //
                        Rectangle {
                            transforms: bind Transform.scale(1.0/alpha, 1.0/alpha);
                            stroke: Color.GRAY
                            height: bind radius*2*alpha
                            width: bind radius*2*alpha
                            fill: Color.color(0, 0, 0, 0);
                            strokeWidth: 1
                       }
                    ]
                },
                //
                // The large image of the picture.
                //
                Group {
                    opacity: bind largeImageFade
                    visible: bind largeImage != null
                    content:
                        [Rectangle {
                            height: screenHeight
                            width: screenWidth
                            fill: Color.BLACK
                            visible: bind largeImageFade == 1.0
                        },
                        ImageView {
                            image: bind largeImage
                            smooth: true
                        }]
                },
                //
                // The tag list.
                //
                Group {
                    visible: bind alpha == 2.0
                    content: bind tagList
                }]
        }
    }
}