#pragma once

#include "app_core/canvas_tool_ui.h"
#include "app_core/document_session.h"
#include "app_core/history_ui.h"
#include "foundation/result.h"

namespace pp::app {

enum class CanvasHotkeyEvent {
    key_down,
    key_up,
    touch_tap,
};

enum class CanvasHotkeyKey {
    other,
    android_back,
    alt,
    e,
    s,
    tab,
    z,
    bracket_left,
    bracket_right,
};

enum class CanvasHotkeyAction {
    none,
    select_tool,
    history,
    save_document,
    toggle_ui,
    adjust_brush_size,
    show_cursor,
};

struct CanvasHotkeyState {
    bool ctrl_down = false;
    bool shift_down = false;
    bool mouse_focused = false;
    int undo_count = 0;
    int redo_count = 0;
    int touch_finger_count = 0;
};

struct CanvasHotkeyPlan {
    CanvasHotkeyAction action = CanvasHotkeyAction::none;
    CanvasHotkeyEvent event = CanvasHotkeyEvent::key_up;
    CanvasHotkeyKey key = CanvasHotkeyKey::other;
    CanvasToolPlan tool;
    HistoryUiPlan history;
    DocumentSaveIntent save_intent = DocumentSaveIntent::save;
    float brush_size_delta = 0.0F;
    bool no_op = true;
};

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

    virtual pp::foundation::Status execute_tool(const CanvasToolPlan& plan) = 0;
    virtual pp::foundation::Status execute_history(const HistoryUiPlan& plan) = 0;
    virtual void save_document(DocumentSaveIntent intent) = 0;
    virtual void toggle_ui() = 0;
    virtual void adjust_brush_size(float delta) = 0;
    virtual void show_cursor() = 0;
};

[[nodiscard]] inline pp::foundation::Status validate_canvas_hotkey_state(
    const CanvasHotkeyState& state) noexcept
{
    if (state.undo_count < 0) {
        return pp::foundation::Status::out_of_range("undo action count must not be negative");
    }
    if (state.redo_count < 0) {
        return pp::foundation::Status::out_of_range("redo action count must not be negative");
    }
    if (state.touch_finger_count < 0) {
        return pp::foundation::Status::out_of_range("touch finger count must not be negative");
    }
    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Result<CanvasHotkeyPlan> plan_canvas_hotkey(
    CanvasHotkeyEvent event,
    CanvasHotkeyKey key,
    const CanvasHotkeyState& state)
{
    const auto state_status = validate_canvas_hotkey_state(state);
    if (!state_status.ok()) {
        return pp::foundation::Result<CanvasHotkeyPlan>::failure(state_status);
    }

    CanvasHotkeyPlan plan;
    plan.event = event;
    plan.key = key;

    if (event == CanvasHotkeyEvent::touch_tap) {
        if (state.touch_finger_count == 2) {
            auto history = plan_history_undo(state.undo_count);
            if (!history) {
                return pp::foundation::Result<CanvasHotkeyPlan>::failure(history.status());
            }
            plan.action = CanvasHotkeyAction::history;
            plan.history = history.value();
            plan.no_op = plan.history.no_op;
        }
        return pp::foundation::Result<CanvasHotkeyPlan>::success(plan);
    }

    if (event == CanvasHotkeyEvent::key_down) {
        switch (key) {
        case CanvasHotkeyKey::e:
            plan.action = CanvasHotkeyAction::select_tool;
            plan.tool = plan_canvas_tool_select(CanvasToolMode::erase);
            plan.no_op = false;
            break;
        case CanvasHotkeyKey::android_back: {
            auto history = plan_history_undo(state.undo_count);
            if (!history) {
                return pp::foundation::Result<CanvasHotkeyPlan>::failure(history.status());
            }
            plan.action = CanvasHotkeyAction::history;
            plan.history = history.value();
            plan.no_op = plan.history.no_op;
            break;
        }
        case CanvasHotkeyKey::alt:
            if (state.mouse_focused) {
                plan.action = CanvasHotkeyAction::show_cursor;
                plan.no_op = false;
            }
            break;
        default:
            break;
        }
        return pp::foundation::Result<CanvasHotkeyPlan>::success(plan);
    }

    switch (key) {
    case CanvasHotkeyKey::e:
        plan.action = CanvasHotkeyAction::select_tool;
        plan.tool = plan_canvas_tool_select(CanvasToolMode::draw);
        plan.no_op = false;
        break;
    case CanvasHotkeyKey::tab:
        plan.action = CanvasHotkeyAction::toggle_ui;
        plan.no_op = false;
        break;
    case CanvasHotkeyKey::z:
        if (state.ctrl_down) {
            auto history = state.shift_down
                ? plan_history_redo(state.redo_count)
                : plan_history_undo(state.undo_count);
            if (!history) {
                return pp::foundation::Result<CanvasHotkeyPlan>::failure(history.status());
            }
            plan.action = CanvasHotkeyAction::history;
            plan.history = history.value();
            plan.no_op = plan.history.no_op;
        }
        break;
    case CanvasHotkeyKey::s:
        if (state.ctrl_down) {
            plan.action = CanvasHotkeyAction::save_document;
            plan.save_intent = state.shift_down
                ? DocumentSaveIntent::save_dirty_version
                : DocumentSaveIntent::save;
            plan.no_op = false;
        }
        break;
    case CanvasHotkeyKey::bracket_left:
        plan.action = CanvasHotkeyAction::adjust_brush_size;
        plan.brush_size_delta = -0.05F;
        plan.no_op = false;
        break;
    case CanvasHotkeyKey::bracket_right:
        plan.action = CanvasHotkeyAction::adjust_brush_size;
        plan.brush_size_delta = 0.05F;
        plan.no_op = false;
        break;
    default:
        break;
    }

    return pp::foundation::Result<CanvasHotkeyPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Status execute_canvas_hotkey_plan(
    const CanvasHotkeyPlan& plan,
    CanvasHotkeyServices& services)
{
    if (plan.no_op || plan.action == CanvasHotkeyAction::none) {
        return pp::foundation::Status::success();
    }

    switch (plan.action) {
    case CanvasHotkeyAction::select_tool:
        return services.execute_tool(plan.tool);
    case CanvasHotkeyAction::history:
        return services.execute_history(plan.history);
    case CanvasHotkeyAction::save_document:
        services.save_document(plan.save_intent);
        return pp::foundation::Status::success();
    case CanvasHotkeyAction::toggle_ui:
        services.toggle_ui();
        return pp::foundation::Status::success();
    case CanvasHotkeyAction::adjust_brush_size:
        if (plan.brush_size_delta == 0.0F) {
            return pp::foundation::Status::invalid_argument("brush-size hotkey plan must include a delta");
        }
        services.adjust_brush_size(plan.brush_size_delta);
        return pp::foundation::Status::success();
    case CanvasHotkeyAction::show_cursor:
        services.show_cursor();
        return pp::foundation::Status::success();
    case CanvasHotkeyAction::none:
        return pp::foundation::Status::success();
    }

    return pp::foundation::Status::invalid_argument("unknown canvas hotkey action");
}

} // namespace pp::app
