package com.example.flappybird.states;

import com.example.flappybird.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

public class PlayState extends GameState {
    // To show instructions screen
    protected boolean inStartGameState = false;

    // Medal to be awarded after each game
    protected String medal;

    protected final Highscore highscore = new Highscore();

    public PlayState(GamePanel panel) {
        super(panel);
    }

    @Override
    public void paintGameState(Graphics g) {
        gamePanel.moveBase(g);

        if (gameBird.isAlive()) {
            // Start at instructions state
            if (inStartGameState) {
                paintStartScreen(g);
            }
            else {
                // Start game
                movePipes(g);
                gameBird.moveBird();
                if(gameBird.collidesWithBase()) {
                    // Play audio and set state to dead
                    gameBird.killBird();
                    FlappyBird.audio.hit();
                }
            }

            // Draw base over pipes
            gamePanel.paintBase(g);

            // Draw player score
            paintScore(g, score, false, 0, 0);
        }
        else {
            movePipes(g);
            gamePanel.paintBase(g);

            // Draw game over assets
            paintScoreBoard(g);
        }
    }

    protected void paintStartScreen(Graphics g) {
        // Set bird's new position
        gameBird.setGameStartPos();

        // Get ready text
        g.drawImage(FlappyBird.textures.get("getReadyText").getImage(),
                FlappyBird.textures.get("getReadyText").getX(),
                FlappyBird.textures.get("getReadyText").getY(), null);

        // Instructions image
        g.drawImage(FlappyBird.textures.get("instructions").getImage(),
                FlappyBird.textures.get("instructions").getX(),
                FlappyBird.textures.get("instructions").getY(), null);
    }

    /**
     * Aligns and draws score using image textures
     *
     * @param mini Boolean for drawing small or large numbers
     * @param x X-coordinate to draw for mini numbers
     */
    public void paintScore(Graphics g, int drawNum, boolean mini, int x, int y) {
        // Char array of digits
        char[] digits = (String.valueOf(drawNum)).toCharArray();

        // Calculate width for numeric textures
        int takeUp = 0;
        for (char digit : digits) {

            // Size to add varies based on texture
            if (mini) {
                takeUp += 18;
            } else {
                takeUp += digit == '1' ? 25 : 35;
            }
        }

        // Calculate x-coordinate
        int drawScoreX = mini ? (x - takeUp) : (FlappyBird.WIDTH / 2 - takeUp / 2);

        // Draw every digit
        for (char digit : digits) {
            Texture texture = FlappyBird.textures.get((mini ? "mini-score-" : "score-") + digit);
            g.drawImage(texture.getImage(), drawScoreX, (mini ? y : 60), null);

            // Size to add varies based on texture
            if (mini) {
                drawScoreX += 18;
            } else {
                drawScoreX += digit == '1' ? 25 : 35;
            }
        }
    }

    /**
     * Moves and repositions pipes
     */
    protected void movePipes(Graphics g) {

        // Decrease distance between pipes
        if (gameBird.isAlive()) {
            pipeDistTracker --;
        }

        // Initialize pipes as null
        Pipe topPipe = null;
        Pipe bottomPipe = null;

        // If there is no distance,
        // a new pipe is needed
        if (pipeDistTracker < 0) {

            // Reset distance
            pipeDistTracker = Pipe.PIPE_DISTANCE;

            for (Pipe p : pipes) {

                // If pipe is out of screen
                if (p.getX() < 0) {
                    if (topPipe == null) {
                        topPipe = p;
                        topPipe.canAwardPoint = true;
                    }
                    else if (bottomPipe == null) {
                        bottomPipe = p;
                        topPipe.canAwardPoint = true;
                    }
                }
            }

            Pipe currentPipe; // New pipe object for top and bottom pipes

            // Move and handle initial creation of top and bottom pipes

            if (topPipe == null) {
                currentPipe = new Pipe("top");
                topPipe = currentPipe;
                pipes.add(topPipe);
            } else {
                topPipe.reset();
            }

            if (bottomPipe == null) {
                currentPipe = new Pipe("bottom");
                bottomPipe = currentPipe;
                pipes.add(bottomPipe);

                // Avoid doubling points when passing initial pipes
                bottomPipe.canAwardPoint = false;
            } else {
                bottomPipe.reset();
            }

            // Set y-coordinate of bottom pipe based on
            // y-coordinate of top pipe
            bottomPipe.setY(topPipe.getY() + Pipe.PIPE_SPACING);

        }

        // Move and draw each pipe
        for (Pipe p : pipes) {

            // Move the pipe
            if (gameBird.isAlive()) {
                p.move();
            }

            // Draw the top and bottom pipes
            if (p.getY() <= 0) {
                g.drawImage(FlappyBird.textures.get("pipe-top").getImage(), p.getX(), p.getY(), null);
            } else {
                g.drawImage(FlappyBird.textures.get("pipe-bottom").getImage(), p.getX(), p.getY(), null);
            }

            // Check if bird hits pipes
            if (gameBird.isAlive()) {
                if (p.collide(gameBird)) {
                    // Kill bird and play sound
                    gameBird.killBird();
                    FlappyBird.audio.hit();
                }
                else {
                    // Checks if bird passes a pipe
                    if (gameBird.getX() >= p.getX() + Pipe.WIDTH / 2) {

                        // Increase score and play sound
                        if (p.canAwardPoint) {
                            FlappyBird.audio.point();
                            score ++;
                            p.canAwardPoint = false;
                        }
                    }
                }
            }
        }
    }

