#pragma once

#include "platform_api/platform_policy.h"
#include "platform_api/platform_services.h"

#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <vector>

#if defined(__OBJC__) && defined(__IOS__)
@class GameViewController;
@class AppDelegate;
#endif

#if defined(__OBJC__) && defined(__OSX__)
@class View;
@class AppOSX;
#endif

namespace pp::platform::apple {

struct RetainedLegacyAppleState final {
#if defined(__OBJC__) && defined(__IOS__)
    GameViewController* ios_view = nullptr;
    AppDelegate* ios_app = nullptr;
#elif defined(__OBJC__) && defined(__OSX__)
    View* osx_view = nullptr;
    AppOSX* osx_app = nullptr;
#endif
    pp::platform::PlatformStoragePaths storage_paths;
};

struct AppleDocumentPickerBridge {
    std::function<void(PickedPathCallback callback)> pick_image;
    std::function<void(std::vector<std::string> file_types, PickedPathCallback callback)> pick_file;
    std::function<void(std::vector<std::string> file_types, PickedPathCallback callback)> pick_save_file;
    std::function<void(PickedPathCallback callback)> pick_directory;
    std::function<std::string(std::string_view path)> format_working_directory_path;
    std::function<std::string()> clipboard_text;
    std::function<bool(std::string_view text)> set_clipboard_text;
    std::function<void(bool visible)> set_virtual_keyboard_visible;
    std::function<void(std::string path)> display_file;
    std::function<void(std::string path)> share_file;
    std::function<void(bool visible)> set_cursor_visible;
    std::function<void()> trigger_crash_test;
    std::function<void()> request_app_close;
    std::function<void()> start_sonarpen;
    std::function<void()> acquire_render_context;
    std::function<void()> release_render_context;
    std::function<void()> present_render_context;
    std::function<void()> bind_main_render_target;
    std::function<void(std::string path, std::string suggested_name, PreparedFileCallback callback)> save_prepared_file;
    std::function<void()> save_ui_state;
};

class AppleDocumentPlatformServices {
public:
    explicit AppleDocumentPlatformServices(
        PlatformFamily family,
        AppleDocumentPickerBridge bridge = {});

    [[nodiscard]] std::vector<std::string> document_browse_roots(
        std::string_view work_path,
        std::string_view data_path) const;

    void pick_image(PickedPathCallback callback) const;
    void pick_file(std::vector<std::string> file_types, PickedPathCallback callback) const;
    void pick_save_file(std::vector<std::string> file_types, PickedPathCallback callback) const;
    void pick_directory(PickedPathCallback callback) const;

    [[nodiscard]] bool supports_working_directory_picker() const;
    [[nodiscard]] std::string format_working_directory_path(std::string_view path) const;
    [[nodiscard]] std::string clipboard_text() const;
    [[nodiscard]] bool set_clipboard_text(std::string_view text) const;
    void set_virtual_keyboard_visible(bool visible) const;
    void display_file(std::string_view path) const;
    void share_file(std::string_view path) const;
    void set_cursor_visible(bool visible) const;
    void trigger_crash_test() const;
    void request_app_close() const;
    void start_sonarpen() const;
    void acquire_render_context() const;
    void release_render_context() const;
    void present_render_context() const;
    void bind_main_render_target() const;
    void save_prepared_file(
        std::string_view path,
        std::string_view suggested_name,
        PreparedFileCallback callback) const;
    void save_ui_state() const;

private:
    PlatformFamily family_;
    AppleDocumentPickerBridge bridge_;
};

class ApplePlatformServices final : public PlatformServices {
public:
    ApplePlatformServices() = default;

    [[nodiscard]] PlatformStoragePaths prepare_storage_paths() override;
    void log_stacktrace() override;
    void trigger_crash_test() override;
    [[nodiscard]] std::string clipboard_text() override;
    [[nodiscard]] bool set_clipboard_text(std::string_view text) override;
    void set_cursor_visible(bool visible) override;
    void set_virtual_keyboard_visible(bool visible) override;
    void attach_ui_thread() override;
    void detach_ui_thread() override;
    void acquire_render_context() override;
    void release_render_context() override;
    void present_render_context() override;
    void bind_default_render_target() override;
    void bind_main_render_target() override;
    void apply_render_platform_hints() override;
    void install_render_debug_callback() override;
    void begin_render_capture_frame() override;
    void end_render_capture_frame() override;
    [[nodiscard]] bool deletes_recorded_files_on_clear() override;
    void clear_recorded_files(std::string_view recording_path) override;
    void publish_exported_image(std::string_view path) override;
    void flush_persistent_storage() override;
    [[nodiscard]] std::vector<std::string> document_browse_roots(
        std::string_view work_path,
        std::string_view data_path) override;
    void save_ui_state() override;
    [[nodiscard]] bool enables_live_asset_reloading() override;
    void update_platform_frame(float delta_time_seconds) override;
    void report_rendered_frames(int frames) override;
    void pick_image(PickedPathCallback callback) override;
    void pick_file(std::vector<std::string> file_types, PickedPathCallback callback) override;
    void pick_save_file(std::vector<std::string> file_types, PickedPathCallback callback) override;
    void pick_directory(PickedPathCallback callback) override;
    [[nodiscard]] bool supports_working_directory_picker() override;
    [[nodiscard]] std::string format_working_directory_path(std::string_view path) override;
    [[nodiscard]] bool uses_prepared_file_writes() override;
    [[nodiscard]] bool uses_work_directory_document_export_collections() override;
    [[nodiscard]] bool disables_network_tls_verification() override;
    [[nodiscard]] bool uses_ppbr_export_data_directory_override() override;
    [[nodiscard]] bool supports_sonarpen() override;
    void start_sonarpen() override;
    [[nodiscard]] int default_canvas_resolution() override;
    [[nodiscard]] bool draws_canvas_tip_for_pointer(
        bool is_mouse,
        bool is_stylus,
        bool is_left_button_release) override;
    [[nodiscard]] float adjust_canvas_input_pressure(float pressure) override;
    [[nodiscard]] PreparedFileTarget prepare_writable_file(
        std::string_view type,
        std::string_view default_name,
        std::string_view data_path,
        std::string_view temporary_path) override;
    void display_file(std::string_view path) override;
    void share_file(std::string_view path) override;
    void request_app_close() override;
    [[nodiscard]] bool start_vr_mode() override;
    void stop_vr_mode() override;
    void save_prepared_file(
        std::string_view path,
        std::string_view suggested_name,
        PreparedFileCallback callback) override;
};

[[nodiscard]] std::unique_ptr<PlatformServices> create_apple_platform_services();
[[nodiscard]] RetainedLegacyAppleState& active_legacy_apple_state();
[[nodiscard]] AppleDocumentPlatformServices&
active_legacy_apple_document_platform_services();
[[nodiscard]] pp::platform::PlatformStoragePaths prepare_legacy_apple_storage_paths();
void set_legacy_apple_storage_paths(pp::platform::PlatformStoragePaths paths);
void set_legacy_apple_state(
#if defined(__OBJC__) && defined(__IOS__)
    GameViewController* ios_view,
    AppDelegate* ios_app
#elif defined(__OBJC__) && defined(__OSX__)
    View* osx_view,
    AppOSX* osx_app
#endif
);
[[nodiscard]] pp::platform::PlatformFamily legacy_apple_platform_family();

}
