#include "app.h"
#include "lua/game_engine.h"
#include "ui/game_screen.h"
#include "ui/wherigo_dialog.h"
#include <cartridge/cartridge.h>

extern "C" {
#include <lua.h>
}

wxDECLARE_APP(cApp);

enum {
  ID_SaveGame = 2000,
  ID_LoadGame = 2001,
  ID_ExportCompletion = 2002,
  ID_MapView = 2003
};

cGameScreen::cGameScreen(wxWindow *parent)
    : wxFrame(parent, wxID_ANY, "Wherigo Player", wxDefaultPosition, wxSize(800, 800)) {

  // Menu
  auto *menuFile = new wxMenu;
  menuFile->Append(ID_SaveGame, "Spielstand speichern\tCtrl+S");
  menuFile->Append(ID_LoadGame, "Spielstand laden\tCtrl+L");
  menuFile->AppendSeparator();
  menuFile->Append(ID_MapView, "Karten-Simulation..."); // Menüpunkt für Simulation
  menuFile->AppendSeparator();
  menuFile->Append(ID_ExportCompletion, "Completion Log exportieren\tCtrl+E");
  menuFile->AppendSeparator();
  menuFile->Append(wxID_EXIT, "Spiel beenden");

  auto *menuHelp = new wxMenu;
  menuHelp->Append(wxID_ABOUT);

  auto *menuBar = new wxMenuBar;
  menuBar->Append(menuFile, "&Datei");
  menuBar->Append(menuHelp, "&Hilfe");
  wxFrameBase::SetMenuBar(menuBar);

  // Main sizer
  auto *mainSizer = new wxBoxSizer(wxVERTICAL);

  // Notebook with tabs
  m_notebook = new wxNotebook(this, wxID_ANY);

  // Tasks tab
  auto *taskPanel = new wxPanel(m_notebook);
  auto *taskSizer = new wxBoxSizer(wxVERTICAL);
  taskSizer->Add(new wxStaticText(taskPanel, wxID_ANY, "Aktive Aufgaben:"), 0, wxALL, 5);
  m_taskList = new wxListBox(taskPanel, wxID_ANY);
  taskSizer->Add(m_taskList, 1, wxALL | wxEXPAND, 5);
  taskPanel->SetSizer(taskSizer);
  m_notebook->AddPage(taskPanel, "Aufgaben");

  // Locations/Zones tab
  auto *zonePanel = new wxPanel(m_notebook);
  auto *zoneSizer = new wxBoxSizer(wxVERTICAL);
  zoneSizer->Add(new wxStaticText(zonePanel, wxID_ANY, "Orte in der Nähe:"), 0, wxALL, 5);
  m_zoneList = new wxListBox(zonePanel, wxID_ANY);
  zoneSizer->Add(m_zoneList, 1, wxALL | wxEXPAND, 5);
  zonePanel->SetSizer(zoneSizer);
  m_notebook->AddPage(zonePanel, "Orte");

  // Inventory tab
  auto *inventoryPanel = new wxPanel(m_notebook);
  auto *inventorySizer = new wxBoxSizer(wxVERTICAL);
  inventorySizer->Add(new wxStaticText(inventoryPanel, wxID_ANY, "Dein Inventar:"), 0, wxALL, 5);
  m_inventoryList = new wxListBox(inventoryPanel, wxID_ANY);
  inventorySizer->Add(m_inventoryList, 1, wxALL | wxEXPAND, 5);
  inventoryPanel->SetSizer(inventorySizer);
  m_notebook->AddPage(inventoryPanel, "Inventar");

  // Characters tab
  auto *characterPanel = new wxPanel(m_notebook);
  auto *characterSizer = new wxBoxSizer(wxVERTICAL);
  characterSizer->Add(new wxStaticText(characterPanel, wxID_ANY, "Personen in der Nähe:"), 0, wxALL, 5);
  m_characterList = new wxListBox(characterPanel, wxID_ANY);
  characterSizer->Add(m_characterList, 1, wxALL | wxEXPAND, 5);
  characterPanel->SetSizer(characterSizer);
  m_notebook->AddPage(characterPanel, "Personen");

  // Items tab
  auto *itemPanel = new wxPanel(m_notebook);
  auto *itemSizer = new wxBoxSizer(wxVERTICAL);
  itemSizer->Add(new wxStaticText(itemPanel, wxID_ANY, "Gegenstände in der Nähe:"), 0, wxALL, 5);
  m_itemList = new wxListBox(itemPanel, wxID_ANY);
  itemSizer->Add(m_itemList, 1, wxALL | wxEXPAND, 5);
  itemPanel->SetSizer(itemSizer);
  m_notebook->AddPage(itemPanel, "Gegenstände");

  mainSizer->Add(m_notebook, 1, wxALL | wxEXPAND, 5);

  SetSizer(mainSizer);

  CentreOnScreen();

  // Status bar
  wxFrameBase::CreateStatusBar();
  wxFrameBase::SetStatusText("Spiel läuft");

  // Event bindings
  Bind(wxEVT_CLOSE_WINDOW, &cGameScreen::OnClose, this);
  Bind(wxEVT_MENU, &cGameScreen::OnAbout, this, wxID_ABOUT);
  Bind(wxEVT_MENU, &cGameScreen::OnExit, this, wxID_EXIT);
  Bind(wxEVT_MENU, &cGameScreen::OnSaveGame, this, ID_SaveGame);
  Bind(wxEVT_MENU, &cGameScreen::OnLoadGame, this, ID_LoadGame);
  Bind(wxEVT_MENU, &cGameScreen::OnExportCompletion, this, ID_ExportCompletion);
  Bind(wxEVT_MENU, &cGameScreen::OnMapSim, this, ID_MapView);

  m_zoneList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnZoneSelected, this);
  m_taskList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnTaskSelected, this);
  m_inventoryList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnInventorySelected, this);
  m_characterList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnCharacterSelected, this);
  m_itemList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnItemSelected, this);

  // Listen to game state changes from GameEngine
  wherigo::GameEngine::getInstance().Bind(wherigo::EVT_GAME_STATE_CHANGED,
                                           &cGameScreen::OnGameStateChanged, this);

  // Initial UI refresh
  CallAfter([this]() { refreshUI(); });
}

