/*
 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 */
package pacman;


import java.util.ArrayList;
import java.util.List;
import javafx.animation.Animation;
import javafx.animation.AnimationTimer;
import javafx.animation.PauseTransition;
import javafx.animation.PauseTransitionBuilder;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.media.AudioClip;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Scale;
import javafx.stage.Stage;
import javafx.util.Duration;


/**
 *
 * @author akouznet
 */
public class Pacman extends Application {
    public static final int CELL_WIDTH = 96; //50; //96;
    public static final int MIKHALYCH_WIDTH = CELL_WIDTH / 2;
    public static final int COIN_WIDTH = CELL_WIDTH / 2;
    public static final int FIELD_WIDTH = 16 * CELL_WIDTH;
    public static final int FIELD_HEIGHT = 12 * CELL_WIDTH;
    public static final int SLAVIK_NUMBER = 4; 
    public static final double SPEED = 2; // 2 cells per second
    public static final Duration FRIGHTENED_TIME = Duration.seconds(15);
    
    private static Pacman game;
    
    public static Pacman getGame() {
        return game;
    }
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
    
    private static AudioClip epicWin;
    private Pane gamePane;
    private Temida field;
    private List<Animation> animations = new ArrayList<Animation>();
    private Mikhalych mikhalych;
    private Enemy[] enemies;
    private PauseTransition palevoTimer;
    private boolean paused = true;
    private long startTime;
    private Scale scale;
    private Label labelBalance;
    private Label labelTime;
    private Label labelPalevo;
    private AnimationTimer animationTimer;
    private StackPane sp;
    private static ImageView fieldImageView;
    private static MediaPlayer mpStart;
    private InvalidationListener xListener;
    private InvalidationListener yListener;
    
    public void addAnimation(Animation animation) {
        animations.add(animation);
    }
    
    public void removeAnimation(Animation animation) {
        animations.remove(animation);
    }
            
    public void removeCoin(Coin coin) {
        gamePane.getChildren().remove(coin);
    }
    
    @Override
    public void start(Stage w) {
        
        game = this;
        
        epicWin = new AudioClip(
            Temida.class.getResource("sounds/epicwin.wav").toExternalForm());
        
        fieldImageView = new ImageView(
                Pacman.class.getResource("images/coin.jpg").toExternalForm());
        
        field = new Temida(new Level.Level1());
        fieldImageView.setViewport(new Rectangle2D(0, 0, field.getWidth() * CELL_WIDTH, field.getHeight() * CELL_WIDTH));
        
        BorderPane scoresPane = new BorderPane();
        scoresPane.setStyle("-fx-background-color: rgb(0, 0, 0, 0.5);");
        
        labelBalance = new Label("Баланс = 0 лв");
        labelBalance.setStyle("-fx-font-size: 50; -fx-text-fill: white;");
        scoresPane.setLeft(labelBalance);
        
        labelTime = new Label("0:00");
        labelTime.setStyle("-fx-font-size: 50; -fx-text-fill: white;");
        scoresPane.setRight(labelTime);
        
        labelPalevo = new Label("");
        labelPalevo.setStyle("-fx-font-size: 50; -fx-text-fill: red;");
        scoresPane.setCenter(labelPalevo);

        scale = new Scale();
        
        gamePane = new Pane();
        
        gamePane.getTransforms().setAll(scale);
        
        recreateField();
        
        createMikhalych();
        
        enemies = new Enemy[SLAVIK_NUMBER];
        createEnemies();
        
        gamePane.getChildren().addAll(mikhalych);
        
        BorderPane bp = new BorderPane();
        bp.setCenter(gamePane);
        bp.setBottom(scoresPane);
        
        sp = new StackPane();
        sp.getChildren().setAll(bp);
        
        final Scene scene = new Scene(sp, 800, 600);
        scene.setFill(Color.BLACK);
        
        animationTimer = new AnimationTimer() {

            @Override
            public void handle(long arg0) {
                long endTime = System.currentTimeMillis();
                long delta = (endTime - startTime) / 1000;
                labelTime.setText(String.format("%02d:%02d", delta / 60, delta % 60));
                
                if (palevoTimer != null 
                        && palevoTimer.getStatus() == Animation.Status.RUNNING) {
                    labelPalevo.setText(String.format("%02d", 
                            (int) Math.round(palevoTimer.getTotalDuration()
                            .subtract(palevoTimer.getCurrentTime()).toSeconds())));
                } else {
                    labelPalevo.setText("");
                }
                
                for (int i = 0; i < enemies.length; i++) {
                    double dist = Math.hypot(
                            mikhalych.getTranslateX() - enemies[i].getTranslateX(), 
                            mikhalych.getTranslateY() - enemies[i].getTranslateY());
                    if (dist < CELL_WIDTH) {
                        switch(enemies[i].getState()) {
                            case NORMAL:
                                mikhalych.die(enemies[i]);
                                break;
                            case FRIGHTENED:
                                mikhalych.catchEnemy(enemies[i]);
                                break;
                        } 
                    }
                }
            }
        };
        animationTimer.start();
        
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {

            @Override
            public void handle(KeyEvent ke) {
                switch(ke.getCode()) {
                    case RIGHT:
                        mikhalych.moveRight();
                        break;
                    case LEFT:
                        mikhalych.moveLeft();
                        break;
                    case UP:
                        mikhalych.moveUp();
                        break;
                    case DOWN:
                        mikhalych.moveDown();
                        break;
//                    case P:
//                        pause();
//                        break;
                    case DIGIT1:
                        gamePane.setScaleX(1);
                        gamePane.setScaleY(1);
                        yListener.invalidated(null);
                        xListener.invalidated(null);
                        break;
                    case DIGIT2:
                        gamePane.setScaleX(0.5);
                        gamePane.setScaleY(0.5);
                        yListener.invalidated(null);
                        xListener.invalidated(null);
                        break;
                }
            }
        });
        
