#include "SDL_log.h"
#include "frostbite2D/2d/sprite.h"
#include "frostbite2D/base/RefPtr.h"
#include <SDL2/SDL.h>
#include <exception>
#include <stdexcept>
#include <string>
#include <vector>

#include <frostbite2D/2d/text_sprite.h>
#include <frostbite2D/audio/audio_system.h>
#include <frostbite2D/core/application.h>
#include <frostbite2D/core/task_system.h>
#include <frostbite2D/graphics/font_manager.h>
#include <frostbite2D/resource/asset.h>
#include <frostbite2D/resource/audio_database.h>
#include <frostbite2D/resource/npk_archive.h>
#include <frostbite2D/resource/pvf_archive.h>
#include <frostbite2D/resource/sound_pack_archive.h>
#include <frostbite2D/scene/scene.h>
#include <frostbite2D/scene/scene_manager.h>
#include <frostbite2D/utils/startup_trace.h>
#include "world/GameWorld.h"

using namespace frostbite2D;

int main(int argc, char **argv) {
  (void)argc;
  (void)argv;

  StartupTrace::reset("main entry");
  StartupTrace::mark("main entry");

  AppConfig config = AppConfig::createDefault();
  config.appName = "Frostbite2D Test App";
  config.appVersion = "1.0.0";
#ifdef SWITCH
  config.windowConfig.width = 1280;
  config.windowConfig.height = 720;
#else
  config.windowConfig.width = 1066;
  config.windowConfig.height = 600;
#endif
  config.windowConfig.title = "Frostbite2D - Async Init Demo";
  config.useVirtualResolution = true;
  config.virtualWidth = 1066;
  config.virtualHeight = 600;
  config.resolutionMode = ResolutionScaleMode::FitHeight;
  config.renderStyleProfile = RenderStyleProfileId::PixelArt2D;

  Application &app = Application::get();
  {
    ScopedStartupTrace startupTrace("Application::init");
    if (!app.init(config)) {
      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
                   "Failed to initialize application!");
      return -1;
    }
  }
  StartupTrace::mark("Application::init complete");

  app.run([]() {
    ScopedStartupTrace startupTrace("Application::run startup callback");

    {
      ScopedStartupTrace stageTrace("FontManager startup");
      auto &fontManager = FontManager::get();
      fontManager.init();
      fontManager.registerFont("default", "assets/Fonts/VonwaonBitmap-12px.ttf",
                               12);
    }

    {
      ScopedStartupTrace stageTrace("AudioSystem startup");
      auto &audioSystem = AudioSystem::get();
      audioSystem.init();
      audioSystem.setMasterVolume(1.0f);
      audioSystem.setSoundVolume(0.8f);
      audioSystem.setMusicVolume(0.6f);
    }

    SDL_Log("游戏启动");

    {
      ScopedStartupTrace stageTrace("Loading scene bootstrap");
      auto LoadingScene = MakePtr<Scene>();
      SceneManager::get().PushScene(LoadingScene);

      auto Background =
          Sprite::createFromFile("assets/ImagePacks2/Loading0.png");
      Background->SetSize(1066, 600);
      LoadingScene->AddChild(Background);

      auto BackgroundBar =
          Sprite::createFromFile("assets/ImagePacks2/Loading1.png");
      BackgroundBar->SetPosition(0 - 107, 566);
      LoadingScene->AddChild(BackgroundBar);

      auto LoadCircleSp =
          Sprite::createFromFile("assets/ImagePacks2/Loading2.png");
      LoadCircleSp->SetAnchor(Vec2(0.5f, 0.5f));
      LoadCircleSp->SetPosition(1066 / 2.0f, 566 - 60);
      LoadCircleSp->SetBlendMode(BlendMode::Additive);
      LoadCircleSp->AddUpdateListener([](Actor &self, float dt) {
        auto rotation = self.GetRotation();
        self.SetRotation(rotation + 180.0f * dt);
      });
      LoadingScene->AddChild(LoadCircleSp);
    }

    StartupTrace::mark("loading scene ready");

    TaskSystem::get().submitThen(
        []() -> std::string {
          ScopedStartupTrace startupTrace("Async resource bootstrap");
          SDL_Log("Async init task on main thread: %s",
                  TaskSystem::get().isMainThread() ? "true" : "false");

          {
            ScopedStartupTrace stageTrace("PvfArchive open+init");
            auto &pvf = PvfArchive::get();
            if (!pvf.open("assets/Script.pvf")) {
              throw std::runtime_error("Failed to open assets/Script.pvf");
            }
            pvf.init();
          }

          {
            ScopedStartupTrace stageTrace("NpkArchive init");
            auto &npk = NpkArchive::get();
            npk.setImagePackDirectory("assets/ImagePacks2");
            npk.setDefaultImg("sprite/interface/base.img", 0);
            npk.init();
          }

          {
            ScopedStartupTrace stageTrace("SoundPackArchive init");
            auto &archive = SoundPackArchive::get();
            archive.setSoundPackDirectory("assets/SoundPacks");
            archive.init();
          }

          {
            ScopedStartupTrace stageTrace("AudioDatabase load");
            auto &audioDatabase = AudioDatabase::get();
            audioDatabase.loadFromFile("assets/audio.xml");
          }

          return "后台资源加载成功";
        },
        [](std::string message) mutable {
          ScopedStartupTrace startupTrace("Async completion main-thread scene switch");
          SDL_Log("%s", message.c_str());
          StartupTrace::mark("before SceneManager::ReplaceScene");

          {
            ScopedStartupTrace stageTrace("SceneManager::ReplaceScene(GameWorld)");
            auto gameWorld = MakePtr<GameWorld>();
            SceneManager::get().ReplaceScene(gameWorld);
          }

          StartupTrace::mark("after SceneManager::ReplaceScene");
        },
        [](std::exception_ptr error) {
          try {
            if (error) {
              std::rethrow_exception(error);
            }
          } catch (const std::exception &ex) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Async init failed: %s",
                         ex.what());
          }
        });
  });

  SDL_Log("Application exited normally");
  app.shutdown();

  return 0;
}
