#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <algorithm>
#include <cstdio>
#include <limits>
#include <memory>
#include <frostbite2D/audio/audio_system.h>
#include <frostbite2D/base/object_registry.h>
#include <frostbite2D/core/application.h>
#include <frostbite2D/core/task_system.h>
#include <frostbite2D/event/event.h>
#include <frostbite2D/event/key_event.h>
#include <frostbite2D/event/mouse_event.h>
#include <frostbite2D/event/touch_event.h>
#include <frostbite2D/event/joystick_event.h>
#include <frostbite2D/event/window_event.h>
#include <frostbite2D/graphics/camera.h>
#include <frostbite2D/graphics/renderer.h>
#include <frostbite2D/platform/switch.h>
#include <frostbite2D/platform/windows.h>
#include <frostbite2D/resource/asset.h>
#include <frostbite2D/resource/npk_archive.h>
#include <frostbite2D/resource/pvf_archive.h>
#include <frostbite2D/resource/save_system.h>
#include <frostbite2D/resource/sound_pack_archive.h>
#include <frostbite2D/scene/scene.h>
#include <frostbite2D/scene/scene_manager.h>
#include <frostbite2D/types/type_math.h>
#include <frostbite2D/utils/startup_trace.h>
#ifdef __SWITCH__
#include <switch.h>
#endif

namespace frostbite2D {

Application &Application::get() {
  static Application instance;
  return instance;
}

bool Application::init() {
  AppConfig cfg;
  return init(cfg);
}

bool Application::init(const AppConfig& config) {
  ScopedStartupTrace startupTrace("Application::init internals");

  if (initialized_) {
    return true;
  }

  config_ = config;
  setMaxFps(config_.maxFps);

  // 平台相关初始化
  #ifdef __SWITCH__
  {
    ScopedStartupTrace stageTrace("switchInit");
    switchInit();
    switchPlatformInitialized_ = true;
  }
  #endif

  #ifdef _WIN32
  {
    ScopedStartupTrace stageTrace("windowsInit");
    windowsInit();
  }
  #endif

  // 初始化核心模块
  if (!initCoreModules()) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize core modules");
    shutdown();
    return false;
  }

  initialized_ = true;
  return true;
}

void Application::shutdown() {
  if (!initialized_ && !sdlInitialized_
#ifdef __SWITCH__
      && !switchPlatformInitialized_ && !switchRomfsMounted_
#endif
  )
    return;

  running_ = false;
  shouldQuit_ = true;
  setTextInputEnabled(false);

  TaskSystem::get().shutdown();

  // 单例销毁（反向初始化顺序）
  // 场景管理
  SceneManager::get().ClearAll();

  // 渲染器（内部会清理 ShaderManager 和 Batch）
  Renderer::get().shutdown();

  // 音频系统
  AudioSystem::get().shutdown();

  // 资源归档
  NpkArchive::get().close();
  PvfArchive::get().close();
  SoundPackArchive::get().close();

  // 清理相机
  if (camera_) {
    delete camera_;
    camera_ = nullptr;
  }

  if (window_) {
    window_->destroy();
    window_ = nullptr;
  }

  if (sdlInitialized_) {
    SDL_Quit();
    sdlInitialized_ = false;
  }

#ifdef __SWITCH__
  if (switchRomfsMounted_) {
    romfsExit();
    switchRomfsMounted_ = false;
  }
#endif

  // 平台相关清理
  #ifdef __SWITCH__
  if (switchPlatformInitialized_) {
    switchShutdown();
    switchPlatformInitialized_ = false;
  }
  #endif

  ObjectRegistry::get().clear();

  initialized_ = false;
  running_ = false;
}

Application::~Application() {
  
}