void cGameScreen::OnClose(wxCloseEvent &event) {
  // Unbind from game engine events
  wherigo::GameEngine::getInstance().Unbind(wherigo::EVT_GAME_STATE_CHANGED,
                                             &cGameScreen::OnGameStateChanged, this);

  // Stop the game engine but don't unload cartridge
  wherigo::GameEngine::getInstance().stop();

  // Hide instead of destroy, parent will handle visibility
  Hide();

  // Notify parent that game was closed
  if (GetParent()) {
    GetParent()->Show();
  }
}

void cGameScreen::OnExit(wxCommandEvent &event) {
  Close();
}

void cGameScreen::OnAbout(wxCommandEvent &event) {
  wxMessageBox("Wherigo Player\nEin Wherigo-Cartridge-Player für Desktop",
               "Über Wherigo Player", wxOK | wxICON_INFORMATION);
}

void cGameScreen::OnSaveGame(wxCommandEvent& event) {
  if (wxGetApp().saveGameState("")) {
    wxMessageBox("Spielstand wurde gespeichert!", "Speichern", wxOK | wxICON_INFORMATION);
  } else {
    wxMessageBox("Fehler beim Speichern des Spielstands!", "Fehler", wxOK | wxICON_ERROR);
  }
}

void cGameScreen::OnLoadGame(wxCommandEvent& event) {
  const int result = wxMessageBox("Möchten Sie den gespeicherten Spielstand laden?\n\nAchtung: Der aktuelle Fortschritt geht verloren!",
                            "Laden", wxYES_NO | wxICON_QUESTION);

  if (result == wxYES) {
    if (wxGetApp().loadGameState("")) {
      wxMessageBox("Spielstand wurde geladen!", "Laden", wxOK | wxICON_INFORMATION);
      refreshUI();
    } else {
      wxMessageBox("Kein gespeicherter Spielstand gefunden oder Fehler beim Laden!", "Fehler", wxOK | wxICON_ERROR);
    }
  }
}

