#pragma once

#include "foundation/result.h"

#include <cmath>

namespace pp::app {

enum class QuickUiSlotKind {
    brush,
    color,
};

enum class QuickUiOperation {
    select_slot,
    open_slot_popup,
    restore_state,
    reset_state,
};

struct QuickUiPlan {
    QuickUiOperation operation = QuickUiOperation::select_slot;
    QuickUiSlotKind slot_kind = QuickUiSlotKind::brush;
    int slot_index = 0;
    int previous_index = 0;
    int brush_index = 0;
    int color_index = 0;
    int slot_count = 0;
    bool fire_event = false;
    bool updates_selection = false;
    bool opens_brush_popup = false;
    bool opens_color_picker = false;
    bool invokes_change_callback = false;
    bool restores_slots = false;
    bool resets_slots = false;
    bool redraws_brush_previews = false;
    bool mutates_quick_state = false;
};

struct QuickSliderPreviewInput {
    bool ui_rtl = false;
    float slider_x = 0.0F;
    float slider_y = 0.0F;
    float slider_height = 0.0F;
    float zoom = 1.0F;
    bool has_pen_mode = false;
    bool has_line_mode = false;
};

struct QuickSliderPreviewPlan {
    float cursor_x = 0.0F;
    float cursor_y = 0.0F;
    bool updates_pen_mode = false;
    bool updates_line_mode = false;
    bool draws_tip = false;
    bool disables_pen_outline = false;
    bool redraws_brush_preview = false;
    bool invokes_change_callback = false;
};

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

    virtual void select_slot(QuickUiSlotKind slot_kind, int slot_index, bool fire_event) = 0;
    virtual void open_slot_popup(QuickUiSlotKind slot_kind, int slot_index) = 0;
    virtual void restore_state(int brush_index, int color_index, bool fire_event) = 0;
    virtual void reset_state(bool fire_event) = 0;
};

