#pragma once

#include "Web_Event.h"

#include <json/json.h>

#include <concepts>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
#include <variant>

namespace renderive::web {

using Web_Transport_Events = std::variant<Frame_Request, Viewport_Resize, Gallery_Request>;
using Render_2D_Web_Events = std::variant<Event, Pointer_Event, Wheel_Event, Key_Event>;
using Web_Demo_Control_Events = std::variant<Set_Demo_Mode,
                                             Set_Center_Frequency,
                                             Set_Bandwidth,
                                             Set_Gain,
                                             Set_Max_Hold,
                                             Set_Smoothing,
                                             Clear_Selection>;

struct Web_Transport_Event_Decoder {
    using event_type = Web_Transport_Events;
    [[nodiscard]] static std::optional<event_type> decode(const Json::Value& root,
                                                          std::string_view source);
};

struct Render_2D_Web_Event_Decoder {
    using event_type = Render_2D_Web_Events;
    [[nodiscard]] static std::optional<event_type> decode(const Json::Value& root,
                                                          std::string_view source);
};

struct Web_Demo_Control_Decoder {
    using event_type = Web_Demo_Control_Events;
    [[nodiscard]] static std::optional<event_type> decode(const Json::Value& root,
                                                          std::string_view source);
};

template <class Decoder>
concept Web_Event_Decoder = requires(const Json::Value& root, std::string_view source) {
    typename Decoder::event_type;
    { Decoder::decode(root, source) } ->
        std::same_as<std::optional<typename Decoder::event_type>>;
};

[[nodiscard]] std::optional<Json::Value> parse_web_event(std::string_view message);

template <class T>
struct Is_Variant : std::false_type {};

template <class... Values>
struct Is_Variant<std::variant<Values...>> : std::true_type {};

template <class Event_Variant, Web_Event_Decoder... Decoders>
class Basic_Web_Event_Adapter final {
public:
    [[nodiscard]] static std::optional<Event_Variant> decode(std::string_view message) {
        const auto root = parse_web_event(message);
        if (!root)
            return std::nullopt;

        std::optional<Event_Variant> result;
        ([&] {
            if (result)
                return;
            auto decoded = Decoders::decode(*root, message);
            if (decoded)
                result = widen(std::move(*decoded));
        }(), ...);
        return result;
    }

private:
    template <class Decoded>
    static Event_Variant widen(Decoded decoded) {
        if constexpr (Is_Variant<std::remove_cvref_t<Decoded>>::value) {
            return std::visit([](auto&& value) -> Event_Variant {
                return Event_Variant(std::forward<decltype(value)>(value));
            }, std::move(decoded));
        } else {
            return Event_Variant(std::move(decoded));
        }
    }
};

using Web_Event_Adapter = Basic_Web_Event_Adapter<Web_Event,
                                                  Web_Transport_Event_Decoder,
                                                  Render_2D_Web_Event_Decoder,
                                                  Web_Demo_Control_Decoder>;

} // namespace renderive::web
