#include "screen_editor.h"

#include <fstream>
#include <iostream>
#include <string>

#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "app/core/platform/file_dialog.h"
#include "app/core/window.h"
#include "app/gfx/arena.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "imgui/imgui.h"
#include "util/hex.h"
#include "util/macro.h"

namespace yaze {
namespace editor {

using core::Renderer;

constexpr uint32_t kRedPen = 0xFF0000FF;

void ScreenEditor::Initialize() {}

absl::Status ScreenEditor::Load() {
  ASSIGN_OR_RETURN(dungeon_maps_,
                   zelda3::LoadDungeonMaps(*rom(), dungeon_map_labels_));
  RETURN_IF_ERROR(zelda3::LoadDungeonMapTile16(
      tile16_blockset_, *rom(), rom()->graphics_buffer(), false));
  // TODO: Load roomset gfx based on dungeon ID
  sheets_.try_emplace(0, gfx::Arena::Get().gfx_sheets()[212]);
  sheets_.try_emplace(1, gfx::Arena::Get().gfx_sheets()[213]);
  sheets_.try_emplace(2, gfx::Arena::Get().gfx_sheets()[214]);
  sheets_.try_emplace(3, gfx::Arena::Get().gfx_sheets()[215]);
  /**
  int current_tile8 = 0;
  int tile_data_offset = 0;
  for (int i = 0; i < 4; ++i) {
    for (int j = 0; j < 32; j++) {
      std::vector<uint8_t> tile_data(64, 0);  // 8x8 tile (64 bytes
      int tile_index = current_tile8 + j;
      int x = (j % 8) * 8;
      int y = (j / 8) * 8;
      sheets_[i].Get8x8Tile(tile_index, x, y, tile_data, tile_data_offset);
      tile8_individual_.emplace_back(gfx::Bitmap(8, 8, 4, tile_data));
      tile8_individual_.back().SetPalette(*rom()->mutable_dungeon_palette(3));
      Renderer::Get().RenderBitmap(&tile8_individual_.back());
    }
    tile_data_offset = 0;
  }
  */
  return absl::OkStatus();
}

absl::Status ScreenEditor::Update() {
  if (ImGui::BeginTabBar("##ScreenEditorTabBar")) {
    if (ImGui::BeginTabItem("Dungeon Maps")) {
      DrawDungeonMapsEditor();
      ImGui::EndTabItem();
    }
    DrawInventoryMenuEditor();
    DrawOverworldMapEditor();
    DrawTitleScreenEditor();
    DrawNamingScreenEditor();
    ImGui::EndTabBar();
  }
  return status_;
}

void ScreenEditor::DrawInventoryMenuEditor() {
  if (ImGui::BeginTabItem("Inventory Menu")) {
    static bool create = false;
    if (!create && rom()->is_loaded()) {
      status_ = inventory_.Create();
      palette_ = inventory_.palette();
      create = true;
    }

    DrawInventoryToolset();

    if (ImGui::BeginTable("InventoryScreen", 3, ImGuiTableFlags_Resizable)) {
      ImGui::TableSetupColumn("Canvas");
      ImGui::TableSetupColumn("Tiles");
      ImGui::TableSetupColumn("Palette");
      ImGui::TableHeadersRow();

      ImGui::TableNextColumn();
      screen_canvas_.DrawBackground();
      screen_canvas_.DrawContextMenu();
      screen_canvas_.DrawBitmap(inventory_.bitmap(), 2, create);
      screen_canvas_.DrawGrid(32.0f);
      screen_canvas_.DrawOverlay();

      ImGui::TableNextColumn();
      tilesheet_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
      tilesheet_canvas_.DrawContextMenu();
      tilesheet_canvas_.DrawBitmap(inventory_.tilesheet(), 2, create);
      tilesheet_canvas_.DrawGrid(16.0f);
      tilesheet_canvas_.DrawOverlay();

      ImGui::TableNextColumn();
      gui::DisplayPalette(palette_, create);

      ImGui::EndTable();
    }
    ImGui::Separator();
    ImGui::EndTabItem();
  }
}

void ScreenEditor::DrawInventoryToolset() {
  if (ImGui::BeginTable("InventoryToolset", 8, ImGuiTableFlags_SizingFixedFit,
                        ImVec2(0, 0))) {
    ImGui::TableSetupColumn("#drawTool");
    ImGui::TableSetupColumn("#sep1");
    ImGui::TableSetupColumn("#zoomOut");
    ImGui::TableSetupColumn("#zoomIN");
    ImGui::TableSetupColumn("#sep2");
    ImGui::TableSetupColumn("#bg2Tool");
    ImGui::TableSetupColumn("#bg3Tool");
    ImGui::TableSetupColumn("#itemTool");

    ImGui::TableNextColumn();
    if (ImGui::Button(ICON_MD_UNDO)) {
      // status_ = inventory_.Undo();
    }
    ImGui::TableNextColumn();
    if (ImGui::Button(ICON_MD_REDO)) {
      // status_ = inventory_.Redo();
    }
    ImGui::TableNextColumn();
    ImGui::Text(ICON_MD_MORE_VERT);
    ImGui::TableNextColumn();
    if (ImGui::Button(ICON_MD_ZOOM_OUT)) {
      screen_canvas_.ZoomOut();
    }
    ImGui::TableNextColumn();
    if (ImGui::Button(ICON_MD_ZOOM_IN)) {
      screen_canvas_.ZoomIn();
    }
    ImGui::TableNextColumn();
    ImGui::Text(ICON_MD_MORE_VERT);
    ImGui::TableNextColumn();
    if (ImGui::Button(ICON_MD_DRAW)) {
      current_mode_ = EditingMode::DRAW;
    }
    ImGui::TableNextColumn();
    if (ImGui::Button(ICON_MD_BUILD)) {
      // current_mode_ = EditingMode::BUILD;
    }

    ImGui::EndTable();
  }
}

void ScreenEditor::DrawDungeonMapScreen(int i) {
  auto &current_dungeon = dungeon_maps_[selected_dungeon];

  floor_number = i;
  screen_canvas_.DrawBackground(ImVec2(325, 325));
  screen_canvas_.DrawTileSelector(64.f);

  auto boss_room = current_dungeon.boss_room;
  for (int j = 0; j < zelda3::kNumRooms; j++) {
    if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
      int tile16_id = current_dungeon.floor_gfx[floor_number][j];
      int posX = ((j % 5) * 32);
      int posY = ((j / 5) * 32);

      gfx::RenderTile16(tile16_blockset_, tile16_id);
      screen_canvas_.DrawBitmap(tile16_blockset_.tile_bitmaps[tile16_id],
                                (posX * 2), (posY * 2), 4.0f);

      if (current_dungeon.floor_rooms[floor_number][j] == boss_room) {
        screen_canvas_.DrawOutlineWithColor((posX * 2), (posY * 2), 64, 64,
                                            kRedPen);
      }

      std::string label =
          dungeon_map_labels_[selected_dungeon][floor_number][j];
      screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
      std::string gfx_id = util::HexByte(tile16_id);
      screen_canvas_.DrawText(gfx_id, (posX * 2), (posY * 2) + 16);
    }
  }

