#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../lib/raylib.h"
#include "../include/entity.h"
#include "../include/game.h"
#include "../include/tile.h"
#include "../include/util.h"
#include "../include/player.h"
#include "../include/const.h"
#include "../include/level.h"

game_t *game_create(void) {
  game_t *game = malloc(sizeof(game_t));
  *game = (game_t) {
    .state = STATE_GAME,
    .level = NULL,
    .quit = false,
    .defeat = false,
    .victory = false,
    .menu = menu_create(),
    .player = NULL,
    .tiles = NULL,
    .tiles_len = 0,
    .entities = NULL,
    .entities_len = 0,
    .effects = NULL,
    .effects_len = 0,
    .assets.tiles = texture_load("assets/tiles.png", SCALE),
    .assets.entities = texture_load("assets/entities.png", SCALE),
    .assets.font = texture_load("assets/font.png", SCALE - 1),
    .assets.images = texture_load("assets/images.png", SCALE),
    .assets.background = texture_load("assets/background.png", SCALE),
    .assets.track = LoadSound("assets/song.wav"),
    .assets.win = LoadSound("assets/win.wav"),
    .assets.lose = LoadSound("assets/lose.wav"),
    .assets.locked = LoadSound("assets/locked.wav"),
    .camera = (Camera2D) {
      .offset = (pos_t) { 0.0, 0.0 },
      .zoom = 1,
    },
    .xp = 0,
    .sceen_timer = 0,
    .door = NULL,
    .overlay = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT),
    .is_muted = false,
  };
  level_load(game, LEVEL_1);
  SetSoundVolume(game->assets.track, GAME_MUSIC_VOLUME);
  SetSoundVolume(game->assets.win, GAME_SOUND_VOLUME);
  SetSoundVolume(game->assets.lose, GAME_SOUND_VOLUME);
  SetSoundVolume(game->assets.locked, GAME_SOUND_VOLUME);
  return game;
}