bool Application::initCoreModules() {
  ScopedStartupTrace startupTrace("Application::initCoreModules");

  // 初始化资产管理器
  auto &asset = Asset::get();

  // 平台相关 switch平台不可以获取当前工作目录
  #ifndef __SWITCH__
  // 获取程序工作目录
  {
    ScopedStartupTrace stageTrace("Asset working directory setup");
    std::string workingDir = SDL_GetBasePath();
    asset.setWorkingDirectory(workingDir);
    SDL_Log("Asset working directory: %s", workingDir.c_str());
  }
#else
  if (!switchRomfsMounted_ && R_SUCCEEDED(romfsInit())) {
    switchRomfsMounted_ = true;
    asset.setWorkingDirectory("romfs:/");
  } else {
    asset.setWorkingDirectory("/switch/Frostbite2D/" + config_.appName);
  }
  SDL_Log("Asset working directory: %s", asset.getWorkingDirectory().c_str());
#endif

  {
    ScopedStartupTrace stageTrace("SaveSystem::init");
    SaveSystemConfig saveConfig;
#ifdef __SWITCH__
    saveConfig.saveRoot = "sdmc:/switch/NSUnknownGame/save";
#endif
    if (!SaveSystem::get().init(saveConfig)) {
      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize save system");
      return false;
    }
  }

  {
    ScopedStartupTrace stageTrace("SDL_Init video/events/controller");
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER) != 0) {
      SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
      return false;
    }
    sdlInitialized_ = true;
  }

  // Disable text input by default. UI widgets should enable it explicitly.
  setTextInputEnabled(false);

  // 打开第一个手柄（如果存在）
  {
    ScopedStartupTrace stageTrace("SDL_GameControllerOpen(0)");
    if (SDL_GameController *controller = SDL_GameControllerOpen(0)) {
      SDL_Log("GameController opened: %s", SDL_GameControllerName(controller));
      SDL_GameControllerClose(controller);
    } else {
      SDL_Log("No GameController found");
    }
  }

  // 使用SDL2创建窗口
  this->window_ = new Window();

  // 创建窗口会使用窗口配置
  {
    ScopedStartupTrace stageTrace("Window::create");
    if (!window_->create(config_.windowConfig)) {
      SDL_Log("Failed to create window");
      return false;
    }
  }
  window_->setImmEnabled(immEnabled_);

  window_->onResize([this](int width, int height) {
    if (!renderer_ || !window_) {
      return;
    }

    renderer_->setWindowSize(width, height, window_->scaleX(), window_->scaleY());
    SDL_Log("Window resized to %dx%d (drawable %dx%d)", width, height,
            window_->drawableWidth(), window_->drawableHeight());
  });

  // 初始化渲染器
  renderer_ = &Renderer::get();
  {
    ScopedStartupTrace stageTrace("Renderer::init");
    if (!renderer_->init()) {
      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize renderer");
      return false;
    }
  }

  // 设置窗口清除颜色和视口
  renderer_->setClearColor(0.0f, 0.0f, 0.0f);
  renderer_->setDefaultRenderStyleProfile(config_.renderStyleProfile);
  renderer_->setVirtualResolutionEnabled(config_.useVirtualResolution);
  renderer_->setResolutionScaleMode(config_.resolutionMode);
  renderer_->setSeparateUIVirtualResolutionEnabled(
      config_.useSeparateUIVirtualResolution);
  renderer_->setUIResolutionScaleMode(config_.uiResolutionMode);
  if (config_.virtualWidth > 0 && config_.virtualHeight > 0) {
    renderer_->setVirtualResolution(config_.virtualWidth, config_.virtualHeight);
  }
  if (config_.uiVirtualWidth > 0 && config_.uiVirtualHeight > 0) {
    renderer_->setUIVirtualResolution(config_.uiVirtualWidth,
                                      config_.uiVirtualHeight);
  }
  renderer_->setWindowSize(window_->width(), window_->height(), window_->scaleX(),
                           window_->scaleY());

  // 创建并设置相机
  {
    ScopedStartupTrace stageTrace("Camera setup");
    camera_ = new Camera();
    camera_->setViewport(renderer_->getVirtualWidth(), renderer_->getVirtualHeight());
    camera_->setFlipY(true); // Use top-left as (0,0).
    renderer_->applyRenderStyleToCamera(
        camera_, config_.renderStyleProfile, RenderStyleLayerRole::World);
    renderer_->setCamera(camera_);
  }

  {
    ScopedStartupTrace stageTrace("TaskSystem::init");
    if (!TaskSystem::get().init()) {
      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize task system");
      return false;
    }
  }

  return true;
}

void Application::run(StartCallback callback) {
  if (!initialized_) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Application not initialized!");
    return;
  }

  if (running_) {
    SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Application already running!");
    return;
  }


  running_ = true;
  shouldQuit_ = false;
  paused_ = false;

  lastFrameTime_ = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
  totalTime_ = 0.0f;
  deltaTime_ = 0.0f;
  frameCount_ = 0;
  fpsTimer_ = 0.0f;
  currentFps_ = 0;

  if (callback) {
    callback();
  }

  StartupTrace::mark("before Application::mainLoop");
  mainLoop();

  running_ = false;
  SDL_Log("Application stopped");
}

