#include "lua/game_engine.h"

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

#include <wx/log.h>

namespace wherigo {

// Define the custom event
wxDEFINE_EVENT(EVT_GAME_STATE_CHANGED, GameStateEvent);

wxBEGIN_EVENT_TABLE(GameEngine, wxEvtHandler)
  EVT_TIMER(wxID_ANY, GameEngine::onGameTick)
wxEND_EVENT_TABLE()

GameEngine& GameEngine::getInstance() {
  static GameEngine instance;
  return instance;
}

GameEngine::GameEngine() : m_gameTimer(this) {
}

GameEngine::~GameEngine() {
  shutdown();
}

void GameEngine::init(lua_State *L, int cartridgeRef) {
  m_luaState = L;
  m_cartridgeRef = cartridgeRef;
  wxLogDebug("GameEngine initialized");
}

void GameEngine::shutdown() {
  stop();
  m_luaState = nullptr;
  m_cartridgeRef = -1;
}

void GameEngine::start() {
  if (m_running) return;

  m_running = true;
  m_gameTimer.Start(1000); // 1 second tick
  wxLogDebug("GameEngine started");
}

void GameEngine::stop() {
  if (!m_running) return;

  m_gameTimer.Stop();
  m_running = false;
  wxLogDebug("GameEngine stopped");
}

void GameEngine::updatePlayerPosition(double lat, double lng, double alt) {
  m_playerLat = lat;
  m_playerLng = lng;
  m_playerAlt = alt;

  if (!m_luaState) return;

  // Update Player.ObjectLocation in Lua
  lua_getglobal(m_luaState, "Player");
  if (lua_istable(m_luaState, -1)) {
    lua_newtable(m_luaState);
    lua_pushnumber(m_luaState, lat);
    lua_setfield(m_luaState, -2, "latitude");
    lua_pushnumber(m_luaState, lng);
    lua_setfield(m_luaState, -2, "longitude");
    lua_pushnumber(m_luaState, alt);
    lua_setfield(m_luaState, -2, "altitude");
    lua_setfield(m_luaState, -2, "ObjectLocation");
  }
  lua_pop(m_luaState, 1);

  checkZones();
}

void GameEngine::onGameTick(wxTimerEvent& event) {
  if (!m_luaState || !m_running) return;

  checkTimers();

  // Notify listeners of potential state changes
  notifyStateChanged();
}

void GameEngine::checkTimers() {
  if (!m_luaState) return;

  // Iterate through all global variables to find running timers
  lua_getglobal(m_luaState, "_G");
  lua_pushnil(m_luaState);

  while (lua_next(m_luaState, -2) != 0) {
    if (lua_istable(m_luaState, -1)) {
      lua_getfield(m_luaState, -1, "ClassName");
      if (lua_isstring(m_luaState, -1) && strcmp(lua_tostring(m_luaState, -1), "ZTimer") == 0) {
        lua_pop(m_luaState, 1); // pop ClassName

        lua_getfield(m_luaState, -1, "Running");
        bool running = lua_toboolean(m_luaState, -1);
        lua_pop(m_luaState, 1);

        if (running) {
          // Get timer info
          lua_getfield(m_luaState, -1, "Name");
          const char *name = lua_isstring(m_luaState, -1) ? lua_tostring(m_luaState, -1) : "(unnamed)";
          lua_pop(m_luaState, 1);

          lua_getfield(m_luaState, -1, "Remaining");
          lua_Number remaining = lua_isnumber(m_luaState, -1) ? lua_tonumber(m_luaState, -1) : -1;
          lua_pop(m_luaState, 1);

          // If Remaining not set, initialize from Duration
          if (remaining < 0) {
            lua_getfield(m_luaState, -1, "Duration");
            remaining = lua_isnumber(m_luaState, -1) ? lua_tonumber(m_luaState, -1) : 0;
            lua_pop(m_luaState, 1);
          }

          // Decrement remaining time
          remaining -= 1.0;
          lua_pushnumber(m_luaState, remaining);
          lua_setfield(m_luaState, -2, "Remaining");

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

          // Check if elapsed
          if (remaining <= 0) {
            wxLogDebug("Timer elapsed: %s", name);

            // Stop the timer
            lua_pushboolean(m_luaState, 0);
            lua_setfield(m_luaState, -2, "Running");

            // Call OnElapsed if exists
            lua_getfield(m_luaState, -1, "OnElapsed");
            if (lua_isfunction(m_luaState, -1)) {
              lua_pushvalue(m_luaState, -2); // push timer as self
              if (lua_pcall(m_luaState, 1, 0, 0) != 0) {
                wxLogError("Timer OnElapsed error: %s", lua_tostring(m_luaState, -1));
                lua_pop(m_luaState, 1);
              }
            } else {
              lua_pop(m_luaState, 1);
            }
          }
        }
      } else {
        lua_pop(m_luaState, 1); // pop ClassName
      }
    }
    lua_pop(m_luaState, 1); // pop value
  }
  lua_pop(m_luaState, 1); // pop _G
}

void GameEngine::checkZones() {
  if (!m_luaState) return;

  // Iterate through all global variables to find zones
  lua_getglobal(m_luaState, "_G");
  lua_pushnil(m_luaState);

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

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

        if (active) {
          // TODO: Check if player is inside zone using Points
          // For now, just log that we would check
          lua_getfield(m_luaState, -1, "Name");
          const char *name = lua_isstring(m_luaState, -1) ? lua_tostring(m_luaState, -1) : "(unnamed)";
          lua_pop(m_luaState, 1);

          // This is where you would implement point-in-polygon check
          // and call OnEnter/OnExit callbacks
        }
      } else {
        lua_pop(m_luaState, 1); // pop ClassName
      }
    }
    lua_pop(m_luaState, 1); // pop value
  }
  lua_pop(m_luaState, 1); // pop _G
}

void GameEngine::notifyStateChanged() {
  GameStateEvent event(EVT_GAME_STATE_CHANGED);
  ProcessEvent(event);
}

} // namespace wherigo

