/* Symbol definitions.
   Copyright (C) 2025 Free Software Foundation, Inc.

GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "elna/boot/symbol.h"

namespace elna
{
namespace boot
{
    type::type()
    {
    }

    type::type(std::shared_ptr<alias_type> alias)
        : tag(type_tag::alias), alias(alias)
    {
    }

    type::type(std::shared_ptr<primitive_type> primitive)
        : tag(type_tag::primitive), primitive(primitive)
    {
    }

    type::type(std::shared_ptr<record_type> record)
        : tag(type_tag::record), record(record)
    {
    }

    type::type(std::shared_ptr<union_type> _union)
        : tag(type_tag::_union), _union(_union)
    {
    }

    type::type(std::shared_ptr<pointer_type> pointer)
        : tag(type_tag::pointer), pointer(pointer)
    {
    }

    type::type(std::shared_ptr<array_type> array)
        : tag(type_tag::array), array(array)
    {
    }

    void type::copy(const type& other)
    {
        switch (other.tag)
        {
        case type_tag::empty:
            break;
        case type_tag::alias:
            new (&alias) std::weak_ptr<alias_type>(other.alias);
            break;
        case type_tag::primitive:
            new (&primitive) std::shared_ptr<primitive_type>(other.primitive);
            break;
        case type_tag::record:
            new (&record) std::shared_ptr<record_type>(other.record);
            break;
        case type_tag::_union:
            new (&_union) std::shared_ptr<union_type>(other._union);
            break;
        case type_tag::pointer:
            new (&pointer) std::shared_ptr<pointer_type>(other.pointer);
            break;
        case type_tag::array:
            new (&array) std::shared_ptr<array_type>(other.array);
            break;
        }
    }

    type::type(const type& other)
        : tag(other.tag)
    {
        copy(other);
    }

    void type::move(type&& other)
    {
        switch (other.tag)
        {
        case type_tag::empty:
            break;
        case type_tag::alias:
            new (&alias) std::weak_ptr<alias_type>(std::move(other.alias));
            break;
        case type_tag::primitive:
            new (&primitive) std::shared_ptr<primitive_type>(std::move(other.primitive));
            break;
        case type_tag::record:
            new (&record) std::shared_ptr<record_type>(std::move(other.record));
            break;
        case type_tag::_union:
            new (&_union) std::shared_ptr<union_type>(std::move(other._union));
            break;
        case type_tag::pointer:
            new (&pointer) std::shared_ptr<pointer_type>(std::move(other.pointer));
            break;
        case type_tag::array:
            new (&array) std::shared_ptr<array_type>(std::move(other.array));
            break;
        }
    }

    type& type::operator=(const type& other)
    {
        this->~type();
        this->tag = other.tag;
        copy(other);
        return *this;
    }

    type::type(type&& other)
        : tag(other.tag)
    {
        move(std::move(other));
    }

    type& type::operator=(type&& other)
    {
        this->~type();
        this->tag = other.tag;
        move(std::move(other));
        return *this;
    }

    type::~type()
    {
        switch (tag)
        {
        case type_tag::empty:
            break;
        case type_tag::alias:
            this->alias.~weak_ptr<alias_type>();
            break;
        case type_tag::primitive:
            this->primitive.~shared_ptr<primitive_type>();
            break;
        case type_tag::record:
            this->record.~shared_ptr<record_type>();
            break;
        case type_tag::_union:
            this->_union.~shared_ptr<union_type>();
            break;
        case type_tag::pointer:
            this->pointer.~shared_ptr<pointer_type>();
            break;
        case type_tag::array:
            this->array.~shared_ptr<array_type>();
            break;
        }
    }

    template<>
    std::shared_ptr<alias_type> type::get<alias_type>() const
    {
        if (tag == type_tag::alias)
        {
            return this->alias.lock();
        }
        else
        {
            return nullptr;
        }
    }

    template<>
    std::shared_ptr<primitive_type> type::get<primitive_type>() const
    {
        if (tag == type_tag::primitive)
        {
            return this->primitive;
        }
        else
        {
            return nullptr;
        }
    }

    template<>
    std::shared_ptr<record_type> type::get<record_type>() const
    {
        if (tag == type_tag::record)
        {
            return this->record;
        }
        else
        {
            return nullptr;
        }
    }

    template<>
    std::shared_ptr<union_type> type::get<union_type>() const
    {
        if (tag == type_tag::_union)
        {
            return this->_union;
        }
        else
        {
            return nullptr;
        }
    }

    template<>
    std::shared_ptr<pointer_type> type::get<pointer_type>() const
    {
        if (tag == type_tag::pointer)
        {
            return this->pointer;
        }
        else
        {
            return nullptr;
        }
    }

    template<>
    std::shared_ptr<array_type> type::get<array_type>() const
    {
        if (tag == type_tag::array)
        {
            return this->array;
        }
        else
        {
            return nullptr;
        }
    }

    bool type::empty() const
    {
        return tag == type_tag::empty;
    }

    alias_type::alias_type(const std::string& name)
        : name(name), reference()
    {
    }

    pointer_type::pointer_type(type base)
        : base(base)
    {
    }

    array_type::array_type(type base, std::uint64_t size)
        : base(base), size(size)
    {
    }

    primitive_type::primitive_type(const std::string& identifier)
        : identifier(identifier)
    {
    }

    info::~info()
    {
    }

    std::shared_ptr<type_info> info::is_type()
    {
        return nullptr;
    }

    type_info::type_info(const type symbol)
        : symbol(symbol)
    {
    }

    std::shared_ptr<type_info> type_info::is_type()
    {
        return std::static_pointer_cast<type_info>(shared_from_this());
    }

    std::shared_ptr<symbol_table> builtin_symbol_table()
    {
        auto result = std::make_shared<symbol_table>();

        result->enter("Int", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Int"))));
        result->enter("Word", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Word"))));
        result->enter("Char", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Char"))));
        result->enter("Bool", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Bool"))));
        result->enter("Byte", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Byte"))));
        result->enter("Float", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Float"))));
        result->enter("String", std::make_shared<type_info>(type(std::make_shared<primitive_type>("String"))));

        return result;
    }
}
}