void Application::quit() {
  SDL_Log("Quit requested");
  shouldQuit_ = true;
}

void Application::pause() {
  if (!paused_) {
    SDL_Log("Application paused");
    paused_ = true;
  }
}

void Application::resume() {
  if (paused_) {
    SDL_Log("Application resumed");
    paused_ = false;
    lastFrameTime_ = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
  }
}

void Application::setMaxFps(int maxFps) {
  maxFps_ = std::max(0, maxFps);
  targetFrameDuration_ = maxFps_ > 0
      ? (1.0 / static_cast<double>(maxFps_))
      : 0.0;
  config_.maxFps = maxFps_;
}

void Application::setTextInputEnabled(bool enabled) {
  textInputRequestCount_ = enabled ? 1 : 0;

  const bool canToggleTextInput = SDL_WasInit(SDL_INIT_VIDEO) != 0;
  if (enabled) {
    if (canToggleTextInput) {
      SDL_StartTextInput();
    }
    if (!textInputEnabled_) {
      SDL_Log("Text input enabled");
    }
    textInputEnabled_ = true;
    return;
  }

  if (canToggleTextInput) {
    SDL_StopTextInput();
  }
  if (textInputEnabled_) {
    SDL_Log("Text input disabled");
  }
  textInputEnabled_ = false;
}

void Application::requestTextInput() {
  if (textInputRequestCount_ < std::numeric_limits<int>::max()) {
    ++textInputRequestCount_;
  }

  if (textInputEnabled_) {
    return;
  }

  const bool canToggleTextInput = SDL_WasInit(SDL_INIT_VIDEO) != 0;
  if (canToggleTextInput) {
    SDL_StartTextInput();
  }
  textInputEnabled_ = true;
  SDL_Log("Text input enabled");
}

void Application::releaseTextInput() {
  if (textInputRequestCount_ <= 0) {
    textInputRequestCount_ = 0;
    return;
  }

  --textInputRequestCount_;
  if (textInputRequestCount_ > 0 || !textInputEnabled_) {
    return;
  }

  const bool canToggleTextInput = SDL_WasInit(SDL_INIT_VIDEO) != 0;
  if (canToggleTextInput) {
    SDL_StopTextInput();
  }
  textInputEnabled_ = false;
  SDL_Log("Text input disabled");
}

void Application::setImmEnabled(bool enabled) {
  immEnabled_ = enabled;
  if (window_) {
    window_->setImmEnabled(enabled);
  }
}

bool Application::isImmEnabled() const {
  if (window_) {
    return window_->isImmEnabled();
  }
  return immEnabled_;
}

void Application::mainLoop() {
  constexpr double kUnfocusedFrameDuration = 1.0 / 15.0;
  const double performanceFrequency =
      static_cast<double>(SDL_GetPerformanceFrequency());
  while (!shouldQuit_) {
    const Uint64 frameStartCounter = SDL_GetPerformanceCounter();
    SDL_Event sdlEvent;
    while (SDL_PollEvent(&sdlEvent)) {
      if (sdlEvent.type == SDL_QUIT) {
        shouldQuit_ = true;
        break;
      }

      if (sdlEvent.type == SDL_WINDOWEVENT && window_) {
        switch (sdlEvent.window.event) {
          case SDL_WINDOWEVENT_RESIZED:
          case SDL_WINDOWEVENT_SIZE_CHANGED:
            window_->refreshMetrics(true);
            break;
          case SDL_WINDOWEVENT_FOCUS_GAINED:
          case SDL_WINDOWEVENT_FOCUS_LOST:
          case SDL_WINDOWEVENT_MINIMIZED:
          case SDL_WINDOWEVENT_MAXIMIZED:
          case SDL_WINDOWEVENT_RESTORED:
            window_->refreshMetrics(false);
            break;
          default:
            break;
        }
      }

      auto event = convertSDLEvent(sdlEvent);
      if (event) {
        dispatchEvent(*event);
      }
    }

    TaskSystem::get().drainMainThreadTasks();
    update();

    render();

    double frameDurationLimit = targetFrameDuration_;
    bool shouldDelay = frameDurationLimit > 0.0;
    if (window_) {
      bool shouldUseBackgroundCap = !window_->focused() || window_->minimized();
      if (shouldUseBackgroundCap) {
        frameDurationLimit = shouldDelay
            ? std::max(frameDurationLimit, kUnfocusedFrameDuration)
            : kUnfocusedFrameDuration;
        shouldDelay = true;
      } else if (window_->vsync()) {
        continue;
      }
    }

    if (!shouldDelay || frameDurationLimit <= 0.0) {
      continue;
    }

    const double frameDuration =
        static_cast<double>(SDL_GetPerformanceCounter() - frameStartCounter) /
        performanceFrequency;
    const double remainingDelay = frameDurationLimit - frameDuration;
    if (remainingDelay > 0.0) {
      const Uint32 delayMs = static_cast<Uint32>(remainingDelay * 1000.0);
      if (delayMs > 0) {
        SDL_Delay(delayMs);
      }
    }
  }
}