        w.setScene(scene);
        w.setTitle("Pacman");
        w.show();
        
        mpStart = new MediaPlayer(new Media(
                Pacman.class.getResource("sounds/start.mp3").toExternalForm()));
        mpStart.setAutoPlay(true);
        startGame();
    }

    private void createMikhalych() {
        mikhalych = new Mikhalych();
        xListener = new InvalidationListener() {

            @Override
            public void invalidated(Observable arg0) {
                int fieldWidth = getField().getWidth() * CELL_WIDTH;
                if (gamePane.getScaleX() != 1) {
                    gamePane.setTranslateX((gamePane.getWidth()
                            - fieldWidth) / 2 * gamePane.getScaleX());
                } else {
                    double x = (gamePane.getWidth() - MIKHALYCH_WIDTH) / 2 - mikhalych.
                            getTranslateX();
                    if (x > 0) {
                        x = 0;
                    }
                    if (x < gamePane.getWidth() - fieldWidth) {
                        x = gamePane.getWidth() - fieldWidth;
                    }
                    gamePane.setTranslateX(x);
                }
            }
        };
        gamePane.widthProperty().addListener(xListener);
        mikhalych.translateXProperty().addListener(xListener);
        yListener = new InvalidationListener() {

            @Override
            public void invalidated(Observable arg0) {
                int fieldHeight = getField().getHeight() * CELL_WIDTH;
                if (gamePane.getScaleY() != 1) {
                    gamePane.setTranslateY(0);
                    // (scene.getHeight() 
                    gamePane.setTranslateY((gamePane.getHeight() 
                            - fieldHeight) / 2 * gamePane.getScaleY());
                } else {
                    double y = (gamePane.getHeight() - MIKHALYCH_WIDTH) / 2 - mikhalych.
                            getTranslateY();
                    if (y > 0) {
                        y = 0;
                    }
                    if (y < gamePane.getHeight() - fieldHeight) {
                        y = gamePane.getHeight() - fieldHeight;
                    }
                    gamePane.setTranslateY(y);
                }
            }
        };
        gamePane.heightProperty().addListener(yListener);
        mikhalych.translateYProperty().addListener(yListener);
        xListener.invalidated(null);
        yListener.invalidated(null);
    }

    private void startGame() {
        // TODO: Find a problem here
//        mp.setOnEndOfMedia(new Runnable() {
//
//            @Override
//            public void run() {
//                startGame();
//            }
//
//            public void startGame() {
//                System.out.println("startGame");
                paused = false;
                for (Enemy slavik : enemies) {
                    slavik.chooseRandomDirection();
                }
                startTime = System.currentTimeMillis();
//            }
//        });
    }

    private void createEnemies() {
        for (int i = 0; i < enemies.length; i++) {
            switch (i) {
                case 0:
                    enemies[i] = new Turmets();
                    break;
                case 1:
                    //enemies[i] = new Turmets();
                    enemies[i] = new Slavik();
                    break;
                case 2:
                    //enemies[i] = new Turmets();
                    enemies[i] = new Anton();
                    break;
                case 3:
//                    enemies[i] = new Anton();
                    enemies[i] = new Vitaly();
                    break;
            }
            enemies[i].setPosition(field.getLevel().slavik_positions[i][0], field.getLevel().slavik_positions[i][1]);
            enemies[i].stop();
            gamePane.getChildren().addAll(enemies[i]);
        }
    }

    private void recreateField() {
        gamePane.getChildren().addAll(fieldImageView);
        for (int x = 0; x < field.getWidth(); x++) {
            for (int y = 0; y < field.getHeight(); y++) {
                if (!field.isFree(x, y)) {
                    Rectangle rect = new Rectangle();
                    rect.setWidth(CELL_WIDTH);
                    rect.setHeight(CELL_WIDTH);
                    rect.setX(x * CELL_WIDTH);
                    rect.setY(y * CELL_WIDTH);
                    rect.setFill(Color.rgb(0, 0, 0, 0.5));
//                    rect.setFill(Color.rgb(255, 255, 0, 1));
                    gamePane.getChildren().addAll(rect);
                } else {
                    if (x != 0 || y != 0) {
                        Coin coin = new Coin();
                        coin.setTranslateX(x * CELL_WIDTH);
                        coin.setTranslateY(y * CELL_WIDTH);
                        gamePane.getChildren().add(coin);
                        field.addCoin(x, y, coin);
                    }
                }
            }
        }
    }

    private void pause() {
        if (!paused) {
            paused = true;
            for (Animation animation : animations) {
                animation.pause();
            }
        } else {
            paused = false;
            for (Animation animation : animations) {
                animation.play();
            }
        }
    }  
    
    public void updateBalance(int balance) {
        labelBalance.setText("Баланс = " + balance + " лв");
    }
    
    public boolean isPaused() {
        return paused;
    }
    
    public Temida getField() {
        return field;
    }

    public Mikhalych getMikhalych() {
        return mikhalych;
    }

    public void epicWin() {
        epicWin.play();
        long endTime = System.currentTimeMillis();
        long delta = (endTime - startTime) / 1000;
        System.out.println("time in seconds = " + delta);
        endOfTheGame("победили");
    }
    
    public void endOfTheGame(String text) {
        pause();
        animationTimer.stop();
        
        Label label = new Label("Вы " + text + "!");
        label.setStyle("-fx-font-size: 50; -fx-text-fill: white;");
        
        Button button = new Button("Играть еще раз");
        button.setStyle("-fx-font-size: 30;");
        final VBox vBox = new VBox(30);
        vBox.setStyle("-fx-background-color: rgb(0, 0, 0, 0.5);");
        vBox.setMaxSize(450, 200);
        vBox.getChildren().setAll(label, button);
        vBox.setAlignment(Pos.CENTER);
        
        button.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent arg0) {
                sp.getChildren().remove(vBox);
                restart();
            }
        });
        
        sp.getChildren().add(vBox);
    }

    public void gameOver() {
        endOfTheGame("банкрот");
    }

    public void dropPalevo(Palevo palevo, int cellX, int cellY) {
        palevo.setTranslateX(cellX * CELL_WIDTH);
        palevo.setTranslateY(cellY * CELL_WIDTH);
        getField().addPalevo(palevo);
        gamePane.getChildren().add(palevo);
    }

    void removePalevo(Palevo palevo) {
        gamePane.getChildren().remove(palevo);
    }
    
    void frightenEnemies() {
        for (Enemy enemy : enemies) {
            enemy.becomeFrightened();
        }
        if (palevoTimer != null) {
            palevoTimer.stop();
            removeAnimation(palevoTimer);
        }
        palevoTimer = PauseTransitionBuilder.create()
                .duration(FRIGHTENED_TIME)
                .onFinished(new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent t) {
                        mikhalych.calm();
                        removeAnimation(palevoTimer);
                        palevoTimer = null;
                    }
                })
                .build();
        addAnimation(palevoTimer);
        palevoTimer.play();
    }
    
    void calmEnemies() {
        for (Enemy enemy : enemies) {
            enemy.becomeNormal();
        }
    }
    
    public void restart() {
        // Clear and stop
        if (palevoTimer != null) {
            palevoTimer.stop();
        }
        
        for (Animation animation : animations) {
            animation.stop();
        }
        animations.clear();
        updateBalance(0);
        gamePane.getChildren().clear();
        
        // Recreate
        field = new Temida(new Level.Level1());
        recreateField();
        createMikhalych();
        createEnemies();
        Enemy.resetPalevo();
        gamePane.getChildren().addAll(mikhalych);
        animationTimer.start();
        System.out.println("error = " + mpStart.getError());
        System.out.println("status = " + mpStart.getStatus());
        System.out.println("getStartTime = " + mpStart.getStartTime());
        mpStart.seek(Duration.ZERO);
        mpStart.play();
        startGame();
    }
}
