#pragma once
#include <unordered_map>
#include <vector>
#include "../base/interface.hpp"
#include "../../protocol/protocol.hpp"
#include "../../service/document.hpp"

namespace lsp::provider::text_document
{
    enum class SemanticTokenType: protocol::uinteger
    {
        kNamespace = 0,
        kType = 1,
        kClass = 2,
        kEnum = 3,
        kInterface = 4,
        kStruct = 5,
        kTypeParameter = 6,
        kParameter = 7,
        kVariable = 8,
        kProperty = 9,
        kEnumMember = 10,
        kEvent = 11,
        kFunction = 12,
        kMethod = 13,
        kMacro = 14,
        kKeyword = 15,
        kModifier = 16,
        kComment = 17,
        kString = 18,
        kNumber = 19,
        kRegexp = 20,
        kOperator = 21,
        kDecorator = 22
    };

    enum class SemanticTokenModifier: protocol::uinteger
    {
        kDeclaration = 1 << 0,
        kDefinition = 1 << 1,
        kReadonly = 1 << 2,
        kStatic = 1 << 3,
        kDeprecated = 1 << 4,
        kAbstract = 1 << 5,
        kAsync = 1 << 6,
        kModification = 1 << 7,
        kDocumentation = 1 << 8,
        kDefault_library = 1 << 9
    };

    class SemanticToken
    {
    public:
        SemanticToken(protocol::uinteger l, protocol::uinteger c, protocol::uinteger len, SemanticTokenType t, protocol::uinteger mod);

        protocol::uinteger line;
        protocol::uinteger character;
        protocol::uinteger length;
        SemanticTokenType type;
        protocol::uinteger modifiers;
    };

    class SemanticTokensCache
    {
    public:
        SemanticTokensCache(const std::string& id, std::vector<uint32_t> tokens, const std::string& version);

        std::string result_id;
        std::vector<uint32_t> data;
        std::string document_version;
    };

    class SemanticTokensCore
    {
    public:
        SemanticTokensCore(const SemanticTokensCore&) = delete;
        SemanticTokensCore& operator=(const SemanticTokensCore&) = delete;

        static SemanticTokensCore& GetInstance();

        std::string HandleRange(const protocol::RequestMessage& request, ExecutionContext& context);
        std::string HandleFull(const protocol::RequestMessage& request, ExecutionContext& context);
        std::string HandleDelta(const protocol::RequestMessage& request, ExecutionContext& context);

    private:
        SemanticTokensCore();

        std::vector<SemanticToken> CollectTokensInRange(TSNode root, const std::string& source, const protocol::Range* range = nullptr);
        void TraverseNode(TSNode node, const std::string& source_code, std::vector<SemanticToken>& tokens, const protocol::Range* range);
        void ProcessNode(TSNode node, std::vector<SemanticToken>& tokens);

        SemanticTokenType DetermineIdentifierType(TSNode node);
        uint32_t DetermineModifiers(TSNode node);
        std::vector<protocol::uinteger> EncodeTokens(const std::vector<SemanticToken>& tokens);
        std::vector<protocol::SemanticTokensEdit> ComputeDelta(const std::vector<protocol::uinteger>& old_data, const std::vector<protocol::uinteger>& new_data);

        void InitNodeTypeMap();
        std::string GenerateResultId(const std::string& uri, const std::string& version);
        bool IsNodeInRange(TSPoint start, TSPoint end, const protocol::Range& range);

        static std::unordered_map<std::string, SemanticTokensCache> cache_;
        static std::mutex cache_mutex_;
        std::unordered_map<std::string, SemanticTokenType> node_type_map_;
    };

    class SemanticTokensRange : public IRequestProvider
    {
    public:
        std::string GetMethod() const override;
        std::string GetProviderName() const override;
        std::string ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context) override;
    };

    class SemanticTokensFull : public IRequestProvider
    {
    public:
        std::string GetMethod() const override;
        std::string GetProviderName() const override;
        std::string ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context) override;
    };

    class SemanticTokensFullDelta : public IRequestProvider
    {
    public:
        std::string GetMethod() const override;
        std::string GetProviderName() const override;
        std::string ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context) override;
    };
}
