// SPDX-License-Identifier: MIT
// Generic application mode interface.
// Copyright (C) 2025 Artem Senichev <artemsen@gmail.com>

#pragma once

#include "image.hpp"
#include "input.hpp"
#include "text.hpp"

#include <array>
#include <functional>
#include <map>
#include <optional>
#include <vector>

class AppMode {
public:
    /** Application mode types. */
    enum Type : uint8_t {
        Viewer,
        Slideshow,
        Gallery,
    };

    /** Image list event types. */
    enum class ImageListEvent : uint8_t {
        Create,
        Modify,
        Remove,
    };

    /** Input event handler. */
    using InputCallback = std::function<void()>;

    /** Unassigned kay input event handler. */
    using UnassignedKeyCallback =
        std::function<void(const InputKeyboard& input)>;

    /** Constructor. */
    AppMode();

    /**
     * Initialize mode instance.
     */
    virtual void initialize() = 0;

    /**
     * Activate mode.
     * @param entry image entry to show/select
     * @param wnd window size
     */
    virtual void activate(const ImageEntryPtr& entry, const Size& wnd) = 0;

    /**
     * Deactivate mode.
     */
    virtual void deactivate() = 0;

    /**
     * Get currently showed/selected image entry.
     * @return current image entry
     */
    virtual ImageEntryPtr get_current() = 0;

    /**
     * Set currently showed/selected image entry.
     * @param entry desired image entry
     * @return true if image was opened/selected
     */
    virtual bool set_current(const ImageEntryPtr& entry) = 0;

    /**
     * Window resize handler.
     * @param wnd window size
     */
    virtual void window_resize(const Size& /*wnd*/) {}

    /**
     * Window resize handler.
     * @param wnd window surface pixmap
     */
    virtual void window_redraw(Pixmap& wnd) = 0;

    /**
     * Handle key press event.
     * @param input input event description
     */
    void handle_keyboard(const InputKeyboard& input);

    /**
     * Handle mouse click.
     * @param input input event description
     * @param pos mouse pointer coordinates
     */
    void handle_mclick(const InputMouse& input, const Point& pos);

    /**
     * Handle mouse move.
     * @param input input event description
     * @param pos mouse pointer coordinates
     * @param delta diff between this and last call
     */
    virtual void handle_mmove(const InputMouse& input, const Point& pos,
                              const Point& delta) = 0;

    /**
     * Handle pinch gesture.
     * @param scale_delta scale diff
     */
    virtual void handle_pinch(const double scale_delta) = 0;

    /**
     * Handle signal.
     * @param input input event description
     * @return false if event not supported
     */
    virtual bool handle_signal(const InputSignal& input);

    /**
     * Handle image list changes.
     * @param event event type
     * @param entries array of added/removed/updated image entries
     */
    virtual void handle_imagelist(const ImageListEvent event,
                                  const std::vector<ImageEntryPtr>& entries);

    /**
     * Check if current mode is active.
     * @return true if current mode is active
     */
    [[nodiscard]] bool is_active() const;

    /**
     * Set or toggle mark state for current image.
     * @param state state to set, `std::nullopt` to toggle
     */
    void mark_current(const std::optional<bool>& state);

    /**
     * Set mark icon color.
     * @param color mark icon color
     */
    void set_mark_color(const argb_t& color);

    /**
     * Set the pinch gesture factor.
     * @param factor pinch gesture factor
     */
    void set_pinch_factor(const double factor);

    /**
     * Set text layer scheme.
     * @param pos block position
     * @param scheme scheme description
     */
    void set_text_scheme(const Text::Position pos, const Text::Scheme& scheme);

    /**
     * Remove all bindings: keyboard/mouse/signals.
     */
    void bind_reset();

    /**
     * Bind key press event.
     * @param input event description
     * @param handler callback
     */
    void bind_input(const InputKeyboard& input, const InputCallback& handler);

    /**
     * Bind mouse click event.
     * @param input event description
     * @param handler callback
     */
    void bind_input(const InputMouse& input, const InputCallback& handler);

    /**
     * Bind signal event.
     * @param input event description
     * @param handler callback
     */
    void bind_input(const InputSignal& input, const InputCallback& handler);

public:
    std::function<void()> on_image_change;   ///< Image switch callback
    UnassignedKeyCallback on_unassigned_key; ///< Unassigned key handler

protected:
    /**
     * Switch to another image.
     */
    void switch_current();

    /**
     * Draw placeholder for empty image list.
     * @param wnd window surface pixmap
     * @param bkg window background color
     */
    static void draw_empty(Pixmap& wnd, const argb_t bkg);

protected:
    argb_t mark_color;                       ///< Mark icon color
    double pinch_factor;                     ///< Pinch gesture factor
    std::array<Text::Scheme, 4> text_scheme; ///< Text layer scheme

private:
    std::map<InputKeyboard, InputCallback> kbindings; ///< Keyboard bindings
    std::map<InputMouse, InputCallback> mbindings;    ///< Mouse bindings
    std::map<InputSignal, InputCallback> sbindings;   ///< Signal bindings
};
