#include <app/application.h>
#include <core/director.h>
#include <core/service.h>
#include <core/event/events.h>
#include <platform/sdl2.h>
#include <platform/window.h>
#include <platform/input.h>
#include <platform/file.h>
#include <utils/logger.h>

#include <chrono>
#include <thread>

#ifdef __SWITCH__
#include <switch.h>
#endif

namespace extra2d {

/**
 * @brief 获取当前时间（秒）
 */
static double getTimeSeconds() {
#ifdef __SWITCH__
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return static_cast<double>(ts.tv_sec) + static_cast<double>(ts.tv_nsec) / 1000000000.0;
#else
    using namespace std::chrono;
    auto now = steady_clock::now();
    auto duration = now.time_since_epoch();
    return duration_cast<std::chrono::duration<double>>(duration).count();
#endif
}

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

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

bool Application::init(const AppConfig& config) {
    if (initialized_) {
        E2D_LOG_WARN("Application already initialized");
        return true;
    }

    config_ = config;

    PlatformType platform = config_.platform;
    if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
        platform = PlatformType::Switch;
#else
        platform = PlatformType::PC;
#endif
    }

    if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
        Result rc;
        rc = romfsInit();
        if (R_SUCCEEDED(rc)) {
            E2D_LOG_INFO("RomFS initialized successfully");
        } else {
            E2D_LOG_WARN("romfsInit failed: {:#08X}", rc);
        }

        rc = socketInitializeDefault();
        if (R_FAILED(rc)) {
            E2D_LOG_WARN("socketInitializeDefault failed");
        }
#endif
    }

    if (!Sdl2::initAll()) {
        E2D_LOG_ERROR("Failed to initialize SDL2");
        return false;
    }

    SVC_MGR.reg(&WINDOW);
    SVC_MGR.reg(&INPUT_SVC);
    SVC_MGR.reg(&FILE_SVC);

    if (!SVC_MGR.initAll()) {
        E2D_LOG_ERROR("Failed to initialize services");
        return false;
    }

    WindowCfg winCfg;
    winCfg.title = config_.title;
    winCfg.width = config_.width;
    winCfg.height = config_.height;
    winCfg.fullscreen = config_.fullscreen;
    winCfg.resizable = config_.resizable;
    winCfg.vsync = config_.vsync;
    winCfg.glMajor = config_.glMajor;
    winCfg.glMinor = config_.glMinor;

    if (!WINDOW.create(winCfg)) {
        E2D_LOG_ERROR("Failed to create window");
        return false;
    }

    if (!DIRECTOR.init()) {
        E2D_LOG_ERROR("Failed to initialize Director");
        return false;
    }

    WINDOW.setOnClose([this]() {
        quit();
    });

    initialized_ = true;
    running_ = true;

    events::OnInit::emit();

    E2D_LOG_INFO("Application initialized successfully");
    E2D_LOG_INFO("Window: {}x{}, Fullscreen: {}, VSync: {}", 
                 config_.width, config_.height, config_.fullscreen, config_.vsync);
    return true;
}

void Application::shutdown() {
    if (!initialized_) return;

    events::OnShutdown::emit();

    E2D_LOG_INFO("Shutting down application...");

    DIRECTOR.shutdown();
    SVC_MGR.shutdownAll();
    Sdl2::shutdown();

    PlatformType platform = config_.platform;
    if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
        platform = PlatformType::Switch;
#else
        platform = PlatformType::PC;
#endif
    }

    if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
        romfsExit();
        socketExit();
#endif
    }

    initialized_ = false;
    running_ = false;

    E2D_LOG_INFO("Application shutdown complete");
}

void Application::run() {
    if (!initialized_) {
        E2D_LOG_ERROR("Application not initialized");
        return;
    }

    lastFrameTime_ = getTimeSeconds();

    while (running_ && !WINDOW.shouldClose()) {
        mainLoop();
    }
}

void Application::quit() {
    shouldQuit_ = true;
    running_ = false;
    WINDOW.requestClose();
}

void Application::pause() {
    if (!paused_) {
        paused_ = true;
        SVC_MGR.pauseAll();
        DIRECTOR.pause();
        events::OnPause::emit();
        E2D_LOG_INFO("Application paused");
    }
}

void Application::resume() {
    if (paused_) {
        paused_ = false;
        SVC_MGR.resumeAll();
        DIRECTOR.resume();
        lastFrameTime_ = getTimeSeconds();
        events::OnResume::emit();
        E2D_LOG_INFO("Application resumed");
    }
}

void Application::mainLoop() {
    if (!WINDOW.pollEvents()) {
        running_ = false;
        return;
    }

    double currentTime = getTimeSeconds();
    deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
    lastFrameTime_ = currentTime;

    totalTime_ += deltaTime_;

    frameCount_++;
    fpsTimer_ += deltaTime_;
    if (fpsTimer_ >= 1.0f) {
        currentFps_ = frameCount_;
        frameCount_ = 0;
        fpsTimer_ -= 1.0f;
    }

    if (!paused_) {
        update();
    }

    WINDOW.swapBuffers();

    if (!config_.vsync && config_.fpsLimit > 0) {
        double frameEndTime = getTimeSeconds();
        double frameTime = frameEndTime - currentTime;
        double target = 1.0 / static_cast<double>(config_.fpsLimit);
        if (frameTime < target) {
            std::this_thread::sleep_for(std::chrono::duration<double>(target - frameTime));
        }
    }
}

void Application::update() {
    SVC_MGR.updateAll(deltaTime_);
    events::OnUpdate::emit(deltaTime_);
    DIRECTOR.mainLoop(deltaTime_);
}

} // namespace extra2d
