#include "dungeon_editor.h"

#include "absl/strings/str_format.h"
#include "app/core/window.h"
#include "app/gfx/arena.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/dungeon_editor_system.h"
#include "app/zelda3/dungeon/dungeon_object_editor.h"
#include "app/zelda3/dungeon/room.h"
#include "imgui/imgui.h"

namespace yaze::editor {

using core::Renderer;

using ImGui::BeginTabBar;
using ImGui::BeginTabItem;
using ImGui::BeginTable;
using ImGui::Button;
using ImGui::EndTabBar;
using ImGui::EndTabItem;
using ImGui::RadioButton;
using ImGui::SameLine;
using ImGui::TableHeadersRow;
using ImGui::TableNextColumn;
using ImGui::TableNextRow;
using ImGui::TableSetupColumn;
using ImGui::Text;

constexpr ImGuiTableFlags kDungeonObjectTableFlags =
    ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
    ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
    ImGuiTableFlags_BordersV;

void DungeonEditor::Initialize() {
  if (rom_ && !dungeon_editor_system_) {
    dungeon_editor_system_ =
        std::make_unique<zelda3::DungeonEditorSystem>(rom_);
    object_editor_ = std::make_shared<zelda3::DungeonObjectEditor>(rom_);
  }
}

absl::Status DungeonEditor::Load() {
  if (!rom_ || !rom_->is_loaded()) {
    return absl::FailedPreconditionError("ROM not loaded");
  }
  
  auto dungeon_man_pal_group = rom()->palette_group().dungeon_main;

  // Use room loader component for loading rooms
  RETURN_IF_ERROR(room_loader_.LoadAllRooms(rooms_));
  RETURN_IF_ERROR(room_loader_.LoadRoomEntrances(entrances_));

  // Load the palette group and palette for the dungeon
  full_palette_ = dungeon_man_pal_group[current_palette_group_id_];
  ASSIGN_OR_RETURN(current_palette_group_,
                   gfx::CreatePaletteGroupFromLargePalette(full_palette_));

  // Calculate usage statistics
  usage_tracker_.CalculateUsageStats(rooms_);

  // Initialize the new editor system
  if (dungeon_editor_system_) {
    auto status = dungeon_editor_system_->Initialize();
    if (!status.ok()) {
      return status;
    }
  }

  // Initialize the new UI components with loaded data
  room_selector_.set_rom(rom_);
  room_selector_.set_rooms(&rooms_);
  room_selector_.set_entrances(&entrances_);
  room_selector_.set_active_rooms(active_rooms_);
  room_selector_.set_room_selected_callback(
      [this](int room_id) { OnRoomSelected(room_id); });

  canvas_viewer_.SetRom(rom_);
  canvas_viewer_.SetRooms(&rooms_);
  canvas_viewer_.SetCurrentPaletteGroup(current_palette_group_);
  canvas_viewer_.SetCurrentPaletteId(current_palette_id_);

  object_selector_.SetRom(rom_);
  object_selector_.SetCurrentPaletteGroup(current_palette_group_);
  object_selector_.SetCurrentPaletteId(current_palette_id_);
  object_selector_.set_dungeon_editor_system(&dungeon_editor_system_);
  object_selector_.set_object_editor(&object_editor_);
  object_selector_.set_rooms(&rooms_);

  // Set up object selection callback
  object_selector_.SetObjectSelectedCallback(
      [this](const zelda3::RoomObject& object) {
        preview_object_ = object;
        object_loaded_ = true;
        toolset_.set_placement_type(DungeonToolset::kObject);
        object_interaction_.SetPreviewObject(object, true);
      });
  
  // Set up component callbacks
  object_interaction_.SetCurrentRoom(&rooms_, current_room_id_);
  object_interaction_.SetObjectPlacedCallback([this](const zelda3::RoomObject& object) {
    renderer_.ClearObjectCache();
  });
  object_interaction_.SetCacheInvalidationCallback([this]() {
    renderer_.ClearObjectCache();
  });
  
  // Set up toolset callbacks
  toolset_.SetUndoCallback([this]() { PRINT_IF_ERROR(Undo()); });
  toolset_.SetRedoCallback([this]() { PRINT_IF_ERROR(Redo()); });
  toolset_.SetPaletteToggleCallback([this]() { palette_showing_ = !palette_showing_; });

  is_loaded_ = true;
  return absl::OkStatus();
}

absl::Status DungeonEditor::Update() {
  if (refresh_graphics_) {
    RETURN_IF_ERROR(RefreshGraphics());
    refresh_graphics_ = false;
  }

  status_ = UpdateDungeonRoomView();

  return absl::OkStatus();
}

absl::Status DungeonEditor::Undo() {
  if (dungeon_editor_system_) {
    return dungeon_editor_system_->Undo();
  }
  return absl::UnimplementedError("Undo not available");
}

absl::Status DungeonEditor::Redo() {
  if (dungeon_editor_system_) {
    return dungeon_editor_system_->Redo();
  }
  return absl::UnimplementedError("Redo not available");
}

absl::Status DungeonEditor::Save() {
  if (dungeon_editor_system_) {
    return dungeon_editor_system_->SaveDungeon();
  }
  return absl::UnimplementedError("Save not available");
}

absl::Status DungeonEditor::RefreshGraphics() {
  std::for_each_n(
      rooms_[current_room_id_].blocks().begin(), 8, [this](int block) {
        gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
            current_palette_group_[current_palette_id_], 0);
        Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
      });

