/** @file FacetRegistry.hpp
 *
 *  @brief Runtime facet implementation lookup
 *
 *  @author Roland Conybeare, Jan 2026
 **/

#pragma once

#include "facet_implementation.hpp"
#include "typeseq.hpp"
#include "obj.hpp"
#include <xo/indentlog/scope.hpp>
#include <xo/indentlog/print/tostr.hpp>
#include <unordered_map>
#include <utility>

namespace xo {
    namespace facet {

        /** @class FacetRegistry
         *
         *  @brief Runtime registry for facet implementations
         *
         *  Enables late-binding between facets. For example, given an
         *  obj<AGCObject> with unknown concrete type at compile time,
         * look up the APrintable implementation at runtime.
         *
         *  Example:
         *    // Registration (at initialization time)
         *    FacetRegistry::instance().register_impl<APrintable, DFloat>();
         *    FacetRegistry::instance().register_impl<APrintable, DList>();
         *
         *    // Lookup (at runtime)
         *    typeseq repr_id = obj->_typeseq();
         *    const APrintable* printable = runtime_impl_for<APrintable>(repr_id);
         *    printable->print(obj.data(), out);
         **/
        class FacetRegistry {
        public:
            using typeseq = xo::reflect::typeseq;
            using key_type = std::pair<typeseq, typeseq>;

            /** hash function for key_type **/
            struct KeyHash {
                std::size_t operator()(const key_type & k) const noexcept {
                    // combine the two seqno values
                    std::size_t h1 = std::hash<int32_t>{}(k.first.seqno());
                    std::size_t h2 = std::hash<int32_t>{}(k.second.seqno());
                    return h1 ^ (h2 << 1);
                }
            };

            /** singleton instance **/
            static FacetRegistry & instance() {
                static FacetRegistry s_instance;
                return s_instance;
            }

            /** Type-safe registration
             *
             *  Registers the compile-time FacetImplementation<AFacet, DRepr>
             *  for runtime lookup.
             *
             *  @tparam AFacet  abstract facet type
             *  @tparam DRepr   data representation type
             **/
            template <typename AFacet, typename DRepr>
            static void register_impl() {
                static FacetImplType<AFacet, DRepr> impl;

                instance()._register_impl(typeseq::id<AFacet>(),
                                          typeseq::id<DRepr>(),
                                          &impl);
            }

            /** Convenience function for runtime lookup
             *
             *  @tparam AFacet  abstract facet type
             *  @param repr_id  typeseq for data representation
             *  @return pointer to AFacet implementation, or nullptr
             **/
            template <typename AFacet>
            static inline const AFacet * impl_for(typeseq repr_id) {
                return FacetRegistry::instance().lookup<AFacet>(repr_id);
            }

            /** Number of registered (facet, repr) pairs **/
            std::size_t size() const { return registry_.size(); }

            /** Check if implementation is registered **/
            bool contains(typeseq facet_id,
                          typeseq repr_id) const
            {
                return (registry_.find(key_type(facet_id, repr_id))
                        != registry_.end());
            }

            /** Type-safe lookup
             *
             *  @tparam AFacet  abstract facet type
             *  @param repr_id  typeseq for data representation
             *  @return pointer to AFacet implementation, or nullptr
             **/
            template <typename AFacet>
            const AFacet * lookup(typeseq repr_id) const {
                return static_cast<const AFacet *>
                                      (this->_lookup(typeseq::id<AFacet>(), repr_id));
            }

            /** Runtime polymorphism:
             *  Switch @param from from interface @tp AFrom to interface @tp ATo.
             *
             *  Use:
             *    obj<AFoo> foo
             *      = ...;  // Foo instance with variant impl
             *    obj<ABar> bar
             *      = FacetRegistry::variant<ABar,AFoo>(foo);
             *
             *    // exception thrown if bar has null data
             *
             *    assert(bar);
             **/
            template <typename ATo, typename AFrom>
            obj<ATo> variant(obj<AFrom> from) {
                auto retval = try_variant<ATo>(from);

                if (!retval)
                    throw std::runtime_error(tostr("FacetRegistry::try_variant failed",
                                                   xtag("AFrom.tseq", typeseq::id<AFrom>()),
                                                   xtag("ATo.tseq", typeseq::id<ATo>())));

                return retval;
            }

            /** Runtime polymorphism:
             *  Switch @param from from interface @tp AFrom to interface @tp ATo.
             *
             *  Use:
             *    obj<AFoo> foo
             *      = ...;  // Foo instance with variant impl
             *    obj<ABar> bar
             *      = FacetRegistry::try_variant<ABar,AFoo>(foo);
             *    if (bar) {
             *       // success
             *    } else {
             *       // foo::DataType doesn't appear to support ABar
             *    }
             **/
            template <typename ATo, typename AFrom>
            obj<ATo> try_variant(obj<AFrom> from) {
                return try_variant<ATo>(from._typeseq(), from.data());
            }

            /** Runtime polymorphism:
             *  Create variant from representation @p data
             *  with actual type @p repr_id.
             *
             *  Use:
             *    obj<AFoo> foo = ...;  // Foo instance with variant impl
             *    obj<ABar> bar
             *      = FacetRegistry::variant<ABar>(foo._typeseq(), foo.opaque_data());
             **/
            template <typename AFacet>
            obj<AFacet> try_variant(typeseq repr_id, void * data) {
                const AFacet * iface = this->lookup<AFacet>(repr_id);

                if (iface)
                    return obj<AFacet>(iface, data);
                else
                    return obj<AFacet>();
            }

            void dump(std::ostream * p_out) {
                (*p_out) << std::endl;
                (*p_out) << "<FacetRegistry" << std::endl;
                for (auto & kv : registry_) {
                    (*p_out)
                    << "  [" << kv.first.first << "," << kv.first.second << "]"
                    << " -> " << kv.second << std::endl;
                }
                (*p_out) << ">" << std::endl;
            }

        private:
            /** Register a facet implementation (type-erased)
             *
             *  @param facet_id  typeseq for abstract facet (e.g., APrintable)
             *  @param repr_id   typeseq for data representation (e.g., DFloat)
             *  @param impl      pointer to stateless implementation instance
             **/
            void _register_impl(typeseq facet_id,
                                typeseq repr_id,
                                const void * impl)
            {
                registry_[key_type(facet_id, repr_id)] = impl;
            }

            /** Lookup facet implementation (type-erased)
             *
             *  @return pointer to implementation, or nullptr if not registered
             **/
            const void * _lookup(typeseq facet_id,
                                 typeseq repr_id) const
            {
                scope log(XO_DEBUG(false));
                log && log(xtag("facet_id", facet_id),
                           xtag("repr_id", repr_id));

                auto ix = registry_.find(key_type(facet_id, repr_id));

                const void *retval = nullptr;

                if (ix != registry_.end())
                    retval = ix->second;

                log && log(xtag("retval", retval));

                return retval;
            }

        private:
            FacetRegistry() = default;

            /** runtime lookup table (AFacet,DRepr) -> impl **/
            std::unordered_map<key_type, const void *, KeyHash> registry_;
        };

    } /*namespace facet*/
} /*namespace xo*/

/* end FacetRegistry.hpp */