void cGameScreen::OnExportCompletion(wxCommandEvent& event) {
  wxFileDialog saveDialog(this, "Completion Log speichern", "", "",
                          "Wherigo Game Log (*.gwl)|*.gwl",
                          wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

  std::string defaultPath = wxGetApp().getCompletionLogPath();
  if (!defaultPath.empty()) {
    saveDialog.SetPath(defaultPath);
  }

  if (saveDialog.ShowModal() == wxID_CANCEL) {
    return;
  }

  if (const wxString filePath = saveDialog.GetPath();
      wxGetApp().generateCompletionLog(filePath.ToStdString())) {
    wxMessageBox("Completion Log wurde erfolgreich exportiert!\n\nDiese Datei kann auf wherigo.com hochgeladen werden, um den Abschluss nachzuweisen.",
                 "Export erfolgreich", wxOK | wxICON_INFORMATION);
  } else {
    wxMessageBox("Fehler beim Exportieren des Completion Logs!", "Fehler", wxOK | wxICON_ERROR);
  }
}

void cGameScreen::OnGameStateChanged(wxEvent& event) {
  // Game state has changed, refresh UI
  refreshUI();
}

void cGameScreen::refreshUI() {
  if (wxGetApp().isCartridgeLoaded())
    SetTitle(wxString::FromUTF8(wxGetApp().getCartridge()->cartridgeName()));

  // Freeze to prevent flickering and focus loss
  Freeze();

  populateZones();
  populateTasks();
  populateInventory();
  populateCharacters();
  populateItems();

  Thaw();
}

void cGameScreen::populateZones() {
  const int selection = m_zoneList->GetSelection();
  const wxString selectedItem = (selection != wxNOT_FOUND) ? m_zoneList->GetString(selection) : "";

  m_zoneList->Clear();

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "Zone") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Active");
        const bool active = lua_toboolean(L, -1);
        lua_pop(L, 1);

        lua_getfield(L, -1, "Visible");
        const bool visible = lua_toboolean(L, -1);
        lua_pop(L, 1);

        if (active && visible) {
          lua_getfield(L, -1, "Name");
          if (lua_isstring(L, -1)) {
            m_zoneList->Append(wxString::FromUTF8(lua_tostring(L, -1)));
          }
          lua_pop(L, 1);
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1);
  }
  lua_pop(L, 1);

  // Restore selection
  if (!selectedItem.IsEmpty()) {
    if (const int idx = m_zoneList->FindString(selectedItem); idx != wxNOT_FOUND) {
      m_zoneList->SetSelection(idx);
    }
  }
}

void cGameScreen::populateTasks() {
  const int selection = m_taskList->GetSelection();
  const wxString selectedItem = (selection != wxNOT_FOUND) ? m_taskList->GetString(selection) : "";

  m_taskList->Clear();

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZTask") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Active");
        bool active = lua_toboolean(L, -1);
        lua_pop(L, 1);

        lua_getfield(L, -1, "Complete");
        const bool complete = lua_toboolean(L, -1);
        lua_pop(L, 1);

        if (active && !complete) {
          lua_getfield(L, -1, "Name");
          if (lua_isstring(L, -1)) {
            m_taskList->Append(wxString::FromUTF8(lua_tostring(L, -1)));
          }
          lua_pop(L, 1);
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1);
  }
  lua_pop(L, 1);

  // Restore selection
  if (!selectedItem.IsEmpty()) {
    if (const int idx = m_taskList->FindString(selectedItem); idx != wxNOT_FOUND) {
      m_taskList->SetSelection(idx);
    }
  }
}

