/home/caleb/ASDV-Java/Semester 3/Assignments/MP2-chapter4_CalebFontenot/src/MP2_chapter4_CalebFontenot/BouncingBall.java
/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package MP2_chapter4_CalebFontenot;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;

/**
 *
 * @author caleb
 */
public class BouncingBall extends Pane {

    private List<MediaPlayer> mediaPlayers = new ArrayList<>();
    GridPane textPane = new GridPane();
    private String currentlyPlaying;
    private final int COLLISION_COOLDOWN = 20;
    private long timeSinceLastCollisionEvent = 0;
    final double RACKET_LENGTH = 100;
    final double radius = 20;
    private double x = radius, y = radius;
    private double dx = 1, dy = 1;
    private Circle circle = new Circle(x, y, radius);
    private Rectangle racket = new Rectangle(RACKET_LENGTH, 20);
    private Label infoLabel = new Label("Your mouse cursor must be inside the bounds of the window to play!");
    private Label racketTime = new Label();
    private Label ballCords = new Label();
    private Label playerScoreLabel = new Label("Player Score: 0");
    private Label computerScoreLabel = new Label("Computer Score: 0");
    private Label nowPlaying = new Label();

    private Timeline animation;

    private int computerScore, playerScore = 0;

    public BouncingBall() {
        circle.setFill(Color.RED); // Set ball color
        racket.setFill(Color.BLUE);
        textPane.add(racketTime, 0, 0);
        textPane.add(ballCords, 0, 1);
        textPane.add(playerScoreLabel, 0, 2);
        textPane.add(computerScoreLabel, 0, 3);
        textPane.add(nowPlaying, 0, 4);
        getChildren().addAll(circle, racket, textPane); // Place a ball into this pane
        racket.relocate(0, 580);
        playSound("bgm");
        // Create an animation for moving the ball
        animation = new Timeline(new KeyFrame(Duration.millis(1), new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {

                timeSinceLastCollisionEvent++;
                racketTime.setText("Frames since last collision: " + timeSinceLastCollisionEvent);
                moveBall();
                // Process collisions
                if (y >= (getHeight() - 20) && (timeSinceLastCollisionEvent > COLLISION_COOLDOWN)) {
                    timeSinceLastCollisionEvent = 0;
                    playSound("collision");
                    incrementComputerScore();
                }
                if ((x <= 20 && (timeSinceLastCollisionEvent > COLLISION_COOLDOWN)) || ((x >= getWidth() - 20) && (timeSinceLastCollisionEvent > COLLISION_COOLDOWN))) {
                    timeSinceLastCollisionEvent = 0;
                    playSound("collision");
                }
                if (y <= 20 && (timeSinceLastCollisionEvent > COLLISION_COOLDOWN)) {
                    timeSinceLastCollisionEvent = 0;
                    playSound("collision");
                }
            }
        })
        );
        animation.setRate(animation.getRate() * .5);
        animation.setCycleCount(Timeline.INDEFINITE);

    }

    private boolean processRacketCollision() {
        boolean racketCollision = racket.getBoundsInParent().intersects(circle.getBoundsInParent());

        if (racketCollision && (timeSinceLastCollisionEvent > COLLISION_COOLDOWN)) { // This is second condition is a cooldown. It prevents odd behavior happening with the ball and the racket if the racket hits the ball at certain angles.
            System.out.println("Racket collision detected!");
            timeSinceLastCollisionEvent = 0;
            incrementPlayerScore();
            playSound("racket");
            return true;
        } else {
            return false;
        }
    }

    private void moveBall() {

        // Check boundaries
        if (x < radius || x > getWidth() - radius) {
            dx *= -1; // Change ball move direction
        }
        if (y < radius || y > getHeight() - radius || processRacketCollision()) {
            dy *= -1; // Change ball move direction
        }

        // Adjust ball position by 1 or -1
        x += dx;
        y += dy;
        circle.setCenterX(x);
        circle.setCenterY(y);
        ballCoordsToLabel();
    }

    public void play() {
        animation.play();
    }

    public void pause() {
        animation.pause();
    }

    public void increaseSpeed() {
        animation.setRate(animation.getRate() * 1.5);
        System.out.println(animation.getRate());
    }

    public void decreaseSpeed() {
        animation.setRate(animation.getRate() * 1.5 > 0 ? animation.getRate() / 1.5 : 0);
        System.out.println(animation.getRate());
    }

    public DoubleProperty rateProperty() {
        return animation.rateProperty();
    }

    public void moveRacket(double x) {
        racket.relocate(x, 580);
    }

    public void showInfoLabel() {
        double paneHeight = getHeight();
        double paneWidth = getWidth();
        getChildren().add(infoLabel);
        // Center Text
        infoLabel.relocate(paneWidth / 4.0, paneHeight / 2.0);
    }

    public void hideInfoLabel() {
        getChildren().remove(infoLabel);
    }

    private void incrementPlayerScore() {
        playerScoreLabel.setText("Player score: " + ++playerScore);
    }

    private void incrementComputerScore() {
        computerScoreLabel.setText("Computer score: " + ++computerScore);

    }

    private void ballCoordsToLabel() {
        ballCords.setText("Ball coords: " + x + ", " + y);
    }

    private void playSound(String sampleType) {
        boolean isSfx = true;
        String sample = "sound/sfx/Sample_00";
        int randInt = (int) (Math.random() * 2);
        switch (sampleType) {
            case "collision":
                randInt = (int) (Math.random() * 2);
                isSfx = true;
                switch (randInt) {
                    case 0:
                        sample += "05";
                        break;
                    case 1:
                        sample += "07";
                        break;
                }
                break;
            case "racket":
                randInt = (int) (Math.random() * 2);
                isSfx = true;
                switch (randInt) {
                    case 0:
                        sample += "10";
                        break;
                    case 1:
                        sample += "12";
                        break;
                }
                break;
            case "bgm":
                sample = "sound/bgm/";
                randInt = (int) (Math.random() * 3);
                isSfx = false;
                switch (randInt) {
                    case 0:
                        currentlyPlaying = "Caleb Fontenot - Blips";
                        nowPlaying.setText("Now playing: " + currentlyPlaying);
                        break;
                    case 1:
                        currentlyPlaying = "Caleb Fontenot - DEAL WITH IT";
                        nowPlaying.setText("Now playing: " + currentlyPlaying);
                        break;
                    case 2:
                        currentlyPlaying = "Caleb Fontenot - Tomodachi Moment";
                        nowPlaying.setText("Now playing: " + currentlyPlaying);
                        break;
                }
                sample += currentlyPlaying;
                break;
        }
        Media sound = new Media(new File(sample + ".wav").toURI().toString());
        MediaPlayer mediaPlayer = new MediaPlayer(sound);
        mediaPlayer.setStartTime(Duration.ZERO);
        mediaPlayer.setAutoPlay(true);
        // If we're playing BGM, lower the volume a little
        if (!isSfx) {
            mediaPlayer.setVolume(0.6);
            mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
        }
        mediaPlayer.play();
        mediaPlayers.add(mediaPlayer); // Prevent the JVM's GC from deleting our media player if there's already one running
        // Free the media player when it has finished playing a sound.
        mediaPlayer.setOnEndOfMedia(new Runnable() {
            @Override
            public void run() {
                //System.out.println("I'm done playing sound! Please take me to the ether, Java GC!");
                mediaPlayer.stop();
                mediaPlayer.dispose();
                mediaPlayers.remove(mediaPlayer);
            }
        });
    }
}