#pragma once

#include "app_core/app_dialog.h"
#include "foundation/result.h"
#include "ui_core/overlay_lifetime.h"
#include "node.h"
#include "node_image.h"
#include "shader.h"

#include <functional>
#include <memory>

class App;
class NodeInputBox;
class NodeMessageBox;
class NodePopupMenu;
class NodeProgressBar;
class NodeSettings;
class NodeDialogCloud;

namespace pp::panopainter {

inline void configure_legacy_slider_color_shader(const glm::mat4& mvp) noexcept
{
    ShaderManager::use(kShader::Color);
    ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
    ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 });
}

inline void configure_legacy_slider_hue_shader(const glm::mat4& mvp, int direction) noexcept
{
    ShaderManager::use(kShader::ColorHue);
    ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
    ShaderManager::u_int(kShaderUniform::Direction, direction);
}

inline void configure_legacy_ui_color_shader(
    const glm::mat4& mvp,
    const glm::vec4& color) noexcept
{
    ShaderManager::use(kShader::Color);
    ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
    ShaderManager::u_vec4(kShaderUniform::Col, color);
}

inline void configure_legacy_ui_color_shader(
    const glm::mat4& mvp) noexcept
{
    configure_legacy_ui_color_shader(mvp, { 0, 0, 0, 1 });
}

inline void configure_legacy_grid_lambert_shader(
    const glm::mat4& mvp,
    const glm::vec3& light_dir,
    float ambient) noexcept
{
    ShaderManager::use(kShader::Lambert);
    ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
    ShaderManager::u_vec3(kShaderUniform::LightDir, light_dir);
    ShaderManager::u_float(kShaderUniform::Ambient, ambient);
}

inline void configure_legacy_grid_lambert_lightmap_shader(
    const glm::mat4& mvp,
    const glm::vec3& light_dir,
    float ambient,
    int texture_slot) noexcept
{
    ShaderManager::use(kShader::LambertLightmap);
    ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
    ShaderManager::u_vec3(kShaderUniform::LightDir, light_dir);
    ShaderManager::u_float(kShaderUniform::Ambient, ambient);
    ShaderManager::u_int(kShaderUniform::Tex, texture_slot);
}

inline void configure_legacy_grid_bake_uv_shader(
    const glm::mat4& mvp) noexcept
{
    ShaderManager::use(kShader::BakeUV);
    ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
}

inline void configure_legacy_ui_color_quad_shader(
    const glm::mat4& mvp,
    const glm::vec4& color) noexcept
{
    ShaderManager::use(kShader::ColorQuad);
    ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
    ShaderManager::u_vec4(kShaderUniform::Col, color);
}

inline void configure_legacy_ui_font_shader(
    const glm::mat4& mvp,
    const glm::vec4& color) noexcept
{
    ShaderManager::use(kShader::Font);
    ShaderManager::u_int(kShaderUniform::Tex, 0);
    ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
    ShaderManager::u_vec4(kShaderUniform::Col, color);
}

void initialize_legacy_overlay_node(App& app, Node& node);

void destroy_legacy_node(Node& node);
void detach_legacy_node_from_parent(Node& node);
void close_legacy_dialog_node(Node& node);
void release_legacy_mouse_capture(Node& node) noexcept;
void configure_legacy_popup_overlay(Node& node) noexcept;
void activate_legacy_popup_overlay(Node& node) noexcept;
void close_legacy_popup_overlay(Node& node) noexcept;
void close_legacy_overlay_handle_ignoring_status(
    Node& anchor,
    pp::ui::NodeHandle overlay) noexcept;
void close_legacy_overlay_handle_and_reset(
    Node& anchor,
    pp::ui::NodeHandle& overlay) noexcept;
void close_legacy_overlay_handles_and_reset(
    Node& anchor,
    pp::ui::NodeHandle& popup_overlay,
    pp::ui::NodeHandle& tick_overlay) noexcept;
void close_legacy_overlay_handles_if_open(
    Node& anchor,
    const pp::foundation::Result<pp::ui::NodeHandle>& popup_overlay,
    const pp::foundation::Result<pp::ui::NodeHandle>& tick_overlay) noexcept;
void close_legacy_dialog_and_hide_keyboard(App& app, Node& node);
void close_legacy_popup_panel(
    Node& node,
    const std::function<void(Node*)>& on_close);

[[nodiscard]] pp::foundation::Status attach_legacy_overlay_node(
    App& app,
    const std::shared_ptr<Node>& node) noexcept;