void game_update(game_t *game) {
  bool do_toggle_mute = false;
  if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
    pos_t mouse_pos = GetMousePosition();
    rect_t mouse_rect = {
      .x = mouse_pos.x,
      .y = mouse_pos.y,
      .width = 1,
      .height = 1,
    };
    rect_t mute_button_rect = {
      .x = TILE_WIDTH / 2.0,
      .y = WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0,
      .width = TILE_WIDTH,
      .height = TILE_HEIGHT,
    };
    rect_t pause_button_rect = {
      .x = WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0,
      .y = WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0,
      .width = TILE_WIDTH,
      .height = TILE_HEIGHT,
    };
    if (rect_collide(mouse_rect, mute_button_rect)) {
      do_toggle_mute = true;
    }
    if (!game->victory && !game->defeat) {
      if (rect_collide(mouse_rect, pause_button_rect)) {
        switch (game->state) {
          case STATE_GAME:
            game->state = STATE_MENU;
            break;
          case STATE_MENU:
            game->state = STATE_GAME;
            break;
        }
      }
    }
  }
  if (IsKeyPressed(KEY_M)) {
    do_toggle_mute = true;
  }
  if (do_toggle_mute) {
    game->is_muted = !game->is_muted;
    if (game->is_muted) {
      SetSoundVolume(game->assets.track, 0.0f);
      SetSoundVolume(game->assets.win, 0.0f);
      SetSoundVolume(game->assets.lose, 0.0f);
      SetSoundVolume(game->assets.locked, 0.0f);
    }
    else {
      SetSoundVolume(game->assets.track, GAME_MUSIC_VOLUME);
      SetSoundVolume(game->assets.win, GAME_SOUND_VOLUME);
      SetSoundVolume(game->assets.lose, GAME_SOUND_VOLUME);
      SetSoundVolume(game->assets.locked, GAME_SOUND_VOLUME);
    }
  }
  if (!IsSoundPlaying(game->assets.track) && !game->victory && !game->defeat) {
    PlaySound(game->assets.track);
  }
  switch (game->state) {
    case STATE_MENU: {
      menu_update(game->menu, game);
      break;
    }
    case STATE_GAME: {
      if (!game->victory && !game->defeat) {
        if (IsKeyPressed(KEY_ESCAPE)) {
          game->state = STATE_MENU;
          game->menu->idx = 0;
        }
      }
      if (game->level != NULL) {
        // defeat
        if (game->defeat) {
          if (game->sceen_timer == 0) {
            StopSound(game->assets.track);
          }
          if (game->sceen_timer == 20) {
            PlaySound(game->assets.lose);
          }
          if (game->sceen_timer == GAME_SCEEN_TIMER) {
            level_e level = game->level->type;
            int max_health = game->player->max_health;
            max_health -= 2;
            if (max_health <= 0) {
              level = LEVEL_1;
              max_health = PLAYER_MAX_HEALTH;
            }
            level_unload(game);
            level_load(game, level);
            game->player->max_health = max_health;
            game->player->health = max_health;
            game->defeat = false;
            game->xp = 0;
            game->sceen_timer = 0;
          }
          else {
            game->sceen_timer++;
          }
        }
        // victory
        if (game->victory) {
          if (game->sceen_timer == 0) {
            StopSound(game->assets.track);
          }
          if (game->sceen_timer == 20) {
            PlaySound(game->assets.win);
          }
          if (game->sceen_timer == GAME_SCEEN_TIMER) {
            if (game->level->type < LEVELS) {
              level_e level = game->level->type;
              int max_health = game->player->max_health;
              level++;
              level_unload(game);
              level_load(game, level);
              game->player->max_health = max_health;
              game->player->health = max_health;
            }
            game->victory = false;
            game->sceen_timer = 0;
          }
          else {
            if (game->sceen_timer == 125) {
              game->door->is_locked = true;
            }
            game->sceen_timer++;
          }
        }
        if (!game->victory && !game->defeat) {
          player_update(game->player, game);
        }
        for (int i = 0; i < game->tiles_len; i++) {
          tile_update(game->tiles[i], game);
        }
        for (int i = 0; i < game->entities_len; i++) {
          int len = game->entities_len;
          entity_update(&game->entities[i], game);
          // check if entity was removed
          if (game->entities_len < len) {
            i--;
          }
        }
        for (int i = 0; i < game->effects_len; i++) {
          int len = game->effects_len;
          effect_update(game->effects[i], game);
          // check if enemy was removed
          if (game->effects_len < len) {
            i--;
          }
        }
        pos_t camera_pos = (pos_t) {
          .x = WINDOW_WIDTH / 2.0 - game->player->pos.x - PLAYER_WIDTH / 2.0,
          .y = WINDOW_HEIGHT / 2.0 - game->player->pos.y,
        };
        if (camera_pos.x > 0.0) {
          camera_pos.x = 0.0;
        }
        if (camera_pos.x + TILE_WIDTH * game->level->width < WINDOW_WIDTH) {
          camera_pos.x = WINDOW_WIDTH - TILE_WIDTH * game->level->width;
        }
        if (camera_pos.y > 0.0) {
          camera_pos.y = 0.0;
        }
        if (camera_pos.y + TILE_HEIGHT * game->level->height < WINDOW_HEIGHT) {
          camera_pos.y = WINDOW_HEIGHT - TILE_HEIGHT * game->level->height;
        }
        game->camera.offset = pos_snap(camera_pos);
      }
      break;
    }
  }
}