  screen_canvas_.DrawGrid(64.f, 5);
  screen_canvas_.DrawOverlay();

  if (!screen_canvas_.points().empty()) {
    int x = screen_canvas_.points().front().x / 64;
    int y = screen_canvas_.points().front().y / 64;
    selected_room = x + (y * 5);
  }
}

void ScreenEditor::DrawDungeonMapsTabs() {
  auto &current_dungeon = dungeon_maps_[selected_dungeon];
  if (ImGui::BeginTabBar("##DungeonMapTabs")) {
    auto nbr_floors =
        current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
    for (int i = 0; i < nbr_floors; i++) {
      int basement_num = current_dungeon.nbr_of_basement - i;
      std::string tab_name = absl::StrFormat("Basement %d", basement_num);
      if (i >= current_dungeon.nbr_of_basement) {
        tab_name = absl::StrFormat("Floor %d",
                                   i - current_dungeon.nbr_of_basement + 1);
      }
      if (ImGui::BeginTabItem(tab_name.data())) {
        DrawDungeonMapScreen(i);
        ImGui::EndTabItem();
      }
    }
    ImGui::EndTabBar();
  }

  gui::InputHexByte(
      "Selected Room",
      &current_dungeon.floor_rooms[floor_number].at(selected_room));

  gui::InputHexWord("Boss Room", &current_dungeon.boss_room);

  const auto button_size = ImVec2(130, 0);

  if (ImGui::Button("Add Floor", button_size) &&
      current_dungeon.nbr_of_floor < 8) {
    current_dungeon.nbr_of_floor++;
    dungeon_map_labels_[selected_dungeon].emplace_back();
  }
  ImGui::SameLine();
  if (ImGui::Button("Remove Floor", button_size) &&
      current_dungeon.nbr_of_floor > 0) {
    current_dungeon.nbr_of_floor--;
    dungeon_map_labels_[selected_dungeon].pop_back();
  }

  if (ImGui::Button("Add Basement", button_size) &&
      current_dungeon.nbr_of_basement < 8) {
    current_dungeon.nbr_of_basement++;
    dungeon_map_labels_[selected_dungeon].emplace_back();
  }
  ImGui::SameLine();
  if (ImGui::Button("Remove Basement", button_size) &&
      current_dungeon.nbr_of_basement > 0) {
    current_dungeon.nbr_of_basement--;
    dungeon_map_labels_[selected_dungeon].pop_back();
  }

  if (ImGui::Button("Copy Floor", button_size)) {
    copy_button_pressed = true;
  }
  ImGui::SameLine();
  if (ImGui::Button("Paste Floor", button_size)) {
    paste_button_pressed = true;
  }
}

void ScreenEditor::DrawDungeonMapsRoomGfx() {
  if (ImGui::BeginChild("##DungeonMapTiles", ImVec2(0, 0), true)) {
    tilesheet_canvas_.DrawBackground(ImVec2((256 * 2) + 2, (192 * 2) + 4));
    tilesheet_canvas_.DrawContextMenu();
    if (tilesheet_canvas_.DrawTileSelector(32.f)) {
      selected_tile16_ = tilesheet_canvas_.points().front().x / 32 +
                         (tilesheet_canvas_.points().front().y / 32) * 16;
      gfx::RenderTile16(tile16_blockset_, selected_tile16_);
      std::ranges::copy(tile16_blockset_.tile_info[selected_tile16_],
                        current_tile16_info.begin());
    }
    tilesheet_canvas_.DrawBitmap(tile16_blockset_.atlas, 1, 1, 2.0f);
    tilesheet_canvas_.DrawGrid(32.f);
    tilesheet_canvas_.DrawOverlay();

    if (!tilesheet_canvas_.points().empty() &&
        !screen_canvas_.points().empty()) {
      dungeon_maps_[selected_dungeon].floor_gfx[floor_number][selected_room] =
          selected_tile16_;
      tilesheet_canvas_.mutable_points()->clear();
    }

    ImGui::Separator();
    current_tile_canvas_.DrawBackground();  // ImVec2(64 * 2 + 2, 64 * 2 + 4));
    current_tile_canvas_.DrawContextMenu();
    if (current_tile_canvas_.DrawTilePainter(tile8_individual_[selected_tile8_],
                                             16)) {
      // Modify the tile16 based on the selected tile and current_tile16_info
      gfx::ModifyTile16(tile16_blockset_, rom()->graphics_buffer(),
                        current_tile16_info[0], current_tile16_info[1],
                        current_tile16_info[2], current_tile16_info[3], 212,
                        selected_tile16_);
      gfx::UpdateTile16(tile16_blockset_, selected_tile16_);
    }
    current_tile_canvas_.DrawBitmap(
        tile16_blockset_.tile_bitmaps[selected_tile16_], 2, 4.0f);
    current_tile_canvas_.DrawGrid(16.f);
    current_tile_canvas_.DrawOverlay();

    gui::InputTileInfo("TL", &current_tile16_info[0]);
    ImGui::SameLine();
    gui::InputTileInfo("TR", &current_tile16_info[1]);
    gui::InputTileInfo("BL", &current_tile16_info[2]);
    ImGui::SameLine();
    gui::InputTileInfo("BR", &current_tile16_info[3]);

    if (ImGui::Button("Modify Tile16")) {
      gfx::ModifyTile16(tile16_blockset_, rom()->graphics_buffer(),
                        current_tile16_info[0], current_tile16_info[1],
                        current_tile16_info[2], current_tile16_info[3], 212,
                        selected_tile16_);
      gfx::UpdateTile16(tile16_blockset_, selected_tile16_);
    }
  }
  ImGui::EndChild();
}

void ScreenEditor::DrawDungeonMapsEditor() {
  if (ImGui::Button(ICON_MD_DRAW)) {
    current_mode_ = EditingMode::DRAW;
  }
  ImGui::SameLine();
  if (ImGui::Button(ICON_MD_EDIT)) {
    current_mode_ = EditingMode::EDIT;
  }
  ImGui::SameLine();
  if (ImGui::Button(ICON_MD_SAVE)) {
    PRINT_IF_ERROR(zelda3::SaveDungeonMapTile16(tile16_blockset_, *rom()));
  }

  static std::vector<std::string> dungeon_names = {
      "Sewers/Sanctuary",   "Hyrule Castle", "Eastern Palace",
      "Desert Palace",      "Tower of Hera", "Agahnim's Tower",
      "Palace of Darkness", "Swamp Palace",  "Skull Woods",
      "Thieves' Town",      "Ice Palace",    "Misery Mire",
      "Turtle Rock",        "Ganon's Tower"};

  if (ImGui::BeginTable("DungeonMapsTable", 4,
                        ImGuiTableFlags_Resizable |
                            ImGuiTableFlags_Reorderable |
                            ImGuiTableFlags_Hideable)) {
    ImGui::TableSetupColumn("Dungeon");
    ImGui::TableSetupColumn("Map");
    ImGui::TableSetupColumn("Rooms Gfx");
    ImGui::TableSetupColumn("Tiles Gfx");
    ImGui::TableHeadersRow();

    ImGui::TableNextColumn();
    for (int i = 0; i < dungeon_names.size(); i++) {
      rom()->resource_label()->SelectableLabelWithNameEdit(
          selected_dungeon == i, "Dungeon Names", absl::StrFormat("%d", i),
          dungeon_names[i]);
      if (ImGui::IsItemClicked()) {
        selected_dungeon = i;
      }
    }

    ImGui::TableNextColumn();
    DrawDungeonMapsTabs();

    ImGui::TableNextColumn();
    DrawDungeonMapsRoomGfx();

    ImGui::TableNextColumn();
    tilemap_canvas_.DrawBackground();
    tilemap_canvas_.DrawContextMenu();
    if (tilemap_canvas_.DrawTileSelector(16.f)) {
      // Get the tile8 ID to use for the tile16 drawing above
      selected_tile8_ = tilemap_canvas_.GetTileIdFromMousePos();
    }
    tilemap_canvas_.DrawBitmapTable(sheets_);
    tilemap_canvas_.DrawGrid();
    tilemap_canvas_.DrawOverlay();

    ImGui::Text("Selected tile8: %d", selected_tile8_);
    ImGui::Separator();
    ImGui::Text("For use with custom inserted graphics assembly patches.");
    if (ImGui::Button("Load GFX from BIN file")) LoadBinaryGfx();

    ImGui::EndTable();
  }
}

void ScreenEditor::LoadBinaryGfx() {
  std::string bin_file = core::FileDialogWrapper::ShowOpenFileDialog();
  if (!bin_file.empty()) {
    std::ifstream file(bin_file, std::ios::binary);
    if (file.is_open()) {
      // Read the gfx data into a buffer
      std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
                                    std::istreambuf_iterator<char>());
      if (auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
          zelda3::LoadDungeonMapTile16(tile16_blockset_, *rom(), converted_bin,
                                       true)
              .ok()) {
        sheets_.clear();
        std::vector<std::vector<uint8_t>> gfx_sheets;
        for (int i = 0; i < 4; i++) {
          gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
                                  converted_bin.begin() + ((i + 1) * 0x1000));
          sheets_.emplace(i, gfx::Bitmap(128, 32, 8, gfx_sheets[i]));
          sheets_[i].SetPalette(*rom()->mutable_dungeon_palette(3));
          Renderer::Get().RenderBitmap(&sheets_[i]);
        }
        binary_gfx_loaded_ = true;
      } else {
        status_ = absl::InternalError("Failed to load dungeon map tile16");
      }
      file.close();
    }
  }
}

void ScreenEditor::DrawTitleScreenEditor() {
  if (ImGui::BeginTabItem("Title Screen")) {
    ImGui::EndTabItem();
  }
}

void ScreenEditor::DrawNamingScreenEditor() {
  if (ImGui::BeginTabItem("Naming Screen")) {
    ImGui::EndTabItem();
  }
}

void ScreenEditor::DrawOverworldMapEditor() {
  if (ImGui::BeginTabItem("Overworld Map")) {
    ImGui::EndTabItem();
  }
}

void ScreenEditor::DrawToolset() {
  static bool show_bg1 = true;
  static bool show_bg2 = true;
  static bool show_bg3 = true;

  static bool drawing_bg1 = true;
  static bool drawing_bg2 = false;
  static bool drawing_bg3 = false;

  ImGui::Checkbox("Show BG1", &show_bg1);
  ImGui::SameLine();
  ImGui::Checkbox("Show BG2", &show_bg2);

  ImGui::Checkbox("Draw BG1", &drawing_bg1);
  ImGui::SameLine();
  ImGui::Checkbox("Draw BG2", &drawing_bg2);
  ImGui::SameLine();
  ImGui::Checkbox("Draw BG3", &drawing_bg3);
}

}  // namespace editor
}  // namespace yaze
