/* Object.cpp
 *
 * author: Roalnd Conybeare, Jul 2025
 */

#include "Object.hpp"
#include "GC.hpp"
#include "Forwarding1.hpp"

using xo::obj::Forwarding1;

void *
operator new (std::size_t z, const xo::Cpof & cpof)
{
    using xo::gc::GC;

    //GC * gc = reinterpret_cast<GC *>(cpof.mm_);

    return cpof.mm_->alloc_gc_copy(z, cpof.src_);
}

namespace xo {
    using xo::reflect::TaggedPtr;

    gc::IAlloc *
    Object::mm = nullptr;

    TaggedPtr
    Object::self_tp() const
    {
        assert(false);
        return TaggedPtr::universal_null();
    }

     void
     Object::display(std::ostream & os) const
     {
         os << "<Object>";
     }

    IObject *
    Object::_forward(IObject * src,
                     gc::IAlloc * gc)
    {
        scope log(XO_DEBUG(gc->debug_flag()), xtag("src", src));

        if (!src)
            return src;

        if (src->_is_forwarded()) {
            log && log("already forwarded", xtag("dest", src->_offset_destination(src)));
            return src->_offset_destination(src);
        }

        if (gc->check_move(src)) {
            log && log("needs forwarding");
            Object::_shallow_move(src, gc);

            /* *src is now a forwarding pointer to a copy in to-space */
            return src->_offset_destination(src);
        } else {
            log && log("already tenured + incr collection");
            /* don't move tenured objects during incremental collection */
            return src;
        }
    }

    IObject *
    Object::_deep_move(IObject * from_src, gc::GC * gc, gc::ObjectStatistics * /*stats*/)
    {
        scope log(XO_DEBUG(gc->config().debug_flag_));

        using gc::generation;

        if (!from_src)
            return nullptr;

        IObject * retval = from_src->_destination();

        if (retval)
            return retval;

        if (!gc->check_move(from_src)) {
            /** incremental collection does not move already-tenured objects **/
            return from_src;
        }

        /**
         *  To-space:
         *
         *     to_lo = start of to-space
         *     w,W   = white objects. An object x is white if x
         *             + all immediate children of x are in to-space
         *             (also implies this GC cycle put it there)
         *     g,G   = grey  objects. An object x is gray if it's in to-space,
         *             but possibly has >0 black children
         *     _     = free to-space memory
         *     N     = nursery space
         *     T     = tenured space
         *
         *     wwwwwwwwwwwwwwwwwwwggggggggggggggggggggg_________________...
         *     ^                  ^                    ^
         *     to_lo              grey_lo(N)           free_ptr(N)
         *
         *  After moving children of one object, advancing {nursery_grey_lo, nursery_free_ptr}
         *
         *     wwwwwwwwwwwwwwwwwwwWWWWgggggggggggggggggGGGGGGGGGGG______...
         *     ^                      ^                           ^
         *     to_lo                  grey_lo(N)                  free_ptr(N)
         *
         *  Invariant:
         *
         *     objects in [to_lo, gray_lo) are white.
         *     all gray objects are in [gray_lo, free_ptr)
         *     memory starting at free_ptr is free.
         *
         *  deep_move terminates when gray_lo catches up to free_ptr
         *
         *  Above is simplified. Complication is that GC (including incremental) may
         *  promote objects from nursery (N) to tenured (T)
         *
         *  So more accurate before/after picture
         *
         *  N  wwwwwwwwwwwwwwwwwwwggggggggggggggggggggg_________________...
         *     ^                  ^                    ^
         *     to_lo(N)           grey_lo(N)           free_ptr(N)
         *
         *  T  wwwwwwwwwwwwwwgggggggggggg_______________________________...
         *     ^             ^           ^
         *     to_lo(T)      grey_lo(T)  free_ptr(N)
         *
         *  After moving children of one object, advancing {nursery_grey_lo, nursery_free_ptr}
         *
         *  N  wwwwwwwwwwwwwwwwwwwWWWWgggggggggggggggggGGGGGGGGGGG_____...
         *     ^                      ^                           ^
         *     to_lo(N)               grey_lo(N)                  free_ptr(N)
         *
         *  T  wwwwwwwwwwwwwwggggggggggggGGGGG_________________________...
         *     ^             ^                ^
         *     to_lo(T)      grey_lo(T)       free_ptr(T)
         *
         *  deep_move terminates when both:
         *  - gray_lo(N) catches up with free_ptr(N)
         *  - gray_lo(T) catches up with free_ptr(T)
         *
         **/

        std::array<std::byte *, gen2int(generation::N)> gray_lo_v
            = { gc->free_ptr(generation::nursery), gc->free_ptr(generation::tenured) };

        IObject * to_src = Object::_shallow_move(from_src, gc);

        std::size_t fixup_work = 0;
        do {
            fixup_work = 0;

            auto fixup_generation = [gc, &log, &gray_lo_v](generation gen) {
                std::size_t work = 0;
                while(gray_lo_v[gen2int(gen)] < gc->free_ptr(gen)) {
                    Object * x = reinterpret_cast<Object *>(gray_lo_v[gen2int(gen)]);

                    // update per-class stats here

                    log && log("fwd children", xtag("x", x));

                    std::size_t xz = x->_forward_children(gc);

                    // must pad xz to multiple of word size,
                    // to match behavior of LinearAlloc::alloc()
                    //
                    xz += gc::IAlloc::alloc_padding(xz);

                    gray_lo_v[gen2int(gen)] += xz;
                    ++work;
                }

                return work;
            };

            fixup_work += fixup_generation(generation::nursery);
            fixup_work += fixup_generation(generation::tenured);
        } while (fixup_work > 0);

        return to_src;
    } /*deep_move*/

    IObject *
    Object::_shallow_move(IObject * src, gc::IAlloc * gc)
    {
        /* filter for source objects that are owned by GC.
         * Care required though -- during GC from/to spaces have been swapped already
         */
        if (gc->check_owned(src))
        {
            IObject * dest = src->_shallow_copy(gc);

            if (dest != src)
                src->_forward_to(dest);

            return dest;
        } else {
            return src;
        }
    }

    void
    Object::_forward_to(IObject * dest)
    {
        char * mem = reinterpret_cast<char *>(this);

        Forwarding1 * fwd = new (mem) Forwarding1(dest);

        (void)fwd;
    }

    std::ostream &
    operator<< (std::ostream & os, gp<Object> x)
    {
        if (x.ptr()) {
            x->display(os);
        } else {
            os << "<nullptr>";
        }

        return os;
    }

} /*namespace xo*/

/* end Object.cpp*/