std::unique_ptr<Event> Application::convertSDLEvent(const SDL_Event& sdlEvent) {
  switch (sdlEvent.type) {
    case SDL_WINDOWEVENT: {
      switch (sdlEvent.window.event) {
        case SDL_WINDOWEVENT_CLOSE:
          return std::make_unique<WindowCloseEvent>(sdlEvent.window.timestamp, sdlEvent.window.windowID);
        case SDL_WINDOWEVENT_RESIZED:
        case SDL_WINDOWEVENT_SIZE_CHANGED:
          return std::make_unique<WindowResizeEvent>(sdlEvent.window.timestamp, sdlEvent.window.windowID, 
                                                     sdlEvent.window.data1, sdlEvent.window.data2);
        case SDL_WINDOWEVENT_FOCUS_GAINED:
        case SDL_WINDOWEVENT_FOCUS_LOST:
          return std::make_unique<WindowFocusEvent>(sdlEvent.window.timestamp, sdlEvent.window.windowID,
                                                     sdlEvent.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
        case SDL_WINDOWEVENT_MINIMIZED:
          return std::make_unique<WindowMinimizeEvent>(sdlEvent.window.timestamp, sdlEvent.window.windowID);
        case SDL_WINDOWEVENT_MAXIMIZED:
          return std::make_unique<WindowMaximizeEvent>(sdlEvent.window.timestamp, sdlEvent.window.windowID);
        case SDL_WINDOWEVENT_RESTORED:
          return std::make_unique<WindowRestoreEvent>(sdlEvent.window.timestamp, sdlEvent.window.windowID);
      }
      break;
    }

    case SDL_KEYDOWN: {
      return std::make_unique<KeyEvent>(sdlEvent.key.timestamp, 
                                        static_cast<KeyCode>(sdlEvent.key.keysym.sym),
                                        static_cast<KeyMod>(sdlEvent.key.keysym.mod),
                                        sdlEvent.key.repeat != 0);
    }

    case SDL_KEYUP: {
      return std::make_unique<KeyUpEvent>(sdlEvent.key.timestamp,
                                          static_cast<KeyCode>(sdlEvent.key.keysym.sym),
                                          static_cast<KeyMod>(sdlEvent.key.keysym.mod));
    }

    case SDL_TEXTINPUT: {
      return std::make_unique<KeyTextEvent>(sdlEvent.text.timestamp, sdlEvent.text.text);
    }

    case SDL_MOUSEMOTION: {
      return std::make_unique<MouseMoveEvent>(sdlEvent.motion.timestamp,
                                               Vec2(static_cast<float>(sdlEvent.motion.x), static_cast<float>(sdlEvent.motion.y)),
                                               Vec2(static_cast<float>(sdlEvent.motion.xrel), static_cast<float>(sdlEvent.motion.yrel)));
    }

    case SDL_MOUSEBUTTONDOWN: {
      return std::make_unique<MouseButtonEvent>(sdlEvent.button.timestamp,
                                                 static_cast<MouseButton>(sdlEvent.button.button),
                                                 Vec2(static_cast<float>(sdlEvent.button.x), static_cast<float>(sdlEvent.button.y)),
                                                 true);
    }

    case SDL_MOUSEBUTTONUP: {
      return std::make_unique<MouseButtonEvent>(sdlEvent.button.timestamp,
                                                 static_cast<MouseButton>(sdlEvent.button.button),
                                                 Vec2(static_cast<float>(sdlEvent.button.x), static_cast<float>(sdlEvent.button.y)),
                                                 false);
    }

    case SDL_MOUSEWHEEL: {
      return std::make_unique<MouseWheelEvent>(sdlEvent.wheel.timestamp,
                                                  Vec2(static_cast<float>(sdlEvent.wheel.mouseX), static_cast<float>(sdlEvent.wheel.mouseY)),
                                                  Vec2(static_cast<float>(sdlEvent.wheel.x), static_cast<float>(sdlEvent.wheel.y)),
                                                  sdlEvent.wheel.direction == SDL_MOUSEWHEEL_FLIPPED);
    }

    case SDL_FINGERDOWN: {
      return std::make_unique<TouchDownEvent>(sdlEvent.tfinger.timestamp,
                                               sdlEvent.tfinger.touchId,
                                               sdlEvent.tfinger.fingerId,
                                               Vec2(static_cast<float>(sdlEvent.tfinger.x), static_cast<float>(sdlEvent.tfinger.y)),
                                               sdlEvent.tfinger.pressure);
    }

    case SDL_FINGERUP: {
      return std::make_unique<TouchUpEvent>(sdlEvent.tfinger.timestamp,
                                             sdlEvent.tfinger.touchId,
                                             sdlEvent.tfinger.fingerId,
                                             Vec2(static_cast<float>(sdlEvent.tfinger.x), static_cast<float>(sdlEvent.tfinger.y)),
                                             sdlEvent.tfinger.pressure);
    }

    case SDL_FINGERMOTION: {
      return std::make_unique<TouchMoveEvent>(sdlEvent.tfinger.timestamp,
                                               sdlEvent.tfinger.touchId,
                                               sdlEvent.tfinger.fingerId,
                                               Vec2(static_cast<float>(sdlEvent.tfinger.x), static_cast<float>(sdlEvent.tfinger.y)),
                                               Vec2(static_cast<float>(sdlEvent.tfinger.dx), static_cast<float>(sdlEvent.tfinger.dy)),
                                               sdlEvent.tfinger.pressure);
    }

    case SDL_CONTROLLERBUTTONDOWN: {
      return std::make_unique<JoystickButtonDownEvent>(sdlEvent.cbutton.timestamp,
                                                         sdlEvent.cbutton.which,
                                                         sdlEvent.cbutton.button);
    }

    case SDL_CONTROLLERBUTTONUP: {
      return std::make_unique<JoystickButtonUpEvent>(sdlEvent.cbutton.timestamp,
                                                       sdlEvent.cbutton.which,
                                                       sdlEvent.cbutton.button);
    }

    case SDL_CONTROLLERAXISMOTION: {
      return std::make_unique<JoystickAxisEvent>(sdlEvent.caxis.timestamp,
                                                  sdlEvent.caxis.which,
                                                  sdlEvent.caxis.axis,
                                                  sdlEvent.caxis.value);
    }
  }

  return nullptr;
}

void Application::dispatchEvent(const Event& event) {
  if (paused_) {
    SceneManager::get().DispatchUIEvent(event);
    return;
  }

  SceneManager::get().DispatchEvent(event);
}

void Application::update() {
  double currentTime = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
  deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
  lastFrameTime_ = currentTime;
  totalTime_ += deltaTime_;

  if (!paused_) {
    SceneManager::get().Update(deltaTime_);
  }
  SceneManager::get().UpdateUI(deltaTime_);

  frameCount_++;
  fpsTimer_ += deltaTime_;
  if (fpsTimer_ >= fpsUpdateInterval_) {
    currentFps_ = static_cast<int>(frameCount_ / fpsTimer_);
    frameCount_ = 0;
    fpsTimer_ = 0.0f;
  }
}

void Application::render() {
  static bool firstFramePresented = false;
  Renderer& renderer = Renderer::get();

  renderer.beginFrame();
  SceneManager::get().Render();
  renderer.endFrame();

  if (window_) {
    window_->swap();
  }

  if (!firstFramePresented) {
    firstFramePresented = true;
    StartupTrace::mark("first frame presented");
  }
}

const AppConfig& Application::getConfig() const {
  return config_;
}
} // namespace frostbite2D