void cGameScreen::populateInventory() {
  const int selection = m_inventoryList->GetSelection();
  const wxString selectedItem = (selection != wxNOT_FOUND) ? m_inventoryList->GetString(selection) : "";

  m_inventoryList->Clear();

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  // Get Player
  lua_getglobal(L, "Player");
  if (!lua_istable(L, -1)) {
    lua_pop(L, 1);
    return;
  }

  // Find all items where Container == Player
  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZItem") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Container");
        if (lua_istable(L, -1)) {
          // Check if Container is Player
          lua_getglobal(L, "Player");
          const bool isInInventory = lua_rawequal(L, -1, -2);
          lua_pop(L, 2); // pop Player and Container

          if (isInInventory) {
            lua_getfield(L, -1, "Visible");
            const bool visible = lua_toboolean(L, -1);
            lua_pop(L, 1);

            if (visible) {
              lua_getfield(L, -1, "Name");
              if (lua_isstring(L, -1)) {
                m_inventoryList->Append(wxString::FromUTF8(lua_tostring(L, -1)));
              }
              lua_pop(L, 1);
            }
          }
        } else {
          lua_pop(L, 1);
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1);
  }
  lua_pop(L, 2); // pop _G and Player

  // Restore selection
  if (!selectedItem.IsEmpty()) {
    if (const int idx = m_inventoryList->FindString(selectedItem);
        idx != wxNOT_FOUND) {
      m_inventoryList->SetSelection(idx);
    }
  }
}

void cGameScreen::populateCharacters() {
  const int selection = m_characterList->GetSelection();
  const wxString selectedItem = (selection != wxNOT_FOUND) ? m_characterList->GetString(selection) : "";

  m_characterList->Clear();

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZCharacter") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Active");
        const bool active = lua_toboolean(L, -1);
        lua_pop(L, 1);

        lua_getfield(L, -1, "Visible");
        const bool visible = lua_toboolean(L, -1);
        lua_pop(L, 1);

        // Check if character has a container (is somewhere)
        lua_getfield(L, -1, "Container");
        const bool hasLocation = lua_istable(L, -1);
        lua_pop(L, 1);

        if (active && visible && hasLocation) {
          lua_getfield(L, -1, "Name");
          wxString name;
          if (lua_isstring(L, -1)) {
            name = wxString::FromUTF8(lua_tostring(L, -1));
          }
          lua_pop(L, 1);

          // Get location name
          lua_getfield(L, -1, "Container");
          lua_getfield(L, -1, "Name");
          wxString location;
          if (lua_isstring(L, -1)) {
            location = wxString::FromUTF8(lua_tostring(L, -1));
          }
          lua_pop(L, 2);

          if (!name.IsEmpty()) {
            m_characterList->Append(wxString::Format("%s (%s)", name, location));
          }
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1);
  }
  lua_pop(L, 1);

  // Restore selection
  if (!selectedItem.IsEmpty()) {
    if (const int idx = m_characterList->FindString(selectedItem);
        idx != wxNOT_FOUND) {
      m_characterList->SetSelection(idx);
    }
  }
}

void cGameScreen::populateItems() {
  const int selection = m_itemList->GetSelection();
  const wxString selectedItem = (selection != wxNOT_FOUND) ? m_itemList->GetString(selection) : "";

  m_itemList->Clear();

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZItem") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Active");
        const bool active = lua_toboolean(L, -1);
        lua_pop(L, 1);

        lua_getfield(L, -1, "Visible");
        const bool visible = lua_toboolean(L, -1);
        lua_pop(L, 1);

        // Check if item is NOT in player inventory (in a zone)
        lua_getfield(L, -1, "Container");
        bool inZone = false;
        if (lua_istable(L, -1)) {
          lua_getfield(L, -1, "ClassName");
          if (lua_isstring(L, -1)) {
            const char *containerClass = lua_tostring(L, -1);
            inZone = (strcmp(containerClass, "Zone") == 0);
          }
          lua_pop(L, 1);
        }
        lua_pop(L, 1);

        if (active && visible && inZone) {
          lua_getfield(L, -1, "Name");
          wxString name;
          if (lua_isstring(L, -1)) {
            name = wxString::FromUTF8(lua_tostring(L, -1));
          }
          lua_pop(L, 1);

          // Get location name
          lua_getfield(L, -1, "Container");
          lua_getfield(L, -1, "Name");
          wxString location;
          if (lua_isstring(L, -1)) {
            location = wxString::FromUTF8(lua_tostring(L, -1));
          }
          lua_pop(L, 2);

          if (!name.IsEmpty()) {
            m_itemList->Append(wxString::Format("%s (%s)", name, location));
          }
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1);
  }
  lua_pop(L, 1);

  // Restore selection
  if (!selectedItem.IsEmpty()) {
    if (const int idx = m_itemList->FindString(selectedItem); idx != wxNOT_FOUND) {
      m_itemList->SetSelection(idx);
    }
  }
}

