/** @file DLambdaExpr.cpp
 *
 *  @author Roland Conybeare, Jan 2026
 **/

#include "LambdaExpr.hpp"
#include "LocalSymtab.hpp"
#include "UniqueString.hpp"
#include <xo/alloc2/Allocator.hpp>
#include <xo/printable2/Printable.hpp>
#include <xo/facet/FacetRegistry.hpp>
#include <xo/reflectutil/typeseq.hpp>

namespace xo {
    using xo::mm::AGCObject;
    using xo::print::APrintable;
    using xo::facet::FacetRegistry;
    using xo::reflect::TypeDescr;
    using xo::reflect::TypeDescrBase;
    using xo::reflect::FunctionTdxInfo;
    using xo::reflect::typeseq;
    using xo::print::quot;

    namespace scm {

#ifdef NOT_YET
        TypeDescr
        assemble_lambda_td()
        {
            std::vector<TypeDescr> arg_td_v;
            {
                arg_td_v.reserve(local_symtab->size());

                for (DLocalSymtab::size_type i = 0, n = local_symtab->size(); i < n; ++i) {
                    const DVariable * var = local_symtab->lookup_var(i);

                    if (!var)
                        break;

                    TypeDescr arg_td = var->valuetype();

                    if (!arg_td)
                        break;

                    arg_td_v.push_back(arg_td);
                }
            }
        }
#endif

        DLambdaExpr::DLambdaExpr(TypeRef typeref,
                                 const DUniqueString * name,
                                 DLocalSymtab * local_symtab,
                                 obj<AExpression> body) : typeref_{typeref},
                                                          name_{name},
                                                          local_symtab_{local_symtab},
                                                          body_expr_{body}
        {
        }

        obj<AExpression,DLambdaExpr>
        DLambdaExpr::make(obj<AAllocator> mm,
                          TypeRef typeref,
                          const DUniqueString * name,
                          DLocalSymtab * local_symtab,
                          obj<AExpression> body)
        {
            return obj<AExpression,DLambdaExpr>(_make(mm, typeref,
                                                      name, local_symtab, body));
        }

        DLambdaExpr *
        DLambdaExpr::_make(obj<AAllocator> mm,
                           TypeRef typeref,
                           const DUniqueString * name,
                           DLocalSymtab * local_symtab,
                           obj<AExpression> body)
        {
            // in general we're not going to know argument types yet.
            // perhaps want to delay this until after type resolution.

            void * mem = mm.alloc(typeseq::id<DLambdaExpr>(), sizeof(DLambdaExpr));

            return new (mem) DLambdaExpr(typeref,
                                         name,
                                         local_symtab,
                                         body);
        }

        TypeDescr
        DLambdaExpr::assemble_lambda_td(DLocalSymtab * symtab,
                                        TypeDescr return_td)
        {
            assert(return_td);

            std::vector<TypeDescr> arg_td_v;
            {
                DLocalSymtab::size_type z = symtab->size();

                arg_td_v.reserve(z);

                for (DLocalSymtab::size_type i = 0; i < z; ++i) {
                    auto param = symtab->lookup_var(Binding::local(i));

                    assert(param);
                    arg_td_v.push_back(param->valuetype());
                }
            }

            auto function_tdx = FunctionTdxInfo(return_td, arg_td_v, false /*!is_noexcept*/);

            TypeDescr lambda_td = TypeDescrBase::require_by_fn_info(function_tdx);

            return lambda_td;
        }

        exprtype
        DLambdaExpr::extype() const noexcept {
            return exprtype::lambda;
        }

        TypeRef
        DLambdaExpr::typeref() const noexcept {
            return typeref_;
        }

        TypeDescr
        DLambdaExpr::valuetype() const noexcept {
            return typeref_.td();
        }

        void
        DLambdaExpr::assign_valuetype(TypeDescr td) noexcept {
            typeref_.resolve(td);
        }

        std::size_t
        DLambdaExpr::shallow_size() const noexcept {
            return sizeof(DLambdaExpr);
        }

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

            if (copy) {
                *copy = *this;
            }

            return copy;
        }

        std::size_t
        DLambdaExpr::forward_children(obj<ACollector> gc) noexcept {
            {
                auto iface = xo::facet::impl_for<AGCObject,DUniqueString>();
                gc.forward_inplace(&iface, (void **)(&name_));
            }

            // type_name_str_

            {
                auto iface = xo::facet::impl_for<AGCObject,DLocalSymtab>();
                gc.forward_inplace(&iface, (void **)(&local_symtab_));
            }

            {
                auto iface = body_expr_.to_facet<AGCObject>().iface();
                gc.forward_inplace(iface, (void **)(&body_expr_));
            }

            // xxx free_var_set
            // xxx captured_var_set
            // xxx layer_var_map
            // xxx nested_lambda_map

            return shallow_size();
        }

        bool
        DLambdaExpr::pretty(const ppindentinfo & ppii) const
        {
            auto body
                = FacetRegistry::instance().try_variant<APrintable,
                                                        AExpression>(body_expr_);

            if (name_ && body) {
                auto local_symtab_pr
                    = obj<APrintable,DLocalSymtab>(local_symtab_);

                return ppii.pps()->pretty_struct(ppii,
                                                 "LambdaExpr",
                                                 refrtag("tref", typeref_),
                                                 refrtag("name", quot(std::string_view(*name_))),
                                                 refrtag("local_symtab", local_symtab_pr),
                                                 //refrtag("argv", local_env_->argv()),
                                                 refrtag("body", body));
            } else {
                return ppii.pps()->pretty_struct(ppii,
                                                 "LambdaExpr");
            }
        }

    } /*namespace scm*/
} /*namespace xo*/

/* end DLambda.cpp */
