#include "pch.h"

#include "legacy_app_startup_services.h"

#include "app.h"
#include "app_core/app_frame.h"
#include "legacy_gl_runtime_dispatch.h"
#include "legacy_preference_storage.h"
#include "legacy_ui_gl_dispatch.h"
#include "platform_legacy/legacy_platform_state.h"
#include "platform_api/platform_services.h"
#include "renderer_gl/opengl_capabilities.h"

namespace pp::panopainter {
namespace {

[[nodiscard]] GLenum linear_texture_filter() noexcept
{
    return static_cast<GLenum>(pp::renderer::gl::linear_texture_filter());
}

[[nodiscard]] GLenum nearest_texture_filter() noexcept
{
    return static_cast<GLenum>(pp::renderer::gl::nearest_texture_filter());
}

[[nodiscard]] GLenum repeat_texture_wrap() noexcept
{
    return static_cast<GLenum>(pp::renderer::gl::repeat_texture_wrap());
}

class LegacyAppStartupServices final
    : public pp::app::AppStartupServices
    , public pp::app::AppStartupResourceServices {
public:
    explicit LegacyAppStartupServices(App& app) noexcept
        : app_(app)
    {
    }

    void store_run_counter(int value) override
    {
        set_legacy_startup_run_counter(value);
        LOG("run_counter %d", value);
    }

    void save_preferences() override
    {
        if (!save_legacy_preferences())
            LOG("save preferences failed");
    }

    void start_timelapse_recording() override
    {
        app_.rec_start();
    }

    void apply_vr_controllers_enabled(bool enabled) override
    {
        app_.vr_controllers_enabled = enabled;
    }

    void show_license_warning() override
    {
        app_.message_box("License", "Could not validate this license, running in demo mode.");
    }

    void initialize_shaders() override
    {
        app_.initShaders();
    }

    void initialize_assets() override
    {
        app_.initAssets();
    }

    void initialize_layout() override
    {
        app_.initLayout();
    }

    void update_title() override
    {
        app_.title_update();
    }

    void create_ui_render_target(int width, int height) override
    {
        app_.uirtt.create(
            width,
            height,
            -1,
            static_cast<GLint>(pp::renderer::gl::rgba8_internal_format()),
            true);
    }

private:
    App& app_;
};

} // namespace

void execute_legacy_app_create(App& app)
{
    const auto initial_surface = pp::app::plan_app_initial_surface();
    app.width = initial_surface.width;
    app.height = initial_surface.height;
}

void execute_legacy_app_init_assets(App& app)
{
    LOG("initializing assets");
    FontManager::init();

    LOG("initializing assets create sampler");
    app.sampler.create(nearest_texture_filter());
    app.sampler_stencil.create(linear_texture_filter(), repeat_texture_wrap());
    app.sampler_linear.create(linear_texture_filter());
    app.m_face_plane.create<1>(2, 2);
    app.sphere.create<8, 8>(1);
    LOG("initializing assets load uvs texture");
    LOG("initializing assets completed");
}

void execute_legacy_app_init_log(App& app)
{
    const auto paths = app.prepare_storage_paths();
    if (!paths.data_path.empty())
        app.data_path = paths.data_path;
    if (!paths.recording_path.empty())
        app.rec_path = paths.recording_path;
    if (!paths.temporary_path.empty())
        app.tmp_path = paths.temporary_path;

    // TODO: save this path somewhere in the settings, don't overwrite every start
    app.work_path = paths.work_path.empty() ? app.data_path : paths.work_path;
    //LogRemote::I.start();
    LogRemote::I.file_init();
    LOG("%s", g_version);

    LOG("load preferences");
    if (!load_legacy_preferences())
        LOG("load preferences failed");
}

void execute_legacy_app_init(App& app)
{
    LOG("Screen Resolution: %dx%d", static_cast<int>(app.width), static_cast<int>(app.height));

    app.render_task([&app]
    {
        app.install_render_debug_callback();
        const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info(
            pp::legacy::gl_runtime::runtime_info_dispatch());
        if (runtime_info_result.ok())
        {
            const auto& runtime_info = runtime_info_result.value();
            LOG("GL version: %s", runtime_info.version);
            LOG("GL vendor: %s", runtime_info.vendor);
            LOG("GL renderer: %s", runtime_info.renderer);
            LOG("GLSL version: %s", runtime_info.shading_language_version);
        }
        else
        {
            LOG("OpenGL runtime info failed: %s", runtime_info_result.status().message);
        }

        app.apply_render_platform_hints();
        const auto startup_state_status = pp::renderer::gl::apply_panopainter_initial_state(
            pp::renderer::gl::OpenGlStateDispatch {
                .enable = pp::legacy::ui_gl::enable_opengl_state,
                .disable = pp::legacy::ui_gl::disable_opengl_state,
                .blend_func = pp::legacy::ui_gl::set_opengl_blend_func,
                .blend_equation_separate = pp::legacy::ui_gl::set_opengl_blend_equation_separate,
            });
        if (!startup_state_status.ok())
            LOG("OpenGL startup state failed: %s", startup_state_status.message);
    });

    const auto startup_preferences =
        read_legacy_startup_preferences(app.vr_controllers_enabled);
    const auto startup_plan = pp::app::plan_app_startup(
        startup_preferences.run_counter,
        startup_preferences.auto_timelapse,
        startup_preferences.vr_controllers_enabled,
        app.check_license());
    if (!startup_plan) {
        LOG("App startup plan failed: %s", startup_plan.status().message);
    } else {
        const auto persistence_status = execute_legacy_app_startup_persistence_plan(
            app,
            startup_plan.value());
        if (!persistence_status.ok())
            LOG("App startup persistence failed: %s", persistence_status.message);
    }

    const auto startup_resources = pp::app::plan_app_startup_resources(app.width, app.height);
    if (!startup_resources) {
        LOG("App startup resource plan failed: %s", startup_resources.status().message);
    } else {
        const auto resource_status = execute_legacy_app_startup_resources(
            app,
            startup_resources.value());
        if (!resource_status.ok())
            LOG("App startup resources failed: %s", resource_status.message);
    }

    if (startup_plan) {
        const auto startup_status = execute_legacy_app_startup_runtime_plan(
            app,
            startup_plan.value());
        if (!startup_status.ok())
            LOG("App startup runtime execution failed: %s", startup_status.message);
    }
}

pp::foundation::Status execute_legacy_app_startup_plan(
    App& app,
    const pp::app::AppStartupPlan& plan)
{
    LegacyAppStartupServices services(app);
    return pp::app::execute_app_startup_plan(plan, services);
}

pp::foundation::Status execute_legacy_app_startup_persistence_plan(
    App& app,
    const pp::app::AppStartupPlan& plan)
{
    LegacyAppStartupServices services(app);
    return pp::app::execute_app_startup_persistence_plan(plan, services);
}

pp::foundation::Status execute_legacy_app_startup_runtime_plan(
    App& app,
    const pp::app::AppStartupPlan& plan)
{
    LegacyAppStartupServices services(app);
    return pp::app::execute_app_startup_runtime_plan(plan, services);
}

pp::foundation::Status execute_legacy_app_startup_resources(
    App& app,
    const pp::app::AppStartupResourcePlan& plan)
{
    LegacyAppStartupServices services(app);
    return pp::app::execute_app_startup_resources(plan, services);
}

} // namespace pp::panopainter