  auto sprites_aux1_pal_group = rom()->palette_group().sprites_aux1;
  std::for_each_n(
      rooms_[current_room_id_].blocks().begin() + 8, 8,
      [this, &sprites_aux1_pal_group](int block) {
        gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
            sprites_aux1_pal_group[current_palette_id_], 0);
        Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
      });
  return absl::OkStatus();
}

// LoadDungeonRoomSize moved to DungeonRoomLoader component

absl::Status DungeonEditor::UpdateDungeonRoomView() {
  toolset_.Draw();

  if (palette_showing_) {
    ImGui::Begin("Palette Editor", &palette_showing_, 0);
    auto dungeon_main_pal_group = rom()->palette_group().dungeon_main;
    current_palette_ = dungeon_main_pal_group[current_palette_group_id_];
    gui::SelectablePalettePipeline(current_palette_id_, refresh_graphics_,
                                   current_palette_);
    ImGui::End();
  }

  // Correct 3-column layout as specified
  if (BeginTable("#DungeonEditTable", 3, kDungeonTableFlags, ImVec2(0, 0))) {
    TableSetupColumn("Room/Entrance Selector", ImGuiTableColumnFlags_WidthFixed,
                     250);
    TableSetupColumn("Canvas & Properties", ImGuiTableColumnFlags_WidthStretch);
    TableSetupColumn("Object Selector/Editor", ImGuiTableColumnFlags_WidthFixed,
                     300);
    TableHeadersRow();
    TableNextRow();

    // Column 1: Room and Entrance Selector (unchanged)
    TableNextColumn();
    room_selector_.Draw();

    // Column 2: Canvas and room properties with tabs
    TableNextColumn();
    DrawCanvasAndPropertiesPanel();

    // Column 3: Object selector, room graphics, and object editor
    TableNextColumn();
    object_selector_.Draw();

    ImGui::EndTable();
  }
  return absl::OkStatus();
}

void DungeonEditor::OnRoomSelected(int room_id) {
  // Update current room ID
  current_room_id_ = room_id;

  // Check if room is already open in a tab
  int existing_tab_index = -1;
  for (int i = 0; i < active_rooms_.Size; i++) {
    if (active_rooms_[i] == room_id) {
      existing_tab_index = i;
      break;
    }
  }

  if (existing_tab_index >= 0) {
    // Room is already open, switch to that tab
    current_active_room_tab_ = existing_tab_index;
  } else {
    // Room is not open, add it as a new tab
    active_rooms_.push_back(room_id);
    current_active_room_tab_ = active_rooms_.Size - 1;
  }

  // Update the room selector's active rooms list
  room_selector_.set_active_rooms(active_rooms_);
}

// DrawToolset() method moved to DungeonToolset component

