/*
 * Copyright (c) 2017 by Gerrit Grunwald
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.point85.tilesfx.skins;

import java.util.List;

import org.point85.tilesfx.Tile;
import org.point85.tilesfx.events.TileEvent;
import org.point85.tilesfx.events.TileEvent.EventType;
import org.point85.tilesfx.fonts.Fonts;
import org.point85.tilesfx.tools.CtxBounds;
import org.point85.tilesfx.tools.CtxCornerRadii;
import org.point85.tilesfx.tools.Helper;

import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.geometry.VPos;
import javafx.scene.CacheHint;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;


public class FlipTileSkin extends TileSkin {
    private final TileEvent       FLIP_FINISHED = new TileEvent(EventType.FLIP_FINISHED);
    private       List<String>    characters;
    private       int             currentSelectionIndex;
    private       int             nextSelectionIndex;
    private       double          flapHeight;
    private       Canvas          upperBackground;
    private       GraphicsContext upperBackgroundCtx;
    private       Canvas          upperBackgroundText;
    private       GraphicsContext upperBackgroundTextCtx;
    private       Canvas          lowerBackground;
    private       GraphicsContext lowerBackgroundCtx;
    private       Canvas          lowerBackgroundText;
    private       GraphicsContext lowerBackgroundTextCtx;
    private       Canvas          flap;
    private       GraphicsContext flapCtx;
    private       Canvas          flapTextFront;
    private       GraphicsContext flapTextFrontCtx;
    private       Canvas          flapTextBack;
    private       GraphicsContext flapTextBackCtx;
    private       Rotate          rotateFlap;
    private       Font            font;
    private       double          centerX;
    private       double          centerY;
    private       Timeline        timeline;


    // ******************** Constructors **************************************
    public FlipTileSkin(final Tile TILE) {
        super(TILE);
    }


    // ******************** Initialization ************************************
    @Override protected void initGraphics() {
        super.initGraphics();

        timeline              = new Timeline();
        characters            = tile.getCharacterList();
        currentSelectionIndex = 0;
        nextSelectionIndex    = 1;

        centerX    = PREFERRED_WIDTH * 0.5;
        centerY    = PREFERRED_HEIGHT * 0.5;

        pane.setBackground(null);
        pane.setBorder(null);

        rotateFlap = new Rotate();
        rotateFlap.setAxis(Rotate.X_AXIS);
        rotateFlap.setAngle(0);

        flapHeight = PREFERRED_HEIGHT * 0.495;

        upperBackground    = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT * 0.495);
        upperBackgroundCtx = upperBackground.getGraphicsContext2D();

        upperBackgroundText    = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT * 0.495);
        upperBackgroundTextCtx = upperBackgroundText.getGraphicsContext2D();
        upperBackgroundTextCtx.setTextBaseline(VPos.CENTER);
        upperBackgroundTextCtx.setTextAlign(TextAlignment.CENTER);

        lowerBackground    = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT * 0.495);
        lowerBackgroundCtx = lowerBackground.getGraphicsContext2D();

        lowerBackgroundText    = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT * 0.495);
        lowerBackgroundTextCtx = lowerBackgroundText.getGraphicsContext2D();
        lowerBackgroundTextCtx.setTextBaseline(VPos.CENTER);
        lowerBackgroundTextCtx.setTextAlign(TextAlignment.CENTER);

        flap = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT * 0.495);
        flap.getTransforms().add(rotateFlap);
        flapCtx = flap.getGraphicsContext2D();

        flapTextFront = new Canvas();
        flapTextFront.getTransforms().add(rotateFlap);
        flapTextFrontCtx = flapTextFront.getGraphicsContext2D();
        flapTextFrontCtx.setTextBaseline(VPos.CENTER);
        flapTextFrontCtx.setTextAlign(TextAlignment.CENTER);

        flapTextBack  = new Canvas();
        flapTextBack.getTransforms().add(rotateFlap);
        flapTextBack.setOpacity(0);
        flapTextBackCtx = flapTextBack.getGraphicsContext2D();
        flapTextBackCtx.setTextBaseline(VPos.CENTER);
        flapTextBackCtx.setTextAlign(TextAlignment.CENTER);

        pane.getChildren().addAll(upperBackground,
                                  lowerBackground,
                                  upperBackgroundText,
                                  lowerBackgroundText,
                                  flap,
                                  flapTextFront,
                                  flapTextBack);
    }

    @Override protected void registerListeners() {
        super.registerListeners();

        rotateFlap.angleProperty().addListener((o, ov, nv) -> {
            if (nv.doubleValue() > 90) {
                flapTextFront.setOpacity(0);
                flapTextBack.setOpacity(1);
            }
        });

        timeline.setOnFinished(EVENT -> {
            tile.fireTileEvent(FLIP_FINISHED);
            if (Double.compare(rotateFlap.getAngle(), 180) == 0) {
                rotateFlap.setAngle(0);
                flapTextBack.setOpacity(0);
                flapTextFront.setOpacity(1);
                drawText();
                if (!tile.getFlipText().equals(characters.get(currentSelectionIndex))) { flipForward(); }
            } else if(Double.compare(rotateFlap.getAngle(), 0) == 0) {
                rotateFlap.setAngle(180);
            }
        });
    }


    // ******************** Methods *******************************************
    @Override protected void handleEvents(final String EVENT_TYPE) {
        super.handleEvents(EVENT_TYPE);
        if (EVENT_TYPE.equals("FLIP_START")) {
            flipForward();
        }
    }

    private void flipForward() {
        timeline.stop();

        flap.setCache(true);
        flap.setCacheHint(CacheHint.ROTATE);
        //flap.setCacheHint(CacheHint.SPEED);

        currentSelectionIndex++;
        if (currentSelectionIndex >= characters.size()) {
            currentSelectionIndex = 0;
        }
        nextSelectionIndex = currentSelectionIndex + 1;
        if (nextSelectionIndex >= characters.size()) {
            nextSelectionIndex = 0;
        }
        KeyValue keyValueFlap = new KeyValue(rotateFlap.angleProperty(), 180, Interpolator.SPLINE(0.5, 0.4, 0.4, 1.0));
        //KeyValue keyValueFlap = new KeyValue(rotateFlap.angleProperty(), 180, Interpolator.EASE_IN);
        KeyFrame keyFrame     = new KeyFrame(Duration.millis(tile.getFlipTimeInMS()), keyValueFlap);
        timeline.getKeyFrames().setAll(keyFrame);
        timeline.play();
    }

    private void drawFlaps() {
        double cornerRadius = tile.getRoundedCorners() ? size * 0.025 : 0;

        // Upper Background
        upperBackground.setCache(false);
        upperBackgroundCtx.clearRect(0, 0, width, flapHeight);
        Helper.drawRoundedRect(upperBackgroundCtx, new CtxBounds(0, 0, width, flapHeight), new CtxCornerRadii(cornerRadius, cornerRadius, 0, 0));
        upperBackgroundCtx.setFill(tile.getBackgroundColor());
        upperBackgroundCtx.fill();
        upperBackground.setCache(true);
        upperBackground.setCacheHint(CacheHint.SPEED);

        // Lower Background
        lowerBackground.setCache(false);
        lowerBackgroundCtx.clearRect(0, 0, width, flapHeight);
        Helper.drawRoundedRect(lowerBackgroundCtx, new CtxBounds(0, 0, width, flapHeight), new CtxCornerRadii(0, 0, cornerRadius, cornerRadius));
        lowerBackgroundCtx.setFill(tile.getBackgroundColor());
        lowerBackgroundCtx.fill();
        lowerBackground.setCache(true);
        lowerBackground.setCacheHint(CacheHint.SPEED);

        // Flap
        flap.setCache(false);
        flapCtx.clearRect(0, 0, width, flapHeight);
        Helper.drawRoundedRect(flapCtx, new CtxBounds(0, 0, width, flapHeight), new CtxCornerRadii(cornerRadius, cornerRadius, 0, 0));
        flapCtx.setFill(tile.getBackgroundColor());
        flapCtx.fill();
        flap.setCache(true);
        flap.setCacheHint(CacheHint.SPEED);
    }

    private void drawText() {
        if (characters.isEmpty()) { return; }
        final String CURRENT_TEXT = characters.get(currentSelectionIndex);
        final String NEXT_TEXT    = characters.get(nextSelectionIndex);
        final Color  TEXT_COLOR   = tile.getForegroundColor();

        // set the text on the upper background
        upperBackgroundTextCtx.clearRect(0, 0, width, flapHeight);
        upperBackgroundTextCtx.setFill(TEXT_COLOR);
        upperBackgroundTextCtx.fillText(NEXT_TEXT, centerX, centerY, width);

        // set the text on the lower background
        lowerBackgroundTextCtx.clearRect(0, 0, width, flapHeight);
        lowerBackgroundTextCtx.setFill(TEXT_COLOR);
        lowerBackgroundTextCtx.fillText(CURRENT_TEXT, centerX, 0, width);

        // set the text on the flap front
        flapTextFrontCtx.clearRect(0, 0, width, flapHeight);
        flapTextFrontCtx.setFill(TEXT_COLOR);
        flapTextFrontCtx.fillText(CURRENT_TEXT, centerX, centerY, width);

        // set the text on the flap back
        flapTextBackCtx.clearRect(0, 0, width, flapHeight);
        flapTextBackCtx.save();
        flapTextBackCtx.scale(1,-1);
        flapTextBackCtx.setFill(TEXT_COLOR);
        flapTextBackCtx.fillText(NEXT_TEXT, centerX, -centerY, width);
        flapTextBackCtx.restore();
    }


    // ******************** Resizing ******************************************
    @Override protected void resize() {
        super.resize();
        centerX    = width * 0.5;
        centerY    = height * 0.5;
        flapHeight = height * 0.495;

        upperBackground.setWidth(width);
        upperBackground.setHeight(flapHeight);

        lowerBackground.setWidth(width);
        lowerBackground.setHeight(flapHeight);
        lowerBackground.setTranslateY(height - flapHeight);

        upperBackgroundText.setWidth(width);
        upperBackgroundText.setHeight(flapHeight);

        lowerBackgroundText.setWidth(width);
        lowerBackgroundText.setHeight(flapHeight);
        lowerBackgroundText.setTranslateY(height - flapHeight);

        flap.setWidth(width);
        flap.setHeight(flapHeight);
        rotateFlap.setPivotY(centerY);

        flapTextFront.setWidth(width);
        flapTextFront.setHeight(flapHeight);

        flapTextBack.setWidth(width);
        flapTextBack.setHeight(flapHeight);

        font = Fonts.latoBold(height * 0.75);

        upperBackgroundTextCtx.setFont(font);
        lowerBackgroundTextCtx.setFont(font);
        flapTextFrontCtx.setFont(font);
        flapTextBackCtx.setFont(font);

        redraw();
    }

    @Override protected void redraw() {
        drawFlaps();
        drawText();
    }
}