#pragma once

#include "foundation/result.h"

#include <cmath>
#include <limits>

namespace pp::app {

struct AppStartupPlan {
    int previous_run_counter = 0;
    int next_run_counter = 1;
    bool save_preferences = true;
    bool start_timelapse = false;
    bool vr_controllers_enabled = true;
    bool show_license_warning = false;
};

struct AppStartupResourcePlan {
    int ui_render_target_width = 0;
    int ui_render_target_height = 0;
    bool initialize_shaders = true;
    bool initialize_assets = true;
    bool initialize_layout = true;
    bool update_title = true;
    bool create_ui_render_target = true;
};

class AppStartupServices {
public:
    virtual ~AppStartupServices() = default;

    virtual void store_run_counter(int value) = 0;
    virtual void save_preferences() = 0;
    virtual void start_timelapse_recording() = 0;
    virtual void apply_vr_controllers_enabled(bool enabled) = 0;
    virtual void show_license_warning() = 0;
};

class AppStartupResourceServices {
public:
    virtual ~AppStartupResourceServices() = default;

    virtual void initialize_shaders() = 0;
    virtual void initialize_assets() = 0;
    virtual void initialize_layout() = 0;
    virtual void update_title() = 0;
    virtual void create_ui_render_target(int width, int height) = 0;
};

[[nodiscard]] inline pp::foundation::Result<AppStartupPlan> plan_app_startup(
    int current_run_counter,
    bool auto_timelapse_enabled,
    bool stored_vr_controllers_enabled,
    bool license_valid)
{
    if (current_run_counter < 0) {
        return pp::foundation::Result<AppStartupPlan>::failure(
            pp::foundation::Status::invalid_argument("run counter must not be negative"));
    }

    if (current_run_counter == std::numeric_limits<int>::max()) {
        return pp::foundation::Result<AppStartupPlan>::failure(
            pp::foundation::Status::out_of_range("run counter would overflow"));
    }

    AppStartupPlan plan;
    plan.previous_run_counter = current_run_counter;
    plan.next_run_counter = current_run_counter + 1;
    plan.start_timelapse = auto_timelapse_enabled;
    plan.vr_controllers_enabled = stored_vr_controllers_enabled;
    plan.show_license_warning = !license_valid;
    return pp::foundation::Result<AppStartupPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Result<AppStartupResourcePlan> plan_app_startup_resources(
    float ui_width,
    float ui_height)
{
    if (!std::isfinite(ui_width) || !std::isfinite(ui_height)) {
        return pp::foundation::Result<AppStartupResourcePlan>::failure(
            pp::foundation::Status::invalid_argument("startup resource dimensions must be finite"));
    }

    if (ui_width < 1.0F || ui_height < 1.0F) {
        return pp::foundation::Result<AppStartupResourcePlan>::failure(
            pp::foundation::Status::invalid_argument("startup resource dimensions must be positive"));
    }

    if (ui_width > static_cast<float>(std::numeric_limits<int>::max())
        || ui_height > static_cast<float>(std::numeric_limits<int>::max())) {
        return pp::foundation::Result<AppStartupResourcePlan>::failure(
            pp::foundation::Status::out_of_range("startup resource dimensions exceed integer range"));
    }

    AppStartupResourcePlan plan;
    plan.ui_render_target_width = static_cast<int>(ui_width);
    plan.ui_render_target_height = static_cast<int>(ui_height);
    return pp::foundation::Result<AppStartupResourcePlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Status execute_app_startup_plan(
    const AppStartupPlan& plan,
    AppStartupServices& services)
{
    if (plan.previous_run_counter < 0 || plan.next_run_counter <= plan.previous_run_counter) {
        return pp::foundation::Status::invalid_argument("startup plan has invalid run counter state");
    }

    services.store_run_counter(plan.next_run_counter);
    if (plan.save_preferences) {
        services.save_preferences();
    }
    if (plan.start_timelapse) {
        services.start_timelapse_recording();
    }
    services.apply_vr_controllers_enabled(plan.vr_controllers_enabled);
    if (plan.show_license_warning) {
        services.show_license_warning();
    }

    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Status execute_app_startup_persistence_plan(
    const AppStartupPlan& plan,
    AppStartupServices& services)
{
    if (plan.previous_run_counter < 0 || plan.next_run_counter <= plan.previous_run_counter) {
        return pp::foundation::Status::invalid_argument("startup plan has invalid run counter state");
    }

    services.store_run_counter(plan.next_run_counter);
    if (plan.save_preferences) {
        services.save_preferences();
    }

    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Status execute_app_startup_runtime_plan(
    const AppStartupPlan& plan,
    AppStartupServices& services)
{
    if (plan.previous_run_counter < 0 || plan.next_run_counter <= plan.previous_run_counter) {
        return pp::foundation::Status::invalid_argument("startup plan has invalid run counter state");
    }

    if (plan.start_timelapse) {
        services.start_timelapse_recording();
    }
    services.apply_vr_controllers_enabled(plan.vr_controllers_enabled);
    if (plan.show_license_warning) {
        services.show_license_warning();
    }

    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Status execute_app_startup_resources(
    const AppStartupResourcePlan& plan,
    AppStartupResourceServices& services)
{
    if (plan.create_ui_render_target
        && (plan.ui_render_target_width <= 0 || plan.ui_render_target_height <= 0)) {
        return pp::foundation::Status::invalid_argument("startup resource plan has invalid UI render target size");
    }

    if (plan.initialize_shaders) {
        services.initialize_shaders();
    }
    if (plan.initialize_assets) {
        services.initialize_assets();
    }
    if (plan.initialize_layout) {
        services.initialize_layout();
    }
    if (plan.update_title) {
        services.update_title();
    }
    if (plan.create_ui_render_target) {
        services.create_ui_render_target(plan.ui_render_target_width, plan.ui_render_target_height);
    }

    return pp::foundation::Status::success();
}

} // namespace pp::app
