#pragma once

#include "GameBoard.h"
#include "GameParameters.h"
#include "Action.h"

#include <vector>
#include <memory>


/**
 * Interprets the player action into the game, depending on the state of the board and the current gamemode
 */
class Game {
    private:
        GameParameters parameters;       // the current parameters of the game
        GameBoard board;                 // the board in which the game is played
        bool started;                    // wheter the game has started
        bool lost;                       // wheter the game is lost
        long int score;                  // the current score
        int framesPassed;                // how many frames have passed since the start of the game
        bool B2BChain;                   // wheter the player is currently on a B2B chain
        std::set<Action> heldActions;    // the list of actions that were pressed last frame
        std::set<Action> initialActions; // the list of actions that have been pressed while there was no active piece
        int heldDAS;                     // the number of frames DAS has been held, positive for right or negative for left
        int heldARR;                     // the number of frames ARR has been held
        int subVerticalPosition;         // how far the active piece is to go down one line
        int leftARETime;                 // how many frames are left before ARE period finishes
        int totalLockDelay;              // how many frames has the active piece touched the ground without moving
        int totalForcedLockDelay;        // how many frames the active piece has touched the ground since the last spawned piece

    public:
        /**
         * Initialize the parameters and creates a new board
         */
        Game(Gamemode gamemode, const Player& controls, int boardWidth, int boardHeight, const std::shared_ptr<PiecesList>& piecesList);

        /**
         * Starts the game
         */
        void start();

        /**
         * Resets the game
         */
        void reset();

    private:
        /**
         * Initializes the game
         */
        void initialize();
    
    public:

        /**
         * Advances to the next frame while excecuting the actions taken by the player,
         * this is where the main game logic takes place
         */
        void nextFrame(const std::set<Action>& playerActions);

    private:
        /**
         * Resets the piece's parameter
         */
        void resetPiece(bool newPiece);

        /**
         * Moves the piece in the specified direction (1 for right and -1 for left)
         */
        void movePiece(int movement);

        /**
         * Rotates the piece with the specified rotation
         */
        void rotatePiece(Rotation rotation);

        /**
         * Locks the piece, updates level and score and spawns the next piece if necessary
         */
        void lockPiece();

    public:
        /**
         * @return If the player has won
         */
        bool hasWon() const;

        /**
         * @return If the player has lost
         */
        bool hasLost() const;

        /**
         * @return The current level
         */
        int getLevel() const;

        /**
         * @return The current number of cleared lines
         */
        int getClearedLines() const;

        /**
         * @return The current grade
         */
        int getGrade() const;

        /**
         * @return The number of frames passed since the start of the game
         */
        int getFramesPassed() const;

        /**
         * @return The current score
         */
        int getScore() const;

        /**
         * @return If the player is currently on a B2B chain
         */
        bool isOnB2BChain() const;

        /**
         * @return How close the active piece's lock delay is to the maximum allowed, betwwen 0 and 1
         */
        float getLockDelayProgression() const;

        /**
         * @return How close the active piece's forced lock delay is to the maximum allowed, betwwen 0 and 1
         */
        float getForcedLockDelayProgression() const;

        /**
         * @return If all blocks are currently bone blocks
         */
        bool areBlocksBones() const;

        /**
         * @return If the board is currently invisible
         */
        bool isBoardInvisible() const;

        /**
         * @return The board
         */
        const Board& getBoard() const;

        /**
         * @return A pointer to the active piece, can be null
         */
        const std::shared_ptr<Piece>& getActivePiece() const;

        /**
         * @return The position of the active piece
         */
        const Position& getActivePiecePosition() const;

        /**
         * @return The position of the ghost piece
         */
        Position getGhostPiecePosition() const;

        /**
         * @return A pointer to the held piece, can be null
         */
        const std::shared_ptr<Piece>& getHeldPiece() const;

        /**
         * @return The next piece queue, can be empty
         */
        const std::vector<Piece>& getNextPieces() const;
};
