#pragma once

#include <cstdint>
#include <string>
#include <variant>

namespace sliger {

enum class ValueType {
    Null,
    Int,
    Bool,
    String,
    Ptr,
};

class Values;

struct Null { };
struct Int {
    int32_t value;
};
struct Bool {
    bool value;
};
struct String {
    std::string value;
};
struct Ptr {
    uint32_t value;
};

class Value {
public:
    Value(Null&& value)
        : m_type(ValueType::Null)
        , value(value)
    {
    }
    Value(Int&& value)
        : m_type(ValueType::Int)
        , value(value)
    {
    }
    Value(Bool&& value)
        : m_type(ValueType::Bool)
        , value(value)
    {
    }
    Value(String&& value)
        : m_type(ValueType::String)
        , value(value)
    {
    }
    Value(Ptr&& value)
        : m_type(ValueType::Ptr)
        , value(value)
    {
    }

    inline auto type() const -> ValueType { return m_type; };

    inline auto as_null() -> Null& { return std::get<Null>(value); }
    inline auto as_null() const -> const Null& { return std::get<Null>(value); }

    inline auto as_int() -> Int& { return std::get<Int>(value); }
    inline auto as_int() const -> const Int& { return std::get<Int>(value); }

    inline auto as_bool() -> Bool& { return std::get<Bool>(value); }
    inline auto as_bool() const -> const Bool& { return std::get<Bool>(value); }

    inline auto as_string() -> String& { return std::get<String>(value); }
    inline auto as_string() const -> const String&
    {
        return std::get<String>(value);
    }

    inline auto as_ptr() -> Ptr& { return std::get<Ptr>(value); }
    inline auto as_ptr() const -> const Ptr& { return std::get<Ptr>(value); }

private:
    ValueType m_type;
    std::variant<Null, Int, Bool, String, Ptr> value;
};

}