[[nodiscard]] inline pp::foundation::Status validate_quick_slider_float(float value) noexcept
{
    if (!std::isfinite(value)) {
        return pp::foundation::Status::invalid_argument("quick slider preview value must be finite");
    }

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

[[nodiscard]] inline pp::foundation::Status validate_quick_slot_count(int slot_count) noexcept
{
    if (slot_count <= 0) {
        return pp::foundation::Status::out_of_range("quick slot count must be greater than zero");
    }

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

[[nodiscard]] inline pp::foundation::Status validate_quick_slot_index(int slot_index, int slot_count) noexcept
{
    const auto count_status = validate_quick_slot_count(slot_count);
    if (!count_status.ok()) {
        return count_status;
    }

    if (slot_index < 0 || slot_index >= slot_count) {
        return pp::foundation::Status::out_of_range("quick slot index is out of range");
    }

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

[[nodiscard]] inline pp::foundation::Result<QuickSliderPreviewPlan> plan_quick_slider_preview(
    const QuickSliderPreviewInput& input)
{
    const auto x_status = validate_quick_slider_float(input.slider_x);
    if (!x_status.ok()) {
        return pp::foundation::Result<QuickSliderPreviewPlan>::failure(x_status);
    }
    const auto y_status = validate_quick_slider_float(input.slider_y);
    if (!y_status.ok()) {
        return pp::foundation::Result<QuickSliderPreviewPlan>::failure(y_status);
    }
    const auto height_status = validate_quick_slider_float(input.slider_height);
    if (!height_status.ok()) {
        return pp::foundation::Result<QuickSliderPreviewPlan>::failure(height_status);
    }
    if (input.slider_height < 0.0F) {
        return pp::foundation::Result<QuickSliderPreviewPlan>::failure(
            pp::foundation::Status::invalid_argument("quick slider preview height must not be negative"));
    }
    const auto zoom_status = validate_quick_slider_float(input.zoom);
    if (!zoom_status.ok()) {
        return pp::foundation::Result<QuickSliderPreviewPlan>::failure(zoom_status);
    }
    if (input.zoom <= 0.0F) {
        return pp::foundation::Result<QuickSliderPreviewPlan>::failure(
            pp::foundation::Status::invalid_argument("quick slider preview zoom must be positive"));
    }

    const float offset = input.ui_rtl ? -100.0F : 100.0F;
    QuickSliderPreviewPlan plan;
    plan.cursor_x = (input.slider_x + offset) * input.zoom;
    plan.cursor_y = (input.slider_y + input.slider_height * 0.5F) * input.zoom;
    plan.updates_pen_mode = input.has_pen_mode;
    plan.updates_line_mode = input.has_line_mode;
    plan.draws_tip = input.has_pen_mode || input.has_line_mode;
    plan.disables_pen_outline = input.has_pen_mode;
    plan.redraws_brush_preview = true;
    plan.invokes_change_callback = true;
    return pp::foundation::Result<QuickSliderPreviewPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Result<QuickUiPlan> plan_quick_slot_click(
    QuickUiSlotKind slot_kind,
    int current_index,
    int clicked_index,
    int slot_count)
{
    const auto current_status = validate_quick_slot_index(current_index, slot_count);
    if (!current_status.ok()) {
        return pp::foundation::Result<QuickUiPlan>::failure(current_status);
    }

    const auto clicked_status = validate_quick_slot_index(clicked_index, slot_count);
    if (!clicked_status.ok()) {
        return pp::foundation::Result<QuickUiPlan>::failure(clicked_status);
    }

    QuickUiPlan plan;
    plan.slot_kind = slot_kind;
    plan.slot_index = clicked_index;
    plan.previous_index = current_index;
    plan.brush_index = slot_kind == QuickUiSlotKind::brush ? clicked_index : 0;
    plan.color_index = slot_kind == QuickUiSlotKind::color ? clicked_index : 0;
    plan.slot_count = slot_count;
    if (clicked_index != current_index) {
        plan.operation = QuickUiOperation::select_slot;
        plan.updates_selection = true;
        plan.invokes_change_callback = true;
        plan.mutates_quick_state = true;
        return pp::foundation::Result<QuickUiPlan>::success(plan);
    }

    plan.operation = QuickUiOperation::open_slot_popup;
    plan.opens_brush_popup = slot_kind == QuickUiSlotKind::brush;
    plan.opens_color_picker = slot_kind == QuickUiSlotKind::color;
    return pp::foundation::Result<QuickUiPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Result<QuickUiPlan> plan_quick_state_restore(
    int brush_index,
    int color_index,
    int slot_count,
    bool fire_event)
{
    const auto brush_status = validate_quick_slot_index(brush_index, slot_count);
    if (!brush_status.ok()) {
        return pp::foundation::Result<QuickUiPlan>::failure(brush_status);
    }

    const auto color_status = validate_quick_slot_index(color_index, slot_count);
    if (!color_status.ok()) {
        return pp::foundation::Result<QuickUiPlan>::failure(color_status);
    }

    QuickUiPlan plan;
    plan.operation = QuickUiOperation::restore_state;
    plan.brush_index = brush_index;
    plan.color_index = color_index;
    plan.slot_count = slot_count;
    plan.fire_event = fire_event;
    plan.updates_selection = true;
    plan.invokes_change_callback = fire_event;
    plan.restores_slots = true;
    plan.redraws_brush_previews = true;
    plan.mutates_quick_state = true;
    return pp::foundation::Result<QuickUiPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Result<QuickUiPlan> plan_quick_state_reset(
    int slot_count,
    bool fire_event)
{
    const auto count_status = validate_quick_slot_count(slot_count);
    if (!count_status.ok()) {
        return pp::foundation::Result<QuickUiPlan>::failure(count_status);
    }

    QuickUiPlan plan;
    plan.operation = QuickUiOperation::reset_state;
    plan.brush_index = 0;
    plan.color_index = 0;
    plan.slot_count = slot_count;
    plan.fire_event = fire_event;
    plan.updates_selection = true;
    plan.invokes_change_callback = fire_event;
    plan.resets_slots = true;
    plan.redraws_brush_previews = true;
    plan.mutates_quick_state = true;
    return pp::foundation::Result<QuickUiPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Status execute_quick_ui_plan(
    const QuickUiPlan& plan,
    QuickUiServices& services)
{
    switch (plan.operation) {
    case QuickUiOperation::select_slot:
        if (!plan.updates_selection) {
            return pp::foundation::Status::invalid_argument("quick select plan must update selection");
        }
        {
            const auto status = validate_quick_slot_index(plan.slot_index, plan.slot_count);
            if (!status.ok()) {
                return status;
            }
        }
        services.select_slot(plan.slot_kind, plan.slot_index, plan.invokes_change_callback);
        return pp::foundation::Status::success();

    case QuickUiOperation::open_slot_popup:
        if (plan.slot_kind == QuickUiSlotKind::brush && !plan.opens_brush_popup) {
            return pp::foundation::Status::invalid_argument("quick brush popup plan must open brush popup");
        }
        if (plan.slot_kind == QuickUiSlotKind::color && !plan.opens_color_picker) {
            return pp::foundation::Status::invalid_argument("quick color popup plan must open color picker");
        }
        {
            const auto status = validate_quick_slot_index(plan.slot_index, plan.slot_count);
            if (!status.ok()) {
                return status;
            }
        }
        services.open_slot_popup(plan.slot_kind, plan.slot_index);
        return pp::foundation::Status::success();

    case QuickUiOperation::restore_state:
        if (!plan.restores_slots) {
            return pp::foundation::Status::invalid_argument("quick restore plan must restore slots");
        }
        {
            const auto brush_status = validate_quick_slot_index(plan.brush_index, plan.slot_count);
            if (!brush_status.ok()) {
                return brush_status;
            }
            const auto color_status = validate_quick_slot_index(plan.color_index, plan.slot_count);
            if (!color_status.ok()) {
                return color_status;
            }
        }
        services.restore_state(plan.brush_index, plan.color_index, plan.fire_event);
        return pp::foundation::Status::success();

    case QuickUiOperation::reset_state:
        if (!plan.resets_slots) {
            return pp::foundation::Status::invalid_argument("quick reset plan must reset slots");
        }
        {
            const auto status = validate_quick_slot_count(plan.slot_count);
            if (!status.ok()) {
                return status;
            }
        }
        services.reset_state(plan.fire_event);
        return pp::foundation::Status::success();
    }

    return pp::foundation::Status::invalid_argument("unknown quick UI operation");
}

} // namespace pp::app