void cGameScreen::OnZoneSelected(wxCommandEvent& event) {
  int sel = m_zoneList->GetSelection();
  if (sel == wxNOT_FOUND) return;

  wxString zoneName = m_zoneList->GetString(sel);

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  // Find the zone object by name
  lua_getglobal(L, "_G");
  lua_pushnil(L);

  bool found = false;
  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "Zone") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Name");
        if (lua_isstring(L, -1)) {
          wxString currentName = wxString::FromUTF8(lua_tostring(L, -1));
          lua_pop(L, 1);

          if (currentName == zoneName) {
            found = true;

            // Zone Info mit Media anzeigen
            lua_getfield(L, -1, "Description");
            wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : "";
            lua_pop(L, 1);

            wxString mediaName;
            lua_getfield(L, -1, "Media");
            if (lua_istable(L, -1)) {
              lua_getfield(L, -1, "Name");
              if (lua_isstring(L, -1)) {
                mediaName = wxString::FromUTF8(lua_tostring(L, -1));
              }
              lua_pop(L, 1);
            }
            lua_pop(L, 1);

            if (!description.IsEmpty() || !mediaName.IsEmpty()) {
              std::vector<wxString> buttons = {"OK"};
              wherigo::WherigoMessageDialog dlg(this, description, zoneName, buttons, mediaName);
              dlg.ShowModal();
            }

            // Call OnEnter callback if it exists
            lua_getfield(L, -1, "OnEnter");
            if (lua_isfunction(L, -1)) {
              lua_pushvalue(L, -2); // push zone as self argument
              if (lua_pcall(L, 1, 0, 0) != 0) {
                wxLogError("Zone OnEnter error: %s", lua_tostring(L, -1));
                lua_pop(L, 1);
              }
            } else {
              lua_pop(L, 1);
            }

            lua_pop(L, 1); // pop zone table
            lua_pop(L, 1); // pop key
            break;
          }
        } else {
          lua_pop(L, 1);
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1);
  }
  lua_pop(L, 1);

  if (!found) {
    wxMessageBox(wxString::Format("Zone '%s' nicht gefunden", zoneName),
                 "Fehler", wxOK | wxICON_ERROR);
  }
}

void cGameScreen::OnTaskSelected(wxCommandEvent& event) {
  int sel = m_taskList->GetSelection();
  if (sel == wxNOT_FOUND) return;

  wxString taskName = m_taskList->GetString(sel);

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  // Find the task object by name
  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZTask") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Name");
        if (lua_isstring(L, -1)) {
          wxString currentName = wxString::FromUTF8(lua_tostring(L, -1));
          lua_pop(L, 1);

          if (currentName == taskName) {
            // Bestehende MessageBox mit Status und Beschreibung
            lua_getfield(L, -1, "Description");
            wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : "Keine Beschreibung verfügbar";
            lua_pop(L, 1);

            lua_getfield(L, -1, "Complete");
            bool complete = lua_toboolean(L, -1);
            lua_pop(L, 1);

            wxString status = complete ? "✓ Abgeschlossen" : "○ Offen";
            wxString message = wxString::Format("%s\n\n%s", status, description);

            wxMessageBox(message, taskName, wxOK | wxICON_INFORMATION);

            lua_pop(L, 1); // pop task table
            lua_pop(L, 1); // pop key
            break;
          }
        } else {
          lua_pop(L, 1);
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1); // pop value
  }
  lua_pop(L, 1); // pop _G
}

