#include "state.hpp"

#include <backends/imgui_impl_opengl3.h>
#include <backends/imgui_impl_sdl3.h>
#include <imgui.h>

#include "util/display.hpp"
#include "util/math.hpp"

using namespace glm;
using namespace game::util;
using namespace game::state;

namespace game
{
  constexpr auto UPDATE_RATE = 60;
  constexpr auto UPDATE_INTERVAL = (1000 / UPDATE_RATE);

  State::State(SDL_Window* _window, SDL_GLContext _context, resource::xml::Schema settings)
      : window(_window), context(_context), canvas(settings.root()->windowSize, Canvas::DEFAULT)
  {
    resources.settings = settings;
    auto* settingsRoot = resources.settings.root();
    SDL_SetWindowSize(window, settingsRoot->windowSize.x, settingsRoot->windowSize.y);
    display::init(window);

    auto lastCharacterIndex = resources.character_index_get(settingsRoot->lastCharacter);
    if (lastCharacterIndex >= 0)
    {
      select.characterIndex = lastCharacterIndex;
      play.set(resources, lastCharacterIndex, state::Play::CONTINUE);
      type = PLAY;
    }
  }

  void State::update()
  {
#ifndef __EMSCRIPTEN__
    if (auto* settingsRoot = resources.settings.root(); settingsRoot && !display::fullscreen_get())
      SDL_GetWindowSize(window, &settingsRoot->windowSize.x, &settingsRoot->windowSize.y);
#endif

    SDL_Event event;

    while (SDL_PollEvent(&event))
    {
      ImGui_ImplSDL3_ProcessEvent(&event);
      if (event.type == SDL_EVENT_QUIT)
      {
        if (type == PLAY) play.exit(resources);
        isRunning = false;
      }
      else if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat)
      {
        auto isTextInput = ImGui::GetIO().WantTextInput;
        auto isFullscreenShortcut = event.key.key == SDLK_F11 ||
                                    (event.key.key == SDLK_RETURN && (event.key.mod & SDL_KMOD_ALT)) ||
                                    (!isTextInput && event.key.scancode == SDL_SCANCODE_F);
        auto isMuteShortcut = !isTextInput && event.key.scancode == SDL_SCANCODE_M;
        if (isFullscreenShortcut) display::fullscreen_toggle();
        if (isMuteShortcut)
          if (auto* settingsRoot = resources.settings.root())
          {
            settingsRoot->isAudioMuted = !settingsRoot->isAudioMuted;
            resources.audio_mute_set(settingsRoot->isAudioMuted);
          }
      }
#if DEBUG
      else if (event.type == SDL_EVENT_DROP_FILE)
      {
        resources.isDeveloper = true;
        developerWindow.dropped_file_open(event.drop.data);
      }
#endif
      if (!isRunning) return;
    }
    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplSDL3_NewFrame();

    ImGui::NewFrame();

    switch (type)
    {
      case SELECT:
        select.update(resources);
        resources.last_character_clear();
        if (select.info.isNewGame || select.info.isContinue)
        {
          Play::Game game = select.info.isNewGame ? Play::NEW_GAME : Play::CONTINUE;
          if (game == Play::NEW_GAME) resources.character_save_clear(select.characterIndex);

          play.set(resources, select.characterIndex, game);
          type = PLAY;

          select.info.isNewGame = false;
          select.info.isContinue = false;
        }
        break;
      case PLAY:
        play.update(resources);
        if (play.menu.settingsMenu.isGoToSelect)
        {
          play.exit(resources);
          type = SELECT;
          resources.last_character_clear();
          play.menu.settingsMenu.isGoToSelect = false;
        }
        break;
      default:
        break;
    }

#if DEBUG
        developerWindow.update(resources, window, type == PLAY ? &play : nullptr);
        if (developerWindow.action.isMenu && type == PLAY)
        {
          play.exit(resources);
          type = SELECT;
          resources.last_character_clear();
        }
        if (developerWindow.action.character >= 0)
        {
          if (type == PLAY) play.exit(resources);
          select.characterIndex = developerWindow.action.character;
          play.set(resources, select.characterIndex, state::Play::CONTINUE);
          type = PLAY;
        }
#endif

    auto isHideCursor = type == PLAY && !resources.isDeveloper && play.cursor_enabled_get();
    if (isHideCursor != isCursorHidden)
    {
      if (isHideCursor)
        SDL_HideCursor();
      else
        SDL_ShowCursor();
      isCursorHidden = isHideCursor;
    }
  }

  void State::render()
  {
    auto* settingsRoot = resources.settings.root();
    auto& color =
        settingsRoot->isUseCharacterColor && type == PLAY ? play.character.data.root()->color : settingsRoot->color;
    auto windowSize = settingsRoot->windowSize;
#ifndef __EMSCRIPTEN__
    SDL_GetWindowSize(window, &windowSize.x, &windowSize.y);
#endif

    canvas.bind();
    canvas.size_set(windowSize);
    canvas.clear(vec4(color, 1.0f));
    canvas.unbind();

    switch (type)
    {
      case SELECT:
        select.render(resources, canvas);
        break;
      case PLAY:
        play.render(resources, canvas);
        break;
      default:
        break;
    }
    SDL_GL_SwapWindow(window);
  }

  void State::loop()
  {
    auto currentUpdate = SDL_GetTicks();

    if (currentUpdate - previousUpdate >= UPDATE_INTERVAL)
    {
      update();
      render();
      previousUpdate = currentUpdate;
    }

    SDL_Delay(1);
  }
}