void game_draw(game_t *game) {
  // render overlay
  BeginTextureMode(game->overlay);
  ClearBackground(BLACK);
  DrawRectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, BLACK);
  BeginBlendMode(BLEND_SUBTRACT_COLORS);
  int radius = WINDOW_WIDTH;
  radius -= radius / 80.0 * game->sceen_timer;
  if (radius < PLAYER_HEIGHT) {
    radius = PLAYER_HEIGHT;
  }
  DrawCircle(game->player->pos.x + PLAYER_WIDTH / 2.0, WINDOW_HEIGHT - (game->player->pos.y + PLAYER_HEIGHT / 2.0), radius, WHITE);
  EndBlendMode();
  EndTextureMode();
  // render sceen
  BeginDrawing();
  ClearBackground((Color) { 5, 5, 5, 255 });
  if (game->level != LEVEL_NULL) {
    BeginMode2D(game->camera);
    for (int i = 0; i < game->tiles_len; i++) {
      tile_draw(game->tiles[i], game);
    }
    for (int i = 0; i < game->entities_len; i++) {
      entity_t *entity = &game->entities[i];
      if (entity->type == ENTITY_GATE || entity->type == ENTITY_DOOR) {
        entity_draw(entity, game);
      }
    }
    for (int i = 0; i < game->entities_len; i++) {
      entity_t *entity = &game->entities[i];
      if (entity->type != ENTITY_GATE && entity->type != ENTITY_DOOR) {
        entity_draw(entity, game);
      }
    }
    if (game->sceen_timer < 100) {
      player_draw(game->player, game);
    }
    for (int i = 0; i < game->effects_len; i++) {
      effect_draw(game->effects[i], game);
    }
    // draw overlay
    DrawTexture(game->overlay.texture, 0, 0, WHITE);
    EndMode2D();
    // draw interface
    int i = 0;
    for (i = 0; i < game->player->health / 2; i++) {
      DrawTextureRec(game->assets.tiles, texture_rect(0, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);
    }
    if (game->player->health % 2 != 0) {
      DrawTextureRec(game->assets.tiles, texture_rect(1, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);
      i++;
    }
    for (; i < game->player->max_health / 2; i++) {
      DrawTextureRec(game->assets.tiles, texture_rect(2, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);
    }
    char xp_text[10];
    snprintf(xp_text, 10, "%dXP", game->xp);
    pos_t xp_pos = (pos_t) {
      .x = WINDOW_WIDTH - TILE_WIDTH / 2.0,
      .y = TILE_WIDTH / 1.5,
    };
    text_draw(xp_pos, xp_text, TEXT_ALIGNMENT_RIGHT, game);
    char level_text[10];
    snprintf(level_text, 10, "L%d", game->level->type);
    pos_t level_pos = (pos_t) {
      .x = WINDOW_WIDTH - TILE_WIDTH / 2.0,
      .y = TILE_WIDTH / 1.5 + 25,
    };
    text_draw(level_pos, level_text, TEXT_ALIGNMENT_RIGHT, game);
  }
  if (game->state == STATE_MENU) {
    menu_draw(game->menu, game);
  }
  if (game->is_muted) {
    DrawTextureRec(game->assets.tiles, texture_rect(5, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
  }
  else {
    DrawTextureRec(game->assets.tiles, texture_rect(4, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
  }
  if (game->state == STATE_GAME) {
    if (game->victory || game->defeat) {
      DrawTextureRec(game->assets.tiles, texture_rect(8, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
    }
    else {
      DrawTextureRec(game->assets.tiles, texture_rect(6, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
    }
  }
  else {
    DrawTextureRec(game->assets.tiles, texture_rect(7, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
  }
  EndDrawing();
}

void game_free(game_t *game) {
  menu_free(game->menu);
  if (game->level != LEVEL_NULL) {
    level_unload(game);
  }
  UnloadTexture(game->assets.tiles);
  UnloadTexture(game->assets.entities);
  UnloadTexture(game->assets.font);
  UnloadTexture(game->assets.images);
  UnloadTexture(game->assets.background);
  UnloadSound(game->assets.track);
  UnloadSound(game->assets.win);
  UnloadSound(game->assets.lose);
  UnloadSound(game->assets.locked);
  UnloadRenderTexture(game->overlay);
  free(game);
}