void DungeonEditor::DrawCanvasAndPropertiesPanel() {
  if (ImGui::BeginTabBar("CanvasPropertiesTabBar")) {
    // Canvas tab - main editing view
    if (ImGui::BeginTabItem("Canvas")) {
      DrawDungeonTabView();
      ImGui::EndTabItem();
    }

    // Room Properties tab - debug and editing controls
    if (ImGui::BeginTabItem("Room Properties")) {
      if (ImGui::Button("Room Debug Info")) {
        ImGui::OpenPopup("RoomDebugPopup");
      }

      // Room properties popup
      if (ImGui::BeginPopup("RoomDebugPopup")) {
        DrawRoomPropertiesDebugPopup();
        ImGui::EndPopup();
      }

      // Quick room info display
      int current_room = current_room_id_;
      if (!active_rooms_.empty() &&
          current_active_room_tab_ < active_rooms_.Size) {
        current_room = active_rooms_[current_active_room_tab_];
      }

      if (current_room >= 0 && current_room < rooms_.size()) {
        auto& room = rooms_[current_room];

        ImGui::Text("Current Room: %03X (%d)", current_room, current_room);
        ImGui::Text("Objects: %zu", room.GetTileObjects().size());
        ImGui::Text("Sprites: %zu", room.GetSprites().size());
        ImGui::Text("Chests: %zu", room.GetChests().size());

        // Selection info
        const auto& selected_indices = object_interaction_.GetSelectedObjectIndices();
        if (!selected_indices.empty()) {
          ImGui::Separator();
          ImGui::Text("Selected Objects: %zu", selected_indices.size());
          if (ImGui::Button("Clear Selection")) {
            object_interaction_.ClearSelection();
          }
        }

        ImGui::Separator();

        // Quick edit controls
        gui::InputHexByte("Layout", &room.layout);
        gui::InputHexByte("Blockset", &room.blockset);
        gui::InputHexByte("Spriteset", &room.spriteset);
        gui::InputHexByte("Palette", &room.palette);

        if (ImGui::Button("Reload Room Graphics")) {
          (void)LoadAndRenderRoomGraphics(current_room);
        }
      }

      ImGui::EndTabItem();
    }

    ImGui::EndTabBar();
  }
}

void DungeonEditor::DrawRoomPropertiesDebugPopup() {
  int current_room = current_room_id_;
  if (!active_rooms_.empty() && current_active_room_tab_ < active_rooms_.Size) {
    current_room = active_rooms_[current_active_room_tab_];
  }

  if (current_room < 0 || current_room >= rooms_.size()) {
    ImGui::Text("Invalid room");
    return;
  }

  auto& room = rooms_[current_room];

  ImGui::Text("Room %03X Debug Information", current_room);
  ImGui::Separator();

  // Room properties table
  if (ImGui::BeginTable("RoomPropertiesPopup", 2,
                        ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
    ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 120);
    ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
    ImGui::TableHeadersRow();

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Room ID");
    ImGui::TableNextColumn();
    ImGui::Text("%03X (%d)", current_room, current_room);

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Layout");
    ImGui::TableNextColumn();
    gui::InputHexByte("##layout", &room.layout);

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Blockset");
    ImGui::TableNextColumn();
    gui::InputHexByte("##blockset", &room.blockset);

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Spriteset");
    ImGui::TableNextColumn();
    gui::InputHexByte("##spriteset", &room.spriteset);

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Palette");
    ImGui::TableNextColumn();
    gui::InputHexByte("##palette", &room.palette);

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Floor 1");
    ImGui::TableNextColumn();
    gui::InputHexByte("##floor1", &room.floor1);

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Floor 2");
    ImGui::TableNextColumn();
    gui::InputHexByte("##floor2", &room.floor2);

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Message ID");
    ImGui::TableNextColumn();
    gui::InputHexWord("##message_id", &room.message_id_);

    ImGui::EndTable();
  }

  ImGui::Separator();

  // Object statistics
  ImGui::Text("Object Statistics:");
  ImGui::Text("Total Objects: %zu", room.GetTileObjects().size());
  ImGui::Text("Layout Objects: %zu", room.GetLayout().GetObjects().size());
  ImGui::Text("Sprites: %zu", room.GetSprites().size());
  ImGui::Text("Chests: %zu", room.GetChests().size());

  ImGui::Separator();

  if (ImGui::Button("Reload Objects")) {
    room.LoadObjects();
  }
  ImGui::SameLine();
    if (ImGui::Button("Clear Cache")) {
      renderer_.ClearObjectCache();
    }
  ImGui::SameLine();
  if (ImGui::Button("Close")) {
    ImGui::CloseCurrentPopup();
  }
}

void DungeonEditor::DrawDungeonTabView() {
  static int next_tab_id = 0;

  if (BeginTabBar("MyTabBar", kDungeonTabBarFlags)) {
    if (ImGui::TabItemButton(ICON_MD_ADD, kDungeonTabFlags)) {
      if (std::find(active_rooms_.begin(), active_rooms_.end(),
                    current_room_id_) != active_rooms_.end()) {
        // Room is already open
        next_tab_id++;
      }
      active_rooms_.push_back(next_tab_id++);  // Add new tab
    }

    // Submit our regular tabs
    for (int n = 0; n < active_rooms_.Size;) {
      bool open = true;

      if (active_rooms_[n] > sizeof(zelda3::kRoomNames) / 4) {
        active_rooms_.erase(active_rooms_.Data + n);
        continue;
      }

      if (BeginTabItem(zelda3::kRoomNames[active_rooms_[n]].data(), &open,
                       ImGuiTabItemFlags_None)) {
        current_active_room_tab_ = n;  // Track which tab is currently active
        DrawDungeonCanvas(active_rooms_[n]);
        EndTabItem();
      }

      if (!open)
        active_rooms_.erase(active_rooms_.Data + n);
      else
        n++;
    }

    EndTabBar();
  }
  ImGui::Separator();
}

