#include "engine/engine.h"

#include <alias/random.h>

// main input
enum Binding {
  Binding_Back,
  Binding_Forward,
  Binding_Pause,
  Binding_LeftClick,
  Binding_RightClick,
  Binding_MouseX,
  Binding_MouseY,
  Binding_PlayerLeft,
  Binding_PlayerRight,
  Binding_PlayerUp,
  Binding_PlayerDown,
};

struct engine_input_BackendPair main_input_backend[] = {
    {engine_input_Device_keyboardEnter, Binding_Forward},
    {engine_input_Device_keyboardEscape, Binding_Back},
    {engine_input_Device_keyboardP, Binding_Pause},
    {engine_input_Device_mouseLeftButton, Binding_LeftClick},
    {engine_input_Device_mouseRightButton, Binding_RightClick},
    {engine_input_Device_mousePositionX, Binding_MouseX},
    {engine_input_Device_mousePositionY, Binding_MouseY},
    {engine_input_Device_keyboardLeft, Binding_PlayerLeft},
    {engine_input_Device_keyboardRight, Binding_PlayerRight},
    {engine_input_Device_keyboardUp, Binding_PlayerUp},
    {engine_input_Device_keyboardDown, Binding_PlayerDown},
    {engine_input_Device_keyboardA, Binding_PlayerLeft},
    {engine_input_Device_keyboardD, Binding_PlayerRight},
    {engine_input_Device_keyboardW, Binding_PlayerUp},
    {engine_input_Device_keyboardS, Binding_PlayerDown},
};

struct MainInputs {
  struct engine_input_Signal menu_up;
  struct engine_input_Signal menu_down;
  struct engine_input_Signal menu_left;
  struct engine_input_Signal menu_right;
  struct engine_input_Signal menu_back;
  struct engine_input_Signal menu_forward;
  struct engine_input_Signal mouse_position;
  struct engine_input_Signal mouse_left_click;
};

struct MainInputs main_inputs = {.menu_up = ENGINE_INPUT_SIGNAL_UP(Binding_PlayerUp),
                                 .menu_down = ENGINE_INPUT_SIGNAL_UP(Binding_PlayerDown),
                                 .menu_left = ENGINE_INPUT_SIGNAL_UP(Binding_PlayerLeft),
                                 .menu_right = ENGINE_INPUT_SIGNAL_UP(Binding_PlayerRight),
                                 .menu_back = ENGINE_INPUT_SIGNAL_UP(Binding_Back),
                                 .menu_forward = ENGINE_INPUT_SIGNAL_UP(Binding_Forward),
                                 .mouse_position = ENGINE_INPUT_SIGNAL_POINT(Binding_MouseX, Binding_MouseY),
                                 .mouse_left_click = ENGINE_INPUT_SIGNAL_DOWN(Binding_LeftClick)};

#include "component-movement.inc"
#include "component-player_control_movement.inc"

#include "system-movement.inc"
#include "system-player_control_movement.inc"

#include "prefab-main_camera.inc"
#include "prefab-player_target.inc"
#include "prefab-player.inc"
#include "prefab-simple_grass.inc"

// gameplay state
struct GameplayState {
  alias_ecs_LayerHandle layer;

  alias_ecs_EntityHandle player;
  alias_ecs_EntityHandle player_target;

  alias_ecs_EntityHandle camera;
} _gameplay_state;

static void _gameplay_playing_begin(void *ud) {
  struct GameplayState *state = (struct GameplayState *)ud;

  ENGINE_ECS(create_layer, engine_ecs(), &(struct alias_ecs_LayerCreateInfo){.max_entities = 0}, &state->layer);

  state->player_target = _spawn_player_target(state->layer);
  state->player = _spawn_player(state->layer, alias_pga2d_point(0, 0), state->player_target);
  state->camera = _spawn_main_camera(state->layer, state->player);
  PlayerControlMovement_write(state->player)->inputs->mouse_position.click_camera = state->camera;

  for(uint32_t i = 0; i < 1000; i++) {
    alias_R x = alias_random_f32_snorm() * 100;
    alias_R y = alias_random_f32_snorm() * 100;
    _spawn_simple_grass(state->layer, alias_pga2d_point(x, y));
  }
}

static void _gameplay_playing_frame(void *ud) {
  _player_control_movement_system();
  _movement_system();
}

static void _gameplay_playing_ui(void *ud) {
  engine_ui_top();
  engine_ui_fontColor(alias_Color_BLACK);
  engine_ui_text("version 1");
}