    protected void paintScoreBoard(Graphics g) {
        // Game over text
        g.drawImage(FlappyBird.textures.get("gameOverText").getImage(),
                FlappyBird.textures.get("gameOverText").getX(),
                FlappyBird.textures.get("gameOverText").getY(), null);

        // Scorecard
        g.drawImage(FlappyBird.textures.get("scoreCard").getImage(),
                FlappyBird.textures.get("scoreCard").getX(),
                FlappyBird.textures.get("scoreCard").getY(), null);

        // New highscore image
        if (scoreWasGreater) {
            g.drawImage(FlappyBird.textures.get("newHighscore").getImage(),
                    FlappyBird.textures.get("newHighscore").getX(),
                    FlappyBird.textures.get("newHighscore").getY(), null);
        }

        // Draw mini fonts for current and best scores
        paintScore(g, score, true, 303, 276);
        paintScore(g, highscore.bestScore(), true, 303, 330);

        // Handle highscore
        if (score > highscore.bestScore()) {

            // New best score
            scoreWasGreater = true;
            highscore.setNewBest(score); // Set in data file
        }

        // Medal
        if (score >= 10) { medal = "bronze"; }
        if (score >= 20) { medal = "silver"; }
        if (score >= 30) { medal = "gold"; }
        if (score >= 40) { medal = "platinum"; }

        // Only award a medal if they deserve it
        if (score > 9) {
            g.drawImage(FlappyBird.textures.get(medal).getImage(),
                    FlappyBird.textures.get(medal).getX(),
                    FlappyBird.textures.get(medal).getY(), null);
        }

        // Buttons
        g.drawImage(FlappyBird.textures.get("playButton").getImage(),
                FlappyBird.textures.get("playButton").getX(),
                FlappyBird.textures.get("playButton").getY(), null);
        g.drawImage(FlappyBird.textures.get("leaderboard").getImage(),
                FlappyBird.textures.get("leaderboard").getX(),
                FlappyBird.textures.get("leaderboard").getY(), null);
    }

    @Override
    public void handleKeyboardEvent(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (gameBird.isAlive()) {
            if (keyCode == KeyEvent.VK_SPACE) {

                // Exit instructions state
                if (inStartGameState) {
                    inStartGameState = false;
                }

                // Jump and play audio even if in instructions state
                gameBird.jump();
                FlappyBird.audio.jump();
            }
        }
    }

    @Override
    public void handleMouseEvent(MouseEvent e) {
        if (gameBird.isAlive()) {

            // Allow jump with clicks
            if (inStartGameState) {
                inStartGameState = false;
            }

            // Jump and play sound
            gameBird.jump();
            FlappyBird.audio.jump();
        }
        else {
            // On game over screen, allow restart and leaderboard buttons
            if (gamePanel.isTouching(FlappyBird.textures.get("playButton").getRect())) {
                inStartGameState = true;
                gamePanel.setGameState(this);
                gameBird.setGameStartPos();
                restart();
            } else if (gamePanel.isTouching(FlappyBird.textures.get("leaderboard").getRect())) {
                // Dummy message
                JOptionPane.showMessageDialog(gamePanel,
                        "We can't access the leaderboard right now!",
                        "Oops!",
                        JOptionPane.ERROR_MESSAGE);
            }
        }
    }
}
