#ifndef ANGELSCRIPTDEBUGGER_EVENTS_HPP
#define ANGELSCRIPTDEBUGGER_EVENTS_HPP
#include "BaseProtocol.hpp"
#include "Types.hpp"
#include "Utils.hpp"

namespace DebugAdapterProtocol {
#define EventDefinition(name, jsonName, ...)                                                                           \
    static const char __c##name[] = jsonName;                                                                          \
    struct name##EventBody : public EventBody __VA_ARGS__;                                                             \
    using name##Event = Event<name##EventBody, __c##name>

    EventDefinition(Breakpoint, "breakpoint", {
        std::string reason;
        Breakpoint breakpoint;
        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["reason"] = reason;
            o["breakpoint"] = breakpoint.ToJson();
            return o;
        }
    });

    EventDefinition(Continued, "continued", {
        size_t threadId;
        std::optional<bool> allThreadsContinued;
        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["threadId"] = threadId;
            JsonSerializeOptional(o, allThreadsContinued);
            return o;
        }
    });

    EventDefinition(Exited, "exited", {
        int64_t exitCode;
        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["exitCode"] = exitCode;
            return o;
        }
    });

    EventDefinition(Initialized, "initialized", {});

    EventDefinition(Invalidated, "invalidated", {
        std::optional<std::vector<std::string>> invalidatedAreas;
        std::optional<size_t> threadId;
        std::optional<size_t> stackFrameId;

        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            JsonSerializeOptional(o, invalidatedAreas);
            JsonSerializeOptional(o, threadId);
            JsonSerializeOptional(o, stackFrameId);
            return o;
        }
    });

    EventDefinition(LoadedSource, "loadedSource", {
        std::string reason;
        Source source;
        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["reason"] = reason;
            o["source"] = source.ToJson();
            return o;
        }
    });

    EventDefinition(Memory, "memory", {
        std::string memoryReference;
        size_t offset;
        size_t count;
        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["memoryReference"] = memoryReference;
            o["offset"] = offset;
            o["count"] = count;
            return o;
        }
    });

    EventDefinition(Module, "module", {
        std::string reason;
        Module module;
        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["reason"] = reason;
            o["module"] = module.ToJson();
            return o;
        }
    });

    EventDefinition(Output, "output", {
        std::optional<std::string> category;
        std::string output;
        std::optional<std::string> group;
        std::optional<int32_t> variablesReference;
        std::optional<Source> source;
        std::optional<size_t> line;
        std::optional<size_t> column;
        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            JsonSerializeOptional(o, category);
            o["output"] = output;
            JsonSerializeOptional(o, group);
            JsonSerializeOptional(o, variablesReference);
            if (source.has_value()) {
                o["source"] = source.value().ToJson();
            }
            JsonSerializeOptional(o, line);
            JsonSerializeOptional(o, column);
            return o;
        }
    });

    EventDefinition(Process, "process", {
        std::string name;
        std::optional<size_t> systemProcessId;
        std::optional<bool> isLocalProcess;
        std::optional<std::string> startMethod;
        std::optional<size_t> pointerSize;

        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["name"] = name;
            JsonSerializeOptional(o, systemProcessId);
            JsonSerializeOptional(o, isLocalProcess);
            JsonSerializeOptional(o, startMethod);
            JsonSerializeOptional(o, pointerSize);
            return o;
        }
    });

    EventDefinition(ProgressEnd, "progressEnd", {
        std::string progressId;
        std::optional<std::string> message;

        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["progressId"] = progressId;
            JsonSerializeOptional(o, message);
            return o;
        }
    });

    EventDefinition(ProgressStart, "progressStart", {
        std::string progressId;
        std::string title;
        std::optional<size_t> requestId;
        std::optional<bool> cancellable;
        std::optional<std::string> message;
        std::optional<float> percentage;

        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["progressId"] = progressId;
            o["title"] = title;
            JsonSerializeOptional(o, requestId);
            JsonSerializeOptional(o, cancellable);
            JsonSerializeOptional(o, message);
            JsonSerializeOptional(o, percentage);
            return o;
        }
    });

    EventDefinition(ProgressUpdate, "progressUpdate", {
        std::string progressId;
        std::optional<std::string> message;
        std::optional<float> percentage;

        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["progressId"] = progressId;
            JsonSerializeOptional(o, message);
            JsonSerializeOptional(o, percentage);
            return o;
        }
    });

    EventDefinition(Stopped, "stopped", {
        std::string reason;
        std::optional<std::string> description;
        std::optional<size_t> threadId = 0;
        std::optional<bool> preserveFocusHint = false;
        std::optional<std::string> text;
        std::optional<bool> allThreadsStopped = true;
        std::optional<std::vector<size_t>> hitBreakpointIds;

        explicit StoppedEventBody(std::string reason, size_t breakpoint)
            : reason(std::move(reason)), hitBreakpointIds({breakpoint}) {}
        explicit StoppedEventBody(std::string reason, std::string description, std::string text)
            : reason(std::move(reason)), description(std::move(description)), text(std::move(text)) {}

        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["reason"] = reason;
            JsonSerializeOptional(o, description);
            JsonSerializeOptional(o, threadId);
            JsonSerializeOptional(o, preserveFocusHint);
            JsonSerializeOptional(o, text);
            JsonSerializeOptional(o, allThreadsStopped);
            JsonSerializeOptional(o, hitBreakpointIds);
            return o;
        }
    });

    EventDefinition(terminated, "terminated", {});

    EventDefinition(Thread, "thread", {
        std::string reason;
        std::optional<size_t> threadId;

        nlohmann::json ToJson() const override {
            auto o = EventBody::ToJson();
            o["reason"] = reason;
            JsonSerializeOptional(o, threadId);
            return o;
        }
    });

}

#endif // ANGELSCRIPTDEBUGGER_EVENTS_HPP