void cGameScreen::OnInventorySelected(wxCommandEvent& event) {
  const int sel = m_inventoryList->GetSelection();
  if (sel == wxNOT_FOUND) return;

  const wxString itemName = m_inventoryList->GetString(sel);

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  // Find the item object by name
  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZItem") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Name");
        if (lua_isstring(L, -1)) {
          wxString currentName = wxString::FromUTF8(lua_tostring(L, -1));
          lua_pop(L, 1);

          if (currentName == itemName) {

            // Call OnClick if exists
            lua_getfield(L, -1, "OnClick");
            if (lua_isfunction(L, -1)) {
              lua_pushvalue(L, -2); // push item as self
              if (lua_pcall(L, 1, 0, 0) != 0) {
                wxLogError("Item OnClick error: %s", lua_tostring(L, -1));
                lua_pop(L, 1);
              }
            } else {
              lua_pop(L, 1);
            }

            lua_pop(L, 1); // pop item table
            lua_pop(L, 1); // pop key
            break;
          }
        } else {
          lua_pop(L, 1);
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1); // pop value
  }
  lua_pop(L, 1); // pop _G
}

void cGameScreen::OnCharacterSelected(wxCommandEvent& event) {
  const int sel = m_characterList->GetSelection();
  if (sel == wxNOT_FOUND) return;

  // Extract name from "Name (Location)" format
  const wxString fullText = m_characterList->GetString(sel);
  const wxString charName = fullText.BeforeFirst('(').Trim();

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  // Find the character object by name
  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZCharacter") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Name");
        if (lua_isstring(L, -1)) {
          const wxString currentName = wxString::FromUTF8(lua_tostring(L, -1));
          lua_pop(L, 1);

          if (currentName == charName) {
            // Get character description
            lua_getfield(L, -1, "Description");
            const wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : ("Keine Beschreibung verfügbar");
            lua_pop(L, 1);

            // Get media if exists
            wxString mediaName;
            lua_getfield(L, -1, "Media");
            if (lua_istable(L, -1)) {
              lua_getfield(L, -1, "Name");
              if (lua_isstring(L, -1)) {
                mediaName = wxString::FromUTF8(lua_tostring(L, -1));
              }
              lua_pop(L, 1);
            }
            lua_pop(L, 1);

            // Show with media support
            std::vector<wxString> buttons = {"OK"};
            wherigo::WherigoMessageDialog dlg(this, description, charName, buttons, mediaName);
            dlg.ShowModal();

            // Call OnClick if exists
            lua_getfield(L, -1, "OnClick");
            if (lua_isfunction(L, -1)) {
              lua_pushvalue(L, -2); // push character as self
              if (lua_pcall(L, 1, 0, 0) != 0) {
                wxLogError("Character OnClick error: %s", lua_tostring(L, -1));
                lua_pop(L, 1);
              }
            } else {
              lua_pop(L, 1);
            }

            lua_pop(L, 1); // pop character table
            lua_pop(L, 1); // pop key
            break;
          }
        } else {
          lua_pop(L, 1);
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1); // pop value
  }
  lua_pop(L, 1); // pop _G
}