void DungeonEditor::DrawDungeonCanvas(int room_id) {
  // Validate room_id and ROM
  if (room_id < 0 || room_id >= rooms_.size()) {
    ImGui::Text("Invalid room ID: %d", room_id);
    return;
  }

  if (!rom_ || !rom_->is_loaded()) {
    ImGui::Text("ROM not loaded");
    return;
  }

  ImGui::BeginGroup();

  gui::InputHexByte("Layout", &rooms_[room_id].layout);
  SameLine();

  gui::InputHexByte("Blockset", &rooms_[room_id].blockset);
  SameLine();

  gui::InputHexByte("Spriteset", &rooms_[room_id].spriteset);
  SameLine();

  gui::InputHexByte("Palette", &rooms_[room_id].palette);

  gui::InputHexByte("Floor1", &rooms_[room_id].floor1);
  SameLine();

  gui::InputHexByte("Floor2", &rooms_[room_id].floor2);
  SameLine();

  gui::InputHexWord("Message ID", &rooms_[room_id].message_id_);
  SameLine();

  if (Button("Load Room Graphics")) {
    (void)LoadAndRenderRoomGraphics(room_id);
  }

  ImGui::SameLine();
  if (ImGui::Button("Reload All Graphics")) {
    (void)ReloadAllRoomGraphics();
  }

  // Debug and control popup
  static bool show_debug_popup = false;
  if (ImGui::Button("Room Debug Info")) {
    show_debug_popup = true;
  }

  if (show_debug_popup) {
    ImGui::OpenPopup("Room Debug Info");
    show_debug_popup = false;
  }

  if (ImGui::BeginPopupModal("Room Debug Info", nullptr,
                             ImGuiWindowFlags_AlwaysAutoResize)) {
    static bool show_objects = false;
    ImGui::Checkbox("Show Object Outlines", &show_objects);

    static bool render_objects = true;
    ImGui::Checkbox("Render Objects", &render_objects);

    static bool show_object_info = false;
    ImGui::Checkbox("Show Object Info", &show_object_info);

    static bool show_layout_objects = false;
    ImGui::Checkbox("Show Layout Objects", &show_layout_objects);

    if (ImGui::Button("Clear Object Cache")) {
      renderer_.ClearObjectCache();
    }

    ImGui::SameLine();
    ImGui::Text("Cache: %zu objects", renderer_.GetCacheSize());

    // Object statistics and metadata
    ImGui::Separator();
    ImGui::Text("Room Statistics:");
    ImGui::Text("Objects: %zu", rooms_[room_id].GetTileObjects().size());
    ImGui::Text("Layout Objects: %zu",
                rooms_[room_id].GetLayout().GetObjects().size());
    ImGui::Text("Sprites: %llu", static_cast<unsigned long long>(
                                     rooms_[room_id].GetSprites().size()));
    ImGui::Text("Chests: %zu", rooms_[room_id].GetChests().size());

    // Palette information
    ImGui::Text("Current Palette Group: %llu",
                static_cast<unsigned long long>(current_palette_group_id_));
    ImGui::Text("Cache Size: %zu objects", renderer_.GetCacheSize());

    // Object type breakdown
    ImGui::Separator();
    ImGui::Text("Object Type Breakdown:");
    std::map<int, int> object_type_counts;
    for (const auto& obj : rooms_[room_id].GetTileObjects()) {
      object_type_counts[obj.id_]++;
    }
    for (const auto& [type, count] : object_type_counts) {
      ImGui::Text("Type 0x%02X: %d objects", type, count);
    }

    // Layout object type breakdown
    ImGui::Separator();
    ImGui::Text("Layout Object Types:");
    auto walls = rooms_[room_id].GetLayout().GetObjectsByType(
        zelda3::RoomLayoutObject::Type::kWall);
    auto floors = rooms_[room_id].GetLayout().GetObjectsByType(
        zelda3::RoomLayoutObject::Type::kFloor);
    auto doors = rooms_[room_id].GetLayout().GetObjectsByType(
        zelda3::RoomLayoutObject::Type::kDoor);
    ImGui::Text("Walls: %zu", walls.size());
    ImGui::Text("Floors: %zu", floors.size());
    ImGui::Text("Doors: %zu", doors.size());

    // Object selection and editing
    static int selected_object_id = -1;
    if (ImGui::Button("Select Object")) {
      // This would open an object selection dialog
      // For now, just cycle through objects
      if (!rooms_[room_id].GetTileObjects().empty()) {
        selected_object_id =
            (selected_object_id + 1) % rooms_[room_id].GetTileObjects().size();
      }
    }

    if (selected_object_id >= 0 &&
        selected_object_id < (int)rooms_[room_id].GetTileObjects().size()) {
      const auto& selected_obj =
          rooms_[room_id].GetTileObjects()[selected_object_id];
      ImGui::Separator();
      ImGui::Text("Selected Object:");
      ImGui::Text("ID: 0x%02X", selected_obj.id_);
      ImGui::Text("Position: (%d, %d)", selected_obj.x_, selected_obj.y_);
      ImGui::Text("Size: 0x%02X", selected_obj.size_);
      ImGui::Text("Layer: %d", static_cast<int>(selected_obj.layer_));
      ImGui::Text("Tile Count: %d", selected_obj.GetTileCount());

      // Object editing controls
      if (ImGui::Button("Edit Object")) {
        // This would open an object editing dialog
      }
      ImGui::SameLine();
      if (ImGui::Button("Delete Object")) {
        // This would remove the object from the room
      }
    }

    if (ImGui::Button("Close")) {
      ImGui::CloseCurrentPopup();
    }
    ImGui::EndPopup();
  }

  ImGui::EndGroup();

  canvas_.DrawBackground();
  canvas_.DrawContextMenu();

  // Handle object selection and placement using component
  object_interaction_.CheckForObjectSelection();

  // Handle mouse input for drag and select functionality  
  object_interaction_.HandleCanvasMouseInput();

  // Update preview object position based on mouse cursor
  if (object_loaded_ && preview_object_.id_ >= 0 && canvas_.IsMouseHovering()) {
    const ImGuiIO& io = ImGui::GetIO();
    ImVec2 mouse_pos = io.MousePos;
    ImVec2 canvas_pos = canvas_.zero_point();
    ImVec2 canvas_mouse_pos =
        ImVec2(mouse_pos.x - canvas_pos.x, mouse_pos.y - canvas_pos.y);
    auto [room_x, room_y] =
         object_interaction_.CanvasToRoomCoordinates(static_cast<int>(canvas_mouse_pos.x),
                                                     static_cast<int>(canvas_mouse_pos.y));
    preview_object_.x_ = room_x;
    preview_object_.y_ = room_y;
  }

  if (is_loaded_) {
    // Automatically load room graphics if not already loaded
    if (rooms_[room_id].blocks().empty()) {
      (void)LoadAndRenderRoomGraphics(room_id);
    }

    // Load room objects if not already loaded
    if (rooms_[room_id].GetTileObjects().empty()) {
      rooms_[room_id].LoadObjects();
    }

    // Render background layers with proper positioning
    renderer_.RenderRoomBackgroundLayers(room_id);

    // Render room objects and sprites with improved graphics
    if (current_palette_id_ < current_palette_group_.size()) {
      auto room_palette = current_palette_group_[current_palette_id_];
      
      // Render regular objects with improved fallback
      for (const auto& object : rooms_[room_id].GetTileObjects()) {
        renderer_.RenderObjectInCanvas(object, room_palette);
      }
      
      // Render sprites as simple 16x16 squares with labels
      renderer_.RenderSprites(rooms_[room_id]);
    }
  }

  // Draw selection box and drag preview using component
  object_interaction_.DrawSelectBox();
  object_interaction_.DrawDragPreview();

  canvas_.DrawGrid();
  canvas_.DrawOverlay();
}

// Legacy method implementations that delegate to components
absl::Status DungeonEditor::LoadAndRenderRoomGraphics(int room_id) {
  if (room_id < 0 || room_id >= rooms_.size()) {
    return absl::InvalidArgumentError("Invalid room ID");
  }
  return room_loader_.LoadAndRenderRoomGraphics(room_id, rooms_[room_id]);
}

absl::Status DungeonEditor::ReloadAllRoomGraphics() {
  return room_loader_.ReloadAllRoomGraphics(rooms_);
}

absl::Status DungeonEditor::UpdateRoomBackgroundLayers(int room_id) {
  // This method is deprecated - rendering is handled by DungeonRenderer component
  return absl::OkStatus();
}

}  // namespace yaze::editor
