#pragma once
#include "DrawUtils.hpp"
#include "Entity.hpp"
#include "Levels.hpp"
#include "Projectile.hpp"
#include "joystick.hpp"
#include "keypad.hpp"

#define SCREEN_WIDTH 320
#define SCREEN_HEIGH 240
#define MAX_ENEMY_NUM 20
#define MAX_DOORS_NUM 4
#define PLAYER_HITBOX_X_OFFSET 6
#define PLAYER_HITBOX_Y_OFFSET 0
#define PLAYER_HITBOX_Y_OFFSET_BOTTOM 11
#define PLAYER_HITBOX_W 16
#define PLAYER_HITBOX_H 13
#define ENEMY_SIZE 16
#define JOY_CENTER_X 615
#define JOY_CENTER_Y 590
#define ENEMY_UPDATE_EVERY 2
#define ENEMY_SPEED 2
#define PROJECTILE_SIZE 5
#define MAX_CHEST_NUM 5

class Game {
  private:
    DrawUtils utils;
    int16_t player_posX;
    int16_t player_posY;
    int16_t player_prevX;
    int16_t player_prevY;
    int level;
    Entity enemy_pos[MAX_ENEMY_NUM];
    uint8_t enemy_count;
    Chest chest_pos[MAX_CHEST_NUM];
    uint8_t chest_count;
    DoorHitbox doors_pos[MAX_DOORS_NUM];
    uint8_t doors_num;
    DoorType last_door_type;
    Projectile projectiles[PROJECTILE_SIZE];
    int8_t dir_x, dir_y;
    int8_t player_hp, player_attack_power;
    int16_t number_of_rooms_entered;
    int16_t enemies_killed;
    int16_t hitCheck;
    int16_t game_over;
    int16_t enemy_hp;
    bool restart;
    int16_t chest_balance;
    bool invert_shoot;
    uint8_t enemy_update_idx;

    void drawLevel();
    void updateEnemies();
    uint8_t playerHitsDoor();
    float normalizeX(uint16_t v);
    float normalizeY(uint16_t v);
    void levelLoadRAM(const char *lvl);
    char getTile(int16_t x, int16_t y);
    void updatePlayerPos();
    void checkCollisionPlayerBlock(int16_t x, int16_t y);
    void checkCollisionPlayerChest(int16_t x, int16_t y);
    void checkCollisionPlayer(int16_t x, int16_t y);

    bool checkCollisionBlock(int16_t x, int16_t y, int16_t width,
                             int16_t height);
    bool checkCollisionChest(int16_t x, int16_t y, int16_t width,
                             int16_t height);
    bool moveEnemy(Entity &enemy);
    bool tryMoveEnemy(Entity &enemy, int16_t next_x, int16_t next_y);
    bool checkCollisionEntities(int16_t x1, int16_t y1, int16_t w1, int16_t h1,
                                int16_t x2, int16_t y2, int16_t w2, int16_t h2);
    bool checkEnemyHitsPlayer();
    bool checkEnemyHitsEnemy(Entity &enemy, int16_t next_x, int16_t next_y);
    bool tryRandomEnemyMove(Entity &enemy);
    void shootProjectiles();
    void updateProjectiles();
    void checkProjectileHitsEnemy();
    void resetGame();
    void interactChest();
    uint8_t pickNextLevel();

    Keypad keypad;
    Joystick joystick;
    char curr_level[LEVEL_H * (LEVEL_W + 1) + 1];

  public:
    Game(Keypad keypad, Joystick joystick);
    void initGame();
    void loop();
};
