#ifndef PORYGONLANG_TOKEN_HPP
#define PORYGONLANG_TOKEN_HPP

#include <utility>
#include <string>
#include "TokenKind.hpp"
#include "../Utilities/HashedString.hpp"

using namespace std;
using namespace Porygon::Utilities;

namespace Porygon::Parser {
    class Token {
        const unsigned int _position;
        const unsigned int _length;
    public:
        [[nodiscard]] virtual TokenKind GetKind() const = 0;

        Token(unsigned int position, unsigned int length)
                : _position(position), _length(length) {
        }

        [[nodiscard]] inline unsigned int GetStartPosition() const {
            return _position;
        }

        [[nodiscard]] inline unsigned int GetEndPosition() const {
            return _position + _length - 1;
        }

        [[nodiscard]] inline unsigned int GetLength() const {
            return _length;
        }

        virtual ~Token() = default;
    };

    class SimpleToken : public Token {
        const TokenKind _kind;
    public:

        explicit SimpleToken(TokenKind kind, unsigned int position, unsigned int length)
                : Token(position, length),
                  _kind(kind) {
        }

        [[nodiscard]] inline TokenKind GetKind() const final {
            return _kind;
        }
    };

    class IntegerToken : public Token {
        const long _value;
    public:

        explicit IntegerToken(long value, unsigned int position, unsigned int length)
                : Token(position, length),
                  _value(value) {
        }

        [[nodiscard]] inline TokenKind GetKind() const final {
            return TokenKind::Integer;
        }

        [[nodiscard]] inline long GetValue() const {
            return _value;
        }
    };

    class FloatToken : public Token {
        const double _value;
    public:

        explicit FloatToken(double value, unsigned int position, unsigned int length)
                : Token(position, length),
                  _value(value) {
        }

        [[nodiscard]] inline TokenKind GetKind() const final {
            return TokenKind::Float;
        }

        [[nodiscard]] inline double GetValue() const {
            return _value;
        }
    };

    class StringToken : public Token {
        const u16string _value;
    public:

        explicit StringToken(u16string value, unsigned int position, unsigned int length)
                : Token(position, length),
                  _value(std::move(value)) {
        }

        [[nodiscard]] inline TokenKind GetKind() const final {
            return TokenKind::String;
        }

        [[nodiscard]] inline const u16string &GetValue() const {
            return _value;
        }
    };

    class IdentifierToken : public Token {
        const Utilities::HashedString _value;
    public:

        explicit IdentifierToken(const HashedString& value, unsigned int position, unsigned int length)
                : Token(position, length),
                  _value(value) {
        }

        [[nodiscard]] inline TokenKind GetKind() const final {
            return TokenKind::Identifier;
        }

        [[nodiscard]] inline HashedString GetValue() const {
            return _value;
        }
    };
}

#endif //PORYGONLANG_TOKEN_HPP