static void _gameplay_playing_pause(void *ud) {
}

static void _gameplay_playing_unpause(void *ud) {
}

static void _gameplay_playing_end(void *ud) {
  struct GameplayState *state = (struct GameplayState *)ud;

  ENGINE_ECS(destroy_layer, engine_ecs(), state->layer, ALIAS_ECS_LAYER_DESTROY_REMOVE_ENTITIES);
}

static struct engine_State _gameplay_playing_state = {
  .begin = _gameplay_playing_begin,
  .frame = _gameplay_playing_frame,
  .ui = _gameplay_playing_ui,
  .pause = _gameplay_playing_pause,
  .unpause = _gameplay_playing_unpause,
  .end = _gameplay_playing_end,
  .ud = &_gameplay_state
};

static void _gameplay_open_level(const char *filename) {
  engine_state_push(&_gameplay_playing_state);
}

// main menu state
static void _main_menu_play(void) {
  engine_state_pop();
  _gameplay_open_level("");
}

static void _main_menu_quit(void) { engine_state_pop(); }

struct {
  const char *text;
  void (*f)(void);
} _main_menu_items[] = {{"play", _main_menu_play}, {"quit", _main_menu_quit}};

static uint32_t _main_menu_index = 0;

#define MAIN_MENU_NUM_ITEMS (sizeof(_main_menu_items) / sizeof(_main_menu_items[0]))

static void _main_menu_ui(void *ud) {
  (void)ud;

  engine_ui_center();
  engine_ui_fontColor(alias_Color_GRAY);

  engine_ui_vertical();
  engine_ui_fontSize(40);
  engine_ui_text("Alias Town");

  engine_ui_fontSize(20);
  for(uint32_t i = 0; i < MAIN_MENU_NUM_ITEMS; i++) {
    engine_ui_text("%s %s", i == _main_menu_index ? ">" : " ", _main_menu_items[i].text);
  }

  engine_ui_end();

  engine_ui_top();
  engine_ui_fontColor(alias_Color_BLACK);
  engine_ui_text("version 1");
}

static void _main_menu_frame(void *ud) {
  if(main_inputs.menu_up.boolean) {
    _main_menu_index = (_main_menu_index == 0 ? MAIN_MENU_NUM_ITEMS : _main_menu_index) - 1;
  }

  if(main_inputs.menu_down.boolean) {
    _main_menu_index = (_main_menu_index + 1) % MAIN_MENU_NUM_ITEMS;
  }

  if(main_inputs.menu_forward.boolean) {
    _main_menu_items[_main_menu_index].f();
  }
}

static struct engine_State _main_menu_state = {.ui = _main_menu_ui, .frame = _main_menu_frame};

// intro state
static struct engine_Image _intro_title_image = {.path = "images/intro_title.png"};

static int _intro_l = 0;
static char *_unicat_names[] = {"Sarah", "Chloe", "Frank", "Anna", "Willa", "Melonie"};

static void _intro_ui(void *ud) {
  (void)ud;

  engine_ui_center();
  engine_ui_image(&_intro_title_image);

  engine_ui_center();
  engine_ui_fontColor(alias_Color_GRAY);

  engine_ui_vertical();
  engine_ui_fontSize(40);
  engine_ui_text("Alias Town");

  engine_ui_fontSize(20);
  engine_ui_text("A game by %s | Unicat",
                 _unicat_names[(_intro_l++ >> 5) % (sizeof(_unicat_names) / sizeof(_unicat_names[0]))]);
  engine_ui_end();

  engine_ui_top();
  engine_ui_fontColor(alias_Color_BLACK);
  engine_ui_text("version 1");
}

static void _intro_frame(void *ud) {
  (void)ud;

  if(main_inputs.menu_back.boolean) {
    engine_state_pop();
    return;
  }

  if(main_inputs.menu_forward.boolean) {
    engine_state_pop();
    engine_state_push(&_main_menu_state);
    return;
  }
}

static struct engine_State _intro_state = {.ui = _intro_ui, .frame = _intro_frame};

// entry point
struct engine_State *engine_application_entryPoint(void) {
  engine_input_setPlayerBackend(0, main_input_backend, sizeof(main_input_backend) / sizeof(main_input_backend[0]));
  engine_input_addFrontend(0, &main_inputs.menu_up, 8);
  return &_intro_state;
}