#pragma once

#include <extra2d/core/module.h>
#include <extra2d/core/registry.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/core/types.h>
#include <string>

namespace extra2d {

class IWindow;
class IInput;
class RenderBackend;
class WindowModule;
class RenderModule;
class InputModule;

/**
 * @brief 应用程序类
 */
class Application {
public:
  static Application &get();

  Application(const Application &) = delete;
  Application &operator=(const Application &) = delete;

  /**
   * @brief 应用信息
   */
  std::string appName = "Extra2D App";
  std::string appVersion = "1.0.0";
  std::string organization = "";

  /**
   * @brief 注册模块
   * @tparam T 模块类型
   * @tparam Args 构造函数参数
   * @return 模块指针
   */
  template <typename T, typename... Args> T *use(Args &&...args) {
    return Registry::instance().use<T>(std::forward<Args>(args)...);
  }

  /**
   * @brief 获取模块
   * @tparam T 模块类型
   * @return 模块指针
   */
  template <typename T> T *get() const { return Registry::instance().get<T>(); }

  /**
   * @brief 初始化
   * @return 初始化成功返回 true
   */
  bool init();

  /**
   * @brief 关闭
   */
  void shutdown();

  /**
   * @brief 运行主循环
   */
  void run();

  /**
   * @brief 请求退出
   */
  void quit();

  /**
   * @brief 暂停
   */
  void pause();

  /**
   * @brief 恢复
   */
  void resume();

  bool isPaused() const { return paused_; }
  bool isRunning() const { return running_; }

  /**
   * @brief 获取窗口
   * @return 窗口指针
   */
  IWindow *window();

  /**
   * @brief 获取渲染器
   * @return 渲染器指针
   */
  RenderBackend *renderer();

  /**
   * @brief 获取输入
   * @return 输入指针
   */
  IInput *input();

  /**
   * @brief 进入场景
   * @param scene 场景指针
   */
  void enterScene(Ptr<class Scene> scene);

  float deltaTime() const { return deltaTime_; }
  float totalTime() const { return totalTime_; }
  int fps() const { return currentFps_; }

private:
  Application();
  ~Application();

  void mainLoop();
  void update();
  void render();
  void configureCameraService();

  bool initialized_ = false;
  bool running_ = false;
  bool paused_ = false;
  bool shouldQuit_ = false;

  float deltaTime_ = 0.0f;
  float totalTime_ = 0.0f;
  double lastFrameTime_ = 0.0;
  int frameCount_ = 0;
  float fpsTimer_ = 0.0f;
  int currentFps_ = 0;
};

} // namespace extra2d
