/** @file DList.cpp
 *
 *  @author Roland Conybeare, Dec 2025
 **/

#include "DList.hpp"
#include "list/IPrintable_DList.hpp"
#include "list/IGCObject_DList.hpp"
#include <xo/gc/GCObject.hpp>
#include <xo/printable2/Printable.hpp>
#include <xo/facet/FacetRegistry.hpp>
#include <xo/facet/facet_implementation.hpp>
#include <xo/indentlog/print/pretty.hpp>
#include <xo/indentlog/print/tag.hpp>

namespace xo {
    using xo::print::APrintable;
    using xo::mm::AGCObject;
    using xo::facet::FacetRegistry;
    using xo::facet::typeseq;

    namespace scm {
        static DList s_null(obj<AGCObject>(), nullptr);

        DList *
        DList::_nil()
        {
            return &s_null;
        }

        DList *
        DList::_cons(obj<AAllocator> mm,
                     obj<AGCObject> car,
                     DList * cdr)
        {
            void * mem = mm.alloc(typeseq::id<DList>(), sizeof(DList));

            return new (mem) DList(car, cdr);
        }

#ifdef OBSOLETE
        DList *
        DList::list(obj<AAllocator> mm,
                    obj<AGCObject> h1)
        {
            void * mem = mm.alloc(typeseq::id<DList>(), sizeof(DList));

            return new (mem) DList(h1, DList::_nil());
        }

        DList *
        DList::list(obj<AAllocator> mm,
                    obj<AGCObject> h1,
                    obj<AGCObject> h2)
        {
            void * mem = mm.alloc(typeseq::id<DList>(), sizeof(DList));

            return new (mem) DList(h1, DList::list(mm, h2));
        }
#endif

        bool
        DList::is_empty() const noexcept
        {
            return this == &s_null;
        }

        auto
        DList::size() const noexcept -> size_type
        {
            const DList * l = this;

            size_type z = 0;

            while (l && l != &s_null) {
                ++z;
                l = l->rest_;
            }

            return z;
        }

        auto
        DList::at(size_type index) const -> obj<AGCObject>
        {
            size_type ix = index;
            const DList * l = this;

            while (l->rest_ && (ix > 0)) {
                --ix;
                l = l->rest_;
            }

            if (ix > 0) {
                assert(l == nullptr);

                throw std::runtime_error
                    (tostr("DList::at: out-of-range index where [0..z) expected",
                           xtag("index", index),
                           xtag("z", this->size())));
            }

            assert(l);

            return l->head_;
        }

        bool
        DList::pretty(const ppindentinfo & ppii) const
        {
            /* adapted from ppstate.pretty_struct(), see also */

            using xo::print::ppstate;

            ppstate * pps = ppii.pps();

            if (ppii.upto()) {
                /* perhaps print on one line */
                pps->write("(");

                /* TODO: probably use iterators here, when available */
                const DList * l = this;

                size_t i = 0;
                while (!l->is_empty()) {
                    if (i > 0)
                        pps->write(" ");

                    obj<APrintable> elt
                        = FacetRegistry::instance().variant<APrintable, AGCObject>(l->head_);



                    assert(elt.data());

                    if (!pps->print_upto(elt))
                        return false;

                    l = l->rest_;
                    ++i;
                }

                pps->write(")");
                return true;
            } else {
                pps->write("(...)");
                return false;
            }
        }

        // ----- GCObject facet ------

        auto
        DList::shallow_size() const noexcept -> size_type
        {
            return sizeof(DList);
        }

        DList *
        DList::shallow_copy(obj<AAllocator> mm) const noexcept
        {
            DList * copy = (DList *)mm.alloc_copy((std::byte *)this);

            if (copy)
                *copy = *this;

            return copy;
        }

        auto
        DList::forward_children(obj<ACollector> gc) noexcept -> size_type
        {
            //scope log(XO_DEBUG(true));

            gc.forward_inplace(&head_);
            //gc.forward_inplace(head_.iface(), (void **)&(head_.data_));

            gc.forward_inplace(&rest_);
            //auto iface = xo::facet::impl_for<AGCObject,DList>();
            //gc.forward_inplace(&iface, (void **)(&rest_));

            return this->shallow_size();
        }
    } /*namespace scm*/
} /*namespace xo*/

/* end DList.cpp */
