/** @file DGlobalEnv.hpp
 *
 *  @author Roland Conybeare, Feb 2026
 **/

#pragma once

#include <xo/expression2/DGlobalSymtab.hpp>
#include <xo/object2/DArray.hpp>
#include <xo/alloc2/GCObjectVisitor.hpp>

namespace xo {
    namespace scm {

        /** @brief runtime bindings for global variabels
         *
         *  Implementation here uses a DArenaHashMap to hold <key,value> pairs.
         *  The hash map has its own memory outside GC space.
         *  Keys are DUniqueStrings, also outside GC space.
         *  Values are regular gc-aware objects, generally will be in GC space.
         *
         *  We need collector to traverse all the values in a global env
         *  on each cycle.  Arrange that by having DGlobalEnv itself
         *  in GC space.
         *
         **/
        class DGlobalEnv {
        public:
            using TypeDescr = xo::reflect::TypeDescr;
            using AGCObject = xo::mm::AGCObject;
            using AGCObjectVisitor = xo::mm::AGCObjectVisitor;
            using VisitReason = xo::mm::VisitReason;
            using AAllocator = xo::mm::AAllocator;
            using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
            using ppindentinfo = xo::print::ppindentinfo;
            using size_type = std::uint32_t;

        public:
            /** @defgroup scm-globalenv-ctors constructors **/
            ///@{

            DGlobalEnv(DGlobalSymtab * symtab, DArray * values);

            static DGlobalEnv * _make(obj<AAllocator> mm,
                                      DGlobalSymtab * symtab);


            ///@}
            /** @defgroup scm-globalenv-methods methods **/
            ///@{

            /** symbol-table size.  Is the number of distinct global variables **/
            size_type n_vars() const noexcept { return symtab_->n_vars(); }

            /** lookup current value associated with binding @p ix **/
            obj<AGCObject> lookup_value(Binding ix) const noexcept;

            /** assign value associated with binding @p to @p x.
             *  If need to expand size of this env, use memory from @p mm
             **/
            void assign_value(obj<AAllocator> mm, Binding ix, obj<AGCObject> x);

            /** create/establish global for symbol @p sym with resolved type @p td
             *  and associate with @p value.
             **/
            DVariable * _upsert_value(obj<AAllocator> mm,
                                      const DUniqueString * sym,
                                      TypeDescr td,
                                      obj<AGCObject> value);

            ///@}
            /** @defgroup scm-globalenv-gcobject-facet **/
            ///@{

            DGlobalEnv * gco_shallow_move(obj<AGCObjectVisitor> gc) noexcept;
            void visit_gco_children(VisitReason reason, obj<AGCObjectVisitor> gc) noexcept;

            ///@}
            /** @defgroup scm-globalenv-printable-facet **/
            ///@{

            bool pretty(const ppindentinfo & ppii) const;

            ///@}

        private:

            /** symbol table assigns a unique index for each symbol **/
            DGlobalSymtab * symtab_;

            /** value for a symbol S will be in values_[symtab->lookup_binding(S)] **/
            DArray * values_ = nullptr;
        };
    }
}