void cGameScreen::OnItemSelected(wxCommandEvent& event) {
  const int sel = m_itemList->GetSelection();
  if (sel == wxNOT_FOUND) return;

  // Extract name from "Name (Location)" format
  const wxString fullText = m_itemList->GetString(sel);
  const wxString itemName = fullText.BeforeFirst('(').Trim();

  lua_State *L = wxGetApp().getLuaState();
  if (!L) return;

  // Find the item object by name
  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZItem") == 0) {
        lua_pop(L, 1);

        lua_getfield(L, -1, "Name");
        if (lua_isstring(L, -1)) {
          const wxString currentName = wxString::FromUTF8(lua_tostring(L, -1));
          lua_pop(L, 1);

          if (currentName == itemName) {
            // Get item description
            lua_getfield(L, -1, "Description");
            const wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : ("Keine Beschreibung verfügbar");
            lua_pop(L, 1);

            // Get media if exists
            wxString mediaName;
            lua_getfield(L, -1, "Media");
            if (lua_istable(L, -1)) {
              lua_getfield(L, -1, "Name");
              if (lua_isstring(L, -1)) {
                mediaName = wxString::FromUTF8(lua_tostring(L, -1));
              }
              lua_pop(L, 1);
            }
            lua_pop(L, 1);

            // Check for commands
            lua_getfield(L, -1, "Commands");
            wxArrayString commands;
            if (lua_istable(L, -1)) {
              const auto n = lua_objlen(L, -1);
              for (int i = 1; i <= n; i++) {
                lua_rawgeti(L, -1, i);
                if (lua_istable(L, -1)) {
                  lua_getfield(L, -1, "Text");
                  if (lua_isstring(L, -1)) {
                    commands.Add(wxString::FromUTF8(lua_tostring(L, -1)));
                  }
                  lua_pop(L, 1);
                }
                lua_pop(L, 1);
              }
            }
            lua_pop(L, 1);

            wxString message = description;
            if (!commands.IsEmpty()) {
              message += ("\n\nVerfügbare Aktionen:\n");
              for (const auto& cmd : commands) {
                message += "• " + cmd + "\n";
              }
            }

            // Use WherigoMessageDialog to show with media support
            const std::vector<wxString> buttons = {"OK"};
            wherigo::WherigoMessageDialog dlg(this, message, itemName, buttons, mediaName);
            dlg.ShowModal();

            // Call OnClick if exists
            lua_getfield(L, -1, "OnClick");
            if (lua_isfunction(L, -1)) {
              lua_pushvalue(L, -2); // push item as self
              if (lua_pcall(L, 1, 0, 0) != 0) {
                wxLogError("Item OnClick error: %s", lua_tostring(L, -1));
                lua_pop(L, 1);
              }
            } else {
              lua_pop(L, 1);
            }

            lua_pop(L, 1); // pop item table
            lua_pop(L, 1); // pop key
            break;
          }
        } else {
          lua_pop(L, 1);
        }
      } else {
        lua_pop(L, 1);
      }
    }
    lua_pop(L, 1); // pop value
  }
  lua_pop(L, 1); // pop _G
}

void cGameScreen::OnMapSim(wxCommandEvent&) {
    std::vector<ZoneInfo> zoneInfos;
    double lat = 53.3, lon = 10.39;

    lua_State *L = wxGetApp().getLuaState();
    if (L) {
        lua_getglobal(L, "_G");
        lua_pushnil(L);
        while (lua_next(L, -2) != 0) {
            if (lua_istable(L, -1)) {
                lua_getfield(L, -1, "ClassName");
                if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "Zone") == 0) {
                    lua_pop(L, 1); // pop ClassName

                    lua_getfield(L, -1, "Active");
                    const bool active = lua_toboolean(L, -1);
                    lua_pop(L, 1);
                    if (!active) { lua_pop(L, 1); continue; }

                    // Read name
                    lua_getfield(L, -1, "Name");
                    std::string zoneName = lua_isstring(L, -1) ? lua_tostring(L, -1) : "";
                    lua_pop(L, 1);

                    // Read OriginalPoint (set by Wherigo Builder on each zone)
                    lua_getfield(L, -1, "OriginalPoint");
                    if (lua_istable(L, -1)) {
                        lua_getfield(L, -1, "latitude");
                        lua_getfield(L, -2, "longitude");
                        if (lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
                            zoneInfos.push_back({
                                lua_tonumber(L, -2),
                                lua_tonumber(L, -1),
                                zoneName
                            });
                        }
                        lua_pop(L, 2); // pop lat, lon
                    }
                    lua_pop(L, 1); // pop OriginalPoint (table or nil)
                } else {
                    lua_pop(L, 1); // pop ClassName
                }
            }
            lua_pop(L, 1); // pop value
        }
        lua_pop(L, 1); // pop _G
    }

    if (!zoneInfos.empty()) {
        lat = zoneInfos[0].lat;
        lon = zoneInfos[0].lon;
    }
    auto *mapSim = new MapSimFrame(this, lat, lon, zoneInfos);
    mapSim->Show();
}