[[nodiscard]] pp::foundation::Status attach_legacy_overlay_node_to_root(
    Node& anchor,
    const std::shared_ptr<Node>& node) noexcept;

[[nodiscard]] pp::foundation::Result<pp::ui::NodeHandle> open_legacy_overlay_node_with_handle(
    Node& anchor,
    const std::shared_ptr<Node>& node,
    bool modal = true) noexcept;

[[nodiscard]] pp::foundation::Status close_legacy_overlay_node(
    Node& anchor,
    pp::ui::NodeHandle overlay) noexcept;

[[nodiscard]] pp::foundation::Result<std::shared_ptr<NodePopupMenu>> add_legacy_popup_menu(
    App& app,
    const char* template_id,
    float x,
    float y,
    float rtl_anchor_width) noexcept;

[[nodiscard]] std::shared_ptr<NodeProgressBar> create_legacy_progress_dialog_overlay(
    App& app,
    const pp::app::AppProgressDialogPlan& plan);

[[nodiscard]] std::shared_ptr<NodeMessageBox> create_legacy_message_dialog_overlay(
    App& app,
    const pp::app::AppMessageDialogPlan& plan);

[[nodiscard]] std::shared_ptr<NodeInputBox> create_legacy_input_dialog_overlay(
    App& app,
    const pp::app::AppInputDialogPlan& plan);

[[nodiscard]] std::shared_ptr<NodeSettings> create_legacy_settings_dialog_overlay(
    App& app);

[[nodiscard]] std::shared_ptr<NodeDialogCloud> create_legacy_cloud_browser_dialog_overlay(
    App& app);

template <class T>
std::shared_ptr<T> make_legacy_overlay_node(App& app)
{
    auto node = std::make_shared<T>();
    initialize_legacy_overlay_node(app, *node);
    return node;
}

template <class T>
std::shared_ptr<T> make_legacy_overlay_node_for_anchor(Node& anchor)
{
    auto node = std::make_shared<T>();
    node->set_manager(anchor.m_manager);
    node->init();
    node->create();
    node->loaded();
    return node;
}

template <class T>
std::shared_ptr<T> add_legacy_overlay_node(App& app)
{
    auto node = make_legacy_overlay_node<T>(app);
    (void)attach_legacy_overlay_node(app, node);
    return node;
}

template <typename PopupT>
concept LegacyPopupOverlay = requires(PopupT& popup, const std::function<void(Node*)>& close_cb) {
    popup.on_popup_close = close_cb;
};

template <LegacyPopupOverlay PopupT>
bool open_popup_and_tick_overlay(
    Node& anchor,
    const std::shared_ptr<PopupT>& popup,
    const glm::vec2& tick_pos,
    float button_height) noexcept
{
    if (!popup) {
        return false;
    }

    auto tick = make_legacy_overlay_node_for_anchor<NodeImage>(anchor);
    tick->SetPositioning(YGPositionTypeAbsolute);
    tick->SetSize(16, 32);
    tick->SetPosition(tick_pos.x, tick_pos.y + (button_height - 32) * 0.5f);
    tick->set_image("data/ui/popup-tick.png");
    tick->m_scale = { 1, 1 };

    const auto popup_overlay = open_legacy_overlay_node_with_handle(anchor, popup);
    const auto tick_overlay = open_legacy_overlay_node_with_handle(anchor, tick);
    if (!popup_overlay || !tick_overlay) {
        close_legacy_overlay_handles_if_open(anchor, popup_overlay, tick_overlay);
        return false;
    }

    const auto popup_handle = popup_overlay.value();
    const auto tick_handle = tick_overlay.value();
    popup->on_popup_close = [&anchor, popup_handle, tick_handle](Node*) {
        close_legacy_overlay_handle_ignoring_status(anchor, popup_handle);
        close_legacy_overlay_handle_ignoring_status(anchor, tick_handle);
    };

    return true;
}

template <class PopupT>
void bind_legacy_popup_close_destroys_overlay(
    PopupT& popup,
    const std::shared_ptr<Node>& overlay) noexcept
{
    popup.on_popup_close = [overlay](Node*) {
        if (overlay) {
            destroy_legacy_node(*overlay);
        }
    };
}

template <class ButtonT>
void bind_legacy_click_destroys_node(ButtonT& button, Node& target) noexcept
{
    button.on_click = [&target](Node*) {
        close_legacy_dialog_node(target);
    };
}

inline std::function<void(Node*)> legacy_destroy_node_callback(Node& target)
{
    return [&target](Node*) {
        close_legacy_dialog_node(target);
    };
}

} // namespace pp::panopainter
