#pragma once

// ------------------------------------------------------------------------------------------------
#include "Core.hpp"

// ------------------------------------------------------------------------------------------------
#include <cstring>
#include <vector>
#include <functional>

// ------------------------------------------------------------------------------------------------
#define SQMOD_VALID_TAG_STR(t) if (!(t)) { STHROWF("The specified tag is invalid"); }

// ------------------------------------------------------------------------------------------------
namespace SqMod { // NOLINT(modernize-concat-nested-namespaces)
namespace Algo {

/* ------------------------------------------------------------------------------------------------
 * Returns a pointer to the first occurrence of 'needle' in 'haystack'.
*/
inline const SQChar * sqmod_stristr(const SQChar * haystack, const SQChar * needle)
{
    for (const auto chr = static_cast< const SQChar >(std::tolower(*needle)); *haystack != '\0'; ++haystack)
    {
        if (static_cast< SQChar >(std::tolower(*haystack)) != chr)
        {
            continue;
        }

        for (const SQChar * itr = haystack, * str = needle;; ++itr, ++str)
        {
            if (*str == '\0')
            {
                return haystack;
            }
            else if (*itr == '\0')
            {
                return nullptr;
            }
            else if (std::tolower(*itr) != std::tolower(*str))
            {
                break;
            }
        }
    }

    return nullptr;
}

/* ------------------------------------------------------------------------------------------------
 * Find the specified string in another string.
*/
inline bool FindStr(const SQChar * str1, const SQChar * str2, bool cs)
{
    if (!str1 || !str2 || (*str1 == '\0' && *str2 == '\0'))
    {
        return false;
    }
    else if (cs)
    {
        return (strstr(str1, str2) != nullptr);
    }

    return (sqmod_stristr(str1, str2) != nullptr);
}

/* ------------------------------------------------------------------------------------------------
 * Compare the specified strings.
*/
inline int32_t CompareStr(const SQChar * lhs, const SQChar * rhs, bool cs)
{
    if (!lhs)
    {
        if (!rhs)
        {
            return 0;
        }

        return -1;
    }

    if (!rhs)
    {
        return 1;
    }

    return cs ? strcmp(lhs, rhs) : sqmod_stricmp(lhs, rhs);
}

/* ------------------------------------------------------------------------------------------------
 * Compare a portion from the specified strings.
*/
inline int32_t CompareStr(const SQChar * lhs, const SQChar * rhs, uint32_t len, bool cs)
{
    if (!lhs)
    {
        if (!rhs)
        {
            return 0;
        }

        return -1;
    }

    if (!rhs)
    {
        return 1;
    }

    return cs ? strncmp(lhs, rhs, len) : sqmod_strnicmp(lhs, rhs, len);
}

/* ------------------------------------------------------------------------------------------------
 * Compare a portion from the specified strings starting from a certain position.
*/
inline int32_t CompareStr(const SQChar * lhs, const SQChar * rhs, uint32_t pos, uint32_t len, bool cs)
{
    if (!lhs)
    {
        if (!rhs)
        {
            return 0;
        }

        return -1;
    }

    if (!rhs)
    {
        return 1;
    }

    while (pos)
    {
        if (*lhs == '\0')
        {
            return -1;
        }

        ++lhs, --pos;
    }

    return cs ? strncmp(lhs, rhs, len) : sqmod_strnicmp(lhs, rhs, len);
}

/* ------------------------------------------------------------------------------------------------
 * Compare the specified strings.
*/
inline bool ApplyStrFilter(const SQChar * name, const SQChar * filter, bool cs)
{
    return cs ? NameFilterCheck(filter, name) : NameFilterCheckInsensitive(filter, name);
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range that the inspector deems worthy.
*/
template < typename Iterator, typename Inspector, typename Collector >
void Collect(Iterator first, Iterator last, Inspector inspect, Collector collect)
{
    for (; first != last; ++first)
    {
        if (inspect(*first))
        {
            collect(*first);
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range that the inspector deems worthy.
*/
template < typename Iterator, typename Inspector, typename Collector >
void CollectWhile(Iterator first, Iterator last, Inspector inspect, Collector collect)
{
    for (; first != last; ++first)
    {
        if (inspect(*first))
        {
            if (!collect(*first))
            {
                break;
            }
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string matches or not the specified one.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachEquals(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && (CompareStr(retrieve(*first).c_str(), str, cs) == 0) == neg)
        {
            collect(*first);
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string matches or not the specified one.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachEqualsWhile(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && (CompareStr(retrieve(*first).c_str(), str, cs) == 0) == neg)
        {
            if (!collect(*first))
            {
                break;
            }
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string begins or not with the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachBegins(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, std::size_t len, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (!inspect(*first))
        {
            continue;
        }
        // Retrieve the string
        const auto & s = retrieve(*first);
        // Compare the string
        if (s.size() >= len)
        {
            if ((CompareStr(s.c_str(), str, static_cast< uint32_t >(len), cs) == 0) == neg)
            {
                collect(*first);
            }
        }
        else if (!neg)
        {
            collect(*first);
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string begins or not with the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachBeginsWhile(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, std::size_t len, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (!inspect(*first))
        {
            continue;
        }
        // Retrieve the string
        const auto & s = retrieve(*first);
        // Compare the string
        if (s.size() >= len)
        {
            if ((CompareStr(s.c_str(), str, static_cast< uint32_t >(len), cs) == 0) == neg)
            {
                if (!collect(*first))
                {
                    break;
                }
            }
        }
        else if (!neg)
        {
            if (!collect(*first))
            {
                break;
            }
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string ends or not with the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachEnds(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, std::size_t len, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (!inspect(*first))
        {
            continue;
        }
        // Retrieve the string
        const auto & s = retrieve(*first);
        // Compare the tag
        if (s.size() >= len)
        {
            if ((CompareStr(s.c_str(), str, static_cast< uint32_t >(s.size() - len), static_cast< uint32_t >(len), cs) == 0) == neg)
            {
                collect(*first);
            }
        }
        else if (!neg)
        {
            collect(*first);
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string ends or not with the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachEndsWhile(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, std::size_t len, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (!inspect(*first))
        {
            continue;
        }
        // Retrieve the string
        const auto & s = retrieve(*first);
        // Compare the tag
        if (s.size() >= len)
        {
            if ((CompareStr(s.c_str(), str, static_cast< uint32_t >(s.size() - len), static_cast< uint32_t >(len), cs) == 0) == neg)
            {
                if (!collect(*first))
                {
                    break;
                }
            }
        }
        else if (!neg)
        {
            if (!collect(*first))
            {
                break;
            }
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string contains or not the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachContains(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && FindStr(retrieve(*first).c_str(), str, cs) == neg)
        {
            collect(*first);
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string contains or not the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachContainsWhile(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && FindStr(retrieve(*first).c_str(), str, cs) == neg)
        {
            if (!collect(*first))
            {
                break;
            }
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string matches or not the specified
 * filter.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachMatches(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && ApplyStrFilter(retrieve(*first).c_str(), str, cs) == neg)
        {
            collect(*first);
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Collect all elements within the specified range where the string matches or not the specified
 * filter.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Collector >
void EachMatchesWhile(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Collector collect,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && ApplyStrFilter(retrieve(*first).c_str(), str, cs) == neg)
        {
            if (!collect(*first))
            {
                break;
            }
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Find the first element within the specified range where the string matches or not the specified one.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Receiver >
void FirstEquals(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Receiver receive,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && (CompareStr(retrieve(*first).c_str(), str, cs) == 0) == neg)
        {
            receive(*first);
            break;
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Find the first element within the specified range where the string begins or not with the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Receiver >
void FirstBegins(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Receiver receive,
                        const SQChar * str, std::size_t len, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (!inspect(*first))
        {
            continue;
        }
        // Retrieve the string
        const auto & s = retrieve(*first);
        // Compare the string
        if (s.size() >= len)
        {
            if ((CompareStr(s.c_str(), str, static_cast< uint32_t >(len), cs) == 0) == neg)
            {
                receive(*first);
                break;
            }
        }
        else if (!neg)
        {
            receive(*first);
            break;
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Find the first element within the specified range where the string ends or not with the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Receiver >
void FirstEnds(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Receiver receive,
                        const SQChar * str, std::size_t len, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (!inspect(*first))
        {
            continue;
        }
        // Retrieve the string
        const auto & s = retrieve(*first);
        // Compare the string
        if (s.size() >= len)
        {
            if ((CompareStr(s.c_str(), str, static_cast< uint32_t >(s.size() - len), static_cast< uint32_t >(len), cs) == 0) == neg)
            {
                receive(*first);
                break;
            }
        }
        else if (!neg)
        {
            receive(*first);
            break;
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Find the first element within the specified range where the string contains or not the specified
 * string.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Receiver >
void FirstContains(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Receiver receive,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && FindStr(retrieve(*first).c_str(), str, cs) == neg)
        {
            receive(*first);
            break;
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Find the first element within the specified range where the string matches or not the specified
 * filter.
*/
template < typename Iterator, typename Inspector, typename Retriever, typename Receiver >
void FirstMatches(Iterator first, Iterator last,
                        Inspector inspect, Retriever retrieve, Receiver receive,
                        const SQChar * str, bool neg, bool cs)
{
    for (; first != last; ++first)
    {
        if (inspect(*first) && ApplyStrFilter(retrieve(*first).c_str(), str, cs) == neg)
        {
            receive(*first);
            break;
        }
    }
}

/* ------------------------------------------------------------------------------------------------
 * Used to work with entity instances in a template fashion.
*/
template < typename T >  struct InstSpec;

/* ------------------------------------------------------------------------------------------------
 * Specialization for the Blip entity type.
*/
template <> struct InstSpec< CBlip >
{
    // --------------------------------------------------------------------------------------------
    typedef Core::Blips Instances; // Container to store instances of this entity type.
    typedef Core::Blips::value_type Instance; // Type that manages this type of entity instance.

    // --------------------------------------------------------------------------------------------
    static constexpr int Max = SQMOD_BLIP_POOL; // Maximum identifier for this entity type.

    // --------------------------------------------------------------------------------------------
    static constexpr const SQChar * LcName = "blip"; // Lowercase name of this entity type.
    static constexpr const SQChar * UcName = "Blip"; // Uppercase name of this entity type.

    /* --------------------------------------------------------------------------------------------
     * Iterator to the beginning of the instance container.
    */
    static inline Instances::const_iterator CBegin()
    {
        return Core::Get().GetBlips().cbegin();
    }

    /* --------------------------------------------------------------------------------------------
     * Iterator to the ending of the instance container.
    */
    static inline Instances::const_iterator CEnd()
    {
        return Core::Get().GetBlips().cend();
    }

    /* --------------------------------------------------------------------------------------------
     * Reference to the NULL instance.
    */
    static inline LightObj & Null()
    {
        return Core::Get().GetNullBlip();
    }
};

/* ------------------------------------------------------------------------------------------------
 * Specialization for the Checkpoint entity type.
*/
template <> struct InstSpec< CCheckpoint >
{
    // --------------------------------------------------------------------------------------------
    typedef Core::Checkpoints Instances; // Container to store instances of this entity type.
    typedef Core::Checkpoints::value_type Instance; // Type that manages this type of entity instance.

    // --------------------------------------------------------------------------------------------
    static constexpr int Max = SQMOD_CHECKPOINT_POOL; // Maximum identifier for this entity type.

    // --------------------------------------------------------------------------------------------
    static constexpr const SQChar * LcName = "checkpoint"; // Lowercase name of this entity type.
    static constexpr const SQChar * UcName = "Checkpoint"; // Uppercase name of this entity type.

    /* --------------------------------------------------------------------------------------------
     * Iterator to the beginning of the instance container.
    */
    static inline Instances::const_iterator CBegin()
    {
        return Core::Get().GetCheckpoints().cbegin();
    }

    /* --------------------------------------------------------------------------------------------
     * Iterator to the ending of the instance container.
    */
    static inline Instances::const_iterator CEnd()
    {
        return Core::Get().GetCheckpoints().cend();
    }

    /* --------------------------------------------------------------------------------------------
     * Reference to the NULL instance.
    */
    static inline LightObj & Null()
    {
        return Core::Get().GetNullCheckpoint();
    }
};

/* ------------------------------------------------------------------------------------------------
 * Specialization for the KeyBind entity type.
*/
template <> struct InstSpec< CKeyBind >
{
    // --------------------------------------------------------------------------------------------
    typedef Core::KeyBinds Instances; // Container to store instances of this entity type.
    typedef Core::KeyBinds::value_type Instance; // Type that manages this type of entity instance.

    // --------------------------------------------------------------------------------------------
    static constexpr int Max = SQMOD_KEYBIND_POOL; // Maximum identifier for this entity type.

    // --------------------------------------------------------------------------------------------
    static constexpr const SQChar * LcName = "keybind"; // Lowercase name of this entity type.
    static constexpr const SQChar * UcName = "KeyBind"; // Uppercase name of this entity type.

    /* --------------------------------------------------------------------------------------------
     * Iterator to the beginning of the instance container.
    */
    static inline Instances::const_iterator CBegin()
    {
        return Core::Get().GetKeyBinds().cbegin();
    }

    /* --------------------------------------------------------------------------------------------
     * Iterator to the ending of the instance container.
    */
    static inline Instances::const_iterator CEnd()
    {
        return Core::Get().GetKeyBinds().cend();
    }

    /* --------------------------------------------------------------------------------------------
     * Reference to the NULL instance.
    */
    static inline LightObj & Null()
    {
        return Core::Get().GetNullKeyBind();
    }
};

/* ------------------------------------------------------------------------------------------------
 * Specialization for the Object entity type.
*/
template <> struct InstSpec< CObject >
{
    // --------------------------------------------------------------------------------------------
    typedef Core::Objects Instances; // Container to store instances of this entity type.
    typedef Core::Objects::value_type Instance; // Type that manages this type of entity instance.

    // --------------------------------------------------------------------------------------------
    static constexpr int Max = SQMOD_OBJECT_POOL; // Maximum identifier for this entity type.

    // --------------------------------------------------------------------------------------------
    static constexpr const SQChar * LcName = "object"; // Lowercase name of this entity type.
    static constexpr const SQChar * UcName = "Object"; // Uppercase name of this entity type.

    /* --------------------------------------------------------------------------------------------
     * Iterator to the beginning of the instance container.
    */
    static inline Instances::const_iterator CBegin()
    {
        return Core::Get().GetObjs().cbegin();
    }

    /* --------------------------------------------------------------------------------------------
     * Iterator to the ending of the instance container.
    */
    static inline Instances::const_iterator CEnd()
    {
        return Core::Get().GetObjs().cend();
    }

    /* --------------------------------------------------------------------------------------------
     * Reference to the NULL instance.
    */
    static inline LightObj & Null()
    {
        return Core::Get().GetNullObject();
    }
};

/* ------------------------------------------------------------------------------------------------
 * Specialization for the Pickup entity type.
*/
template <> struct InstSpec< CPickup >
{
    // --------------------------------------------------------------------------------------------
    typedef Core::Pickups Instances; // Container to store instances of this entity type.
    typedef Core::Pickups::value_type Instance; // Type that manages this type of entity instance.

    // --------------------------------------------------------------------------------------------
    static constexpr int Max = SQMOD_PICKUP_POOL; // Maximum identifier for this entity type.

    // --------------------------------------------------------------------------------------------
    static constexpr const SQChar * LcName = "pickup"; // Lowercase name of this entity type.
    static constexpr const SQChar * UcName = "Pickup"; // Uppercase name of this entity type.

    /* --------------------------------------------------------------------------------------------
     * Iterator to the beginning of the instance container.
    */
    static inline Instances::const_iterator CBegin()
    {
        return Core::Get().GetPickups().cbegin();
    }

    /* --------------------------------------------------------------------------------------------
     * Iterator to the ending of the instance container.
    */
    static inline Instances::const_iterator CEnd()
    {
        return Core::Get().GetPickups().cend();
    }

    /* --------------------------------------------------------------------------------------------
     * Reference to the NULL instance.
    */
    static inline LightObj & Null()
    {
        return Core::Get().GetNullPickup();
    }
};

/* ------------------------------------------------------------------------------------------------
 * Specialization for the Player entity type.
*/
template <> struct InstSpec< CPlayer >
{
    // --------------------------------------------------------------------------------------------
    typedef Core::Players Instances; // Container to store instances of this entity type.
    typedef Core::Players::value_type Instance; // Type that manages this type of entity instance.

    // --------------------------------------------------------------------------------------------
    static constexpr int Max = SQMOD_PLAYER_POOL; // Maximum identifier for this entity type.

    // --------------------------------------------------------------------------------------------
    static constexpr const SQChar * LcName = "player"; // Lowercase name of this entity type.
    static constexpr const SQChar * UcName = "Player"; // Uppercase name of this entity type.

    /* --------------------------------------------------------------------------------------------
     * Iterator to the beginning of the instance container.
    */
    static inline Instances::const_iterator CBegin()
    {
        return Core::Get().GetPlayers().cbegin();
    }

    /* --------------------------------------------------------------------------------------------
     * Iterator to the ending of the instance container.
    */
    static inline Instances::const_iterator CEnd()
    {
        return Core::Get().GetPlayers().cend();
    }

    /* --------------------------------------------------------------------------------------------
     * Reference to the NULL instance.
    */
    static inline LightObj & Null()
    {
        return Core::Get().GetNullPlayer();
    }
};

/* ------------------------------------------------------------------------------------------------
 * Specialization for the Vehicle entity type.
*/
template <> struct InstSpec< CVehicle >
{
    // --------------------------------------------------------------------------------------------
    typedef Core::Vehicles Instances; // Container to store instances of this entity type.
    typedef Core::Vehicles::value_type Instance; // Type that manages this type of entity instance.

    // --------------------------------------------------------------------------------------------
    static constexpr int Max = SQMOD_VEHICLE_POOL; // Maximum identifier for this entity type.

    // --------------------------------------------------------------------------------------------
    static constexpr const SQChar * LcName = "vehicle"; // Lowercase name of this entity type.
    static constexpr const SQChar * UcName = "Vehicle"; // Uppercase name of this entity type.

    /* --------------------------------------------------------------------------------------------
     * Iterator to the beginning of the instance container.
    */
    static inline Instances::const_iterator CBegin()
    {
        return Core::Get().GetVehicles().cbegin();
    }

    /* --------------------------------------------------------------------------------------------
     * Iterator to the ending of the instance container.
    */
    static inline Instances::const_iterator CEnd()
    {
        return Core::Get().GetVehicles().cend();
    }

    /* --------------------------------------------------------------------------------------------
     * Reference to the NULL instance.
    */
    static inline LightObj & Null()
    {
        return Core::Get().GetNullVehicle();
    }
};

/* ------------------------------------------------------------------------------------------------
 * Functor to check if an instance is valid.
*/
template < typename T > struct ValidInstFunc
{
    /* --------------------------------------------------------------------------------------------
     * Function call operator.
    */
    bool operator () (const typename InstSpec< T >::Instance & inst) const
    {
        return VALID_ENTITY(inst.mID);
    }
};

/* ------------------------------------------------------------------------------------------------
 * Functor to retrieve the entity tag.
*/
template < typename T > struct InstTagFunc
{
    /* --------------------------------------------------------------------------------------------
     * Function call operator.
    */
    const String & operator () (const typename InstSpec< T >::Instance & inst) const
    {
        return inst.mInst->GetTag();
    }
};

/* ------------------------------------------------------------------------------------------------
 * Functor to append the entity instance object at the back of an array.
*/
template < typename T > struct AppendElemFunc
{
    // --------------------------------------------------------------------------------------------
    const SQInteger mIdx; // Array index after pushing one object on the stack.
    HSQUIRRELVM     mVM; // The VM to use when pushing the object.

    /* --------------------------------------------------------------------------------------------
     * Base constructor.
    */
    explicit AppendElemFunc(SQInteger idx = -2, HSQUIRRELVM vm = SqVM())
        : mIdx(idx), mVM(vm)
    {
        /* ... */
    }

    /* --------------------------------------------------------------------------------------------
     * Function call operator.
    */
    void operator () (const typename InstSpec< T >::Instance & inst) const
    {
        // Push the script object on the stack
        sq_pushobject(mVM, inst.mObj.GetObj());
        // Append the object at the back of the array
        if (SQ_FAILED(sq_arrayappend(mVM, mIdx)))
        {
            STHROWF("Unable to append {} instance to the list", InstSpec< T >::LcName);
        }
    }
};

/* ------------------------------------------------------------------------------------------------
 * Functor to retrieve the entity tag.
*/
template < typename T > struct RecvElemFunc
{
    // --------------------------------------------------------------------------------------------
    LightObj  mObj; // Reference to the received object.

    /* --------------------------------------------------------------------------------------------
     * Default constructor.
    */
    RecvElemFunc()
        : mObj(InstSpec< T >::Null())
    {
        /* ... */
    }

    /* --------------------------------------------------------------------------------------------
     * Function call operator.
    */
    void operator () (const typename InstSpec< T >::Instance & inst)
    {
        mObj = inst.mObj;
    }
};

/* ------------------------------------------------------------------------------------------------
 * Functor to forward the the received element to a callback.
*/
template < typename T > struct ForwardElemFunc
{
public:

    // --------------------------------------------------------------------------------------------
    LightObj    mEnv; // The script callback environment object.
    LightObj    mFunc; // The script callback object to receive the element.
    uint32_t      mCount; // The number of elements forwarded by this functor.

    /* --------------------------------------------------------------------------------------------
     * Base constructor.
    */
    explicit ForwardElemFunc(Function & func)
        : mEnv(func.GetEnv()), mFunc(func.GetFunc()), mCount(0)
    {
        if (mFunc.IsNull())
        {
            STHROWF("Invalid script callback");
        }
    }

    /* --------------------------------------------------------------------------------------------
     * Function call operator.
    */
    bool operator () (const typename InstSpec< T >::Instance & inst)
    {
        HSQUIRRELVM vm = SqVM();
        // Push the callback object
        sq_pushobject(vm, mFunc.mObj);
        // Push the callback environment object
        sq_pushobject(vm, mEnv.mObj);
        // Push the element instance on the stack
        sq_pushobject(vm, inst.mObj.mObj);
        // Make the function call and store the result
        SQRESULT res = sq_call(vm, 2, static_cast< SQBool >(true), static_cast< SQBool >(ErrorHandling::IsEnabled()));
        // Make sure the callback object and return value are popped from the stack
        const SqPopGuard pg(vm, 2);
        // Validate the result
        if (SQ_FAILED(res))
        {
            return false; // Stop the iteration process
        }
        // Is the returned value not null?
        else if (sq_gettype(vm, -1) != OT_NULL)
        {
            // Default to continue
            SQBool ret = SQTrue;
            // Obtain the returned value as a boolean
            sq_tobool(vm, -1, &ret);
            // Should we proceed to the next element or stop here?
            if (ret == SQFalse)
            {
                return false; // Stop the iteration process
            }
        }
        // Only successful forwards are counted!
        ++mCount;
        // Proceed to the next element, if any
        return true;
    }

    /* --------------------------------------------------------------------------------------------
     * Implicit cast to the count value.
    */
    operator uint32_t () const // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
    {
        return mCount;
    }
};

/* ------------------------------------------------------------------------------------------------
 * Functor to forward the the received element and specific payload to a callback.
*/
template < typename T > struct ForwardElemDataFunc
{
public:

    // --------------------------------------------------------------------------------------------
    LightObj    mEnv; // The script callback environment object.
    LightObj    mFunc; // The script callback object to receive the element.
    LightObj    mData; // The script payload to accompany the element.
    uint32_t      mCount; // The number of elements forwarded by this functor.

    /* --------------------------------------------------------------------------------------------
     * Base constructor.
    */
    ForwardElemDataFunc(LightObj & data, Function & func)
        : mEnv(func.GetEnv()), mFunc(func.GetFunc()), mData(data), mCount(0)
    {
        if (mFunc.IsNull())
        {
            STHROWF("Invalid script callback");
        }
    }

    /* --------------------------------------------------------------------------------------------
     * Function call operator.
    */
    bool operator () (const typename InstSpec< T >::Instance & inst)
    {
        HSQUIRRELVM vm = SqVM();
        // Push the callback object
        sq_pushobject(vm, mFunc.mObj);
        // Push the callback environment object
        sq_pushobject(vm, mEnv.mObj);
        // Push the element instance on the stack
        sq_pushobject(vm, inst.mObj.mObj);
        // Push the iteration context on the stack
        sq_pushobject(vm, mData.mObj);
        // Make the function call and store the result
        SQRESULT res = sq_call(vm, 3, static_cast< SQBool >(true), static_cast< SQBool >(ErrorHandling::IsEnabled()));
        // Make sure the callback object and return value are popped from the stack
        const SqPopGuard pg(vm, 2);
        // Validate the result
        if (SQ_FAILED(res))
        {
            return false; // Stop the iteration process
        }
        // Is the returned value not null?
        else if (sq_gettype(vm, -1) != OT_NULL)
        {
            // Default to continue
            SQBool ret = SQTrue;
            // Obtain the returned value as a boolean
            sq_tobool(vm, -1, &ret);
            // Should we proceed to the next element or stop here?
            if (ret == SQFalse)
            {
                return false; // Stop the iteration process
            }
        }
        // Only successful forwards are counted!
        ++mCount;
        // Proceed to the next element, if any
        return true;
    }

    /* --------------------------------------------------------------------------------------------
     * Implicit cast to the count value.
    */
    operator uint32_t () const // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
    {
        return mCount;
    }
};

/* ------------------------------------------------------------------------------------------------
 * Functor to count the the received elements.
*/
template < typename T > struct CountElemFunc
{
public:

    // --------------------------------------------------------------------------------------------
    uint32_t      mCount; // The number of elements received by this functor.

    /* --------------------------------------------------------------------------------------------
     * Base constructor.
    */
    CountElemFunc()
        : mCount(0)
    {
        /* ... */
    }

    /* --------------------------------------------------------------------------------------------
     * Function call operator.
    */
    void operator () (const typename InstSpec< T >::Instance & /*inst*/)
    {
        ++mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Implicit cast to the count value.
    */
    operator uint32_t () const // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
    {
        return mCount;
    }
};

/* ------------------------------------------------------------------------------------------------
 * Shared utilities for entities in order to avoid duplicate code.
*/
template < typename T > struct Entity
{
public:

    // --------------------------------------------------------------------------------------------
    typedef T Type; // The type of entity that we're working with.
    typedef InstSpec< T > Inst; // The type of entity instance to work with.

    // --------------------------------------------------------------------------------------------
    typedef ValidInstFunc< T >          ValidInst;
    typedef InstTagFunc< T >            InstTag;
    typedef AppendElemFunc< T >         AppendElem;
    typedef RecvElemFunc< T >           RecvElem;
    typedef ForwardElemFunc< T >        ForwardElem;
    typedef ForwardElemDataFunc< T >    ForwardElemData;
    typedef CountElemFunc< T >          CountElem;

public:

    /* --------------------------------------------------------------------------------------------
     * Default constructor.
    */
    Entity() = delete;

    /* --------------------------------------------------------------------------------------------
     * Copy constructor.
    */
    Entity(const Entity & o) = delete;

    /* --------------------------------------------------------------------------------------------
     * Move constructor.
    */
    Entity(Entity && o) = delete;

    /* --------------------------------------------------------------------------------------------
     * Destructor.
    */
    ~Entity() = delete;

    /* --------------------------------------------------------------------------------------------
     * Copy assignment operator.
    */
    Entity & operator = (const Entity & o) = delete;

    /* --------------------------------------------------------------------------------------------
     * Move assignment operator.
    */
    Entity & operator = (Entity && o) = delete;

    /* --------------------------------------------------------------------------------------------
     * Find the entity that matches the specified identifier.
    */
    static inline const LightObj & FindByID(int32_t id)
    {
        // Perform a range check on the specified identifier
        if (INVALID_ENTITYEX(id, Inst::Max))
        {
            STHROWF("The specified {} identifier is invalid: {}", Inst::LcName, id);
        }
        // Obtain the ends of the entity pool
        typename Inst::Instances::const_iterator itr = Inst::CBegin();
        typename Inst::Instances::const_iterator end = Inst::CEnd();
        // Process each entity in the pool
        for (; itr != end; ++itr)
        {
            // Does the identifier match the specified one?
            if (itr->mID == id)
            {
                return itr->mObj; // Stop searching and return this entity
            }
        }
        // Unable to locate a player matching the specified identifier
        return Inst::Null();
    }

    /* --------------------------------------------------------------------------------------------
     * Find all active entities of this type.
    */
    static inline Array AllActive()
    {
        const StackGuard sg;
        // Allocate an empty array on the stack
        sq_newarray(SqVM(), 0);
        // Process each entity in the pool
        Collect(Inst::CBegin(), Inst::CEnd(), ValidInst(), AppendElem());
        // Return the array at the top of the stack
        return Var< Array >(SqVM(), -1).value;
    }

    /* --------------------------------------------------------------------------------------------
     * Collect all entities of this type where the tag matches or not the specified one.
    */
    static inline Array AllWhereTagEquals(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Remember the current stack size
        const StackGuard sg;
        // Allocate an empty array on the stack
        sq_newarray(SqVM(), 0);
        // Process each entity in the pool
        EachEquals(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(), AppendElem(), tag, !neg, cs);
        // Return the array at the top of the stack
        return Var< Array >(SqVM(), -1).value;
    }

    /* --------------------------------------------------------------------------------------------
     * Collect all entities of this type where the tag begins or not with the specified string.
    */
    static inline Array AllWhereTagBegins(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Remember the current stack size
        const StackGuard sg;
        // Allocate an empty array on the stack
        sq_newarray(SqVM(), 0);
        // Process each entity in the pool
        EachBegins(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(), AppendElem(), tag, strlen(tag), !neg, cs);
        // Return the array at the top of the stack
        return Var< Array >(SqVM(), -1).value;
    }

    /* --------------------------------------------------------------------------------------------
     * Collect all entities of this type where the tag ends or not with the specified string.
    */
    static inline Array AllWhereTagEnds(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Remember the current stack size
        const StackGuard sg;
        // Allocate an empty array on the stack
        sq_newarray(SqVM(), 0);
        // Process each entity in the pool
        EachEnds(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(), AppendElem(), tag, strlen(tag), !neg, cs);
        // Return the array at the top of the stack
        return Var< Array >(SqVM(), -1).value;
    }

    /* --------------------------------------------------------------------------------------------
     * Collect all entities of this type where the tag contains or not the specified string.
    */
    static inline Array AllWhereTagContains(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Remember the current stack size
        const StackGuard sg;
        // Allocate an empty array on the stack
        sq_newarray(SqVM(), 0);
        // Process each entity in the pool
        EachContains(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(), AppendElem(), tag, !neg, cs);
        // Return the array at the top of the stack
        return Var< Array >(SqVM(), -1).value;
    }

    /* --------------------------------------------------------------------------------------------
     * Collect all entities of this type where the tag matches or not the specified filter.
    */
    static inline Array AllWhereTagMatches(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Remember the current stack size
        const StackGuard sg;
        // Allocate an empty array on the stack
        sq_newarray(SqVM(), 0);
        // Process each entity in the pool
        EachMatches(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(), AppendElem(), tag, !neg, cs);
        // Return the array at the top of the stack
        return Var< Array >(SqVM(), -1).value;
    }

    /* --------------------------------------------------------------------------------------------
     * Retrieve the first entity of this type where the tag matches or not the specified one.
    */
    static inline LightObj FirstWhereTagEquals(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element receiver
        RecvElem recv;
        // Process each entity in the pool
        FirstEquals(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< RecvElem >(recv), tag, !neg, cs);
        // Return the received element, if any
        return recv.mObj;
    }

    /* --------------------------------------------------------------------------------------------
     * Retrieve the first entity of this type where the tag begins or not with the specified string.
    */
    static inline LightObj FirstWhereTagBegins(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element receiver
        RecvElem recv;
        // Process each entity in the pool
        FirstBegins(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< RecvElem >(recv), tag, strlen(tag), !neg, cs);
        // Return the received element, if any
        return recv.mObj;
    }

    /* --------------------------------------------------------------------------------------------
     * Retrieve the first entity of this type where the tag ends or not with the specified string.
    */
    static inline LightObj FirstWhereTagEnds(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element receiver
        RecvElem recv;
        // Process each entity in the pool
        FirstEnds(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< RecvElem >(recv), tag, strlen(tag), !neg, cs);
        // Return the received element, if any
        return recv.mObj;
    }

    /* --------------------------------------------------------------------------------------------
     * Retrieve the first entity of this type where the tag contains or not the specified string.
    */
    static inline LightObj FirstWhereTagContains(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element receiver
        RecvElem recv;
        // Process each entity in the pool
        FirstContains(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< RecvElem >(recv), tag, !neg, cs);
        // Return the received element, if any
        return recv.mObj;
    }

    /* --------------------------------------------------------------------------------------------
     * Retrieve the first entity of this type where the tag matches or not the specified filter.
    */
    static inline LightObj FirstWhereTagMatches(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element receiver
        RecvElem recv;
        // Process each entity in the pool
        FirstMatches(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< RecvElem >(recv), tag, !neg, cs);
        // Return the received element, if any
        return recv.mObj;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all active entities of this type.
    */
    static inline uint32_t EachActive(Function & func)
    {
        // Create a new element forwarder
        ForwardElem fwd(func);
        // Process each entity in the pool
        CollectWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(),
                        std::reference_wrapper< ForwardElem >(fwd));
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all active entities of this type.
    */
    static inline uint32_t EachActiveData(LightObj & data, Function & func)
    {
        // Create a new element forwarder
        ForwardElemData fwd(data, func);
        // Process each entity in the pool
        CollectWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(),
                        std::reference_wrapper< ForwardElemData >(fwd));
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag matches or not the specified one.
    */
    static inline uint32_t EachWhereTagEquals(bool neg, bool cs, const SQChar * tag, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElem fwd(func);
        // Process each entity in the pool
        EachEqualsWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElem >(fwd), tag, !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag matches or not the specified one.
    */
    static inline uint32_t EachWhereTagEqualsData(bool neg, bool cs, const SQChar * tag, LightObj & data, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElemData fwd(data, func);
        // Process each entity in the pool
        EachEqualsWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElemData >(fwd), tag, !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag begins with the specified string.
    */
    static inline uint32_t EachWhereTagBegins(bool neg, bool cs, const SQChar * tag, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElem fwd(func);
        // Process each entity in the pool
        EachBeginsWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElem >(fwd), tag, strlen(tag), !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag begins with the specified string.
    */
    static inline uint32_t EachWhereTagBeginsData(bool neg, bool cs, const SQChar * tag, LightObj & data, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElemData fwd(data, func);
        // Process each entity in the pool
        EachBeginsWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElemData >(fwd), tag, strlen(tag), !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag ends or not with the specified string.
    */
    static inline uint32_t EachWhereTagEnds(bool neg, bool cs, const SQChar * tag, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElem fwd(func);
        // Process each entity in the pool
        EachEndsWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElem >(fwd), tag, strlen(tag), !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag ends or not with the specified string.
    */
    static inline uint32_t EachWhereTagEndsData(bool neg, bool cs, const SQChar * tag, LightObj & data, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElemData fwd(data, func);
        // Process each entity in the pool
        EachEndsWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElemData >(fwd), tag, strlen(tag), !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag contains the specified string.
    */
    static inline uint32_t EachWhereTagContains(bool neg, bool cs, const SQChar * tag, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElem fwd(func);
        // Process each entity in the pool
        EachContainsWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElem >(fwd), tag, !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag contains the specified string.
    */
    static inline uint32_t EachWhereTagContainsData(bool neg, bool cs, const SQChar * tag, LightObj & data, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElemData fwd(data, func);
        // Process each entity in the pool
        EachContainsWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElemData >(fwd), tag, !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag match the specified filter.
    */
    static inline uint32_t EachWhereTagMatches(bool neg, bool cs, const SQChar * tag, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElem fwd(func);
        // Process each entity in the pool
        EachMatchesWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElem >(fwd), tag, !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Process all entities of this type where the tag match the specified filter.
    */
    static inline uint32_t EachWhereTagMatchesData(bool neg, bool cs, const SQChar * tag, LightObj & data, Function & func)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element forwarder
        ForwardElemData fwd(data, func);
        // Process each entity in the pool
        EachMatchesWhile(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< ForwardElemData >(fwd), tag, !neg, cs);
        // Return the forward count
        return fwd.mCount;
    }

    /* --------------------------------------------------------------------------------------------
     * Count all active entities of this type.
    */
    static inline uint32_t CountActive()
    {
        // Create a new element counter
        CountElem cnt;
        // Process each entity in the pool
        Collect(Inst::CBegin(), Inst::CEnd(), ValidInst(),
                        std::reference_wrapper< CountElem >(cnt));
        // Return the count
        return cnt;
    }

    /* --------------------------------------------------------------------------------------------
     * Count all entities of this type where the tag matches or not the specified one.
    */
    static inline uint32_t CountWhereTagEquals(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element counter
        CountElem cnt;
        // Process each entity in the pool
        EachEquals(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< CountElem >(cnt), tag, !neg, cs);
        // Return the count
        return cnt;
    }

    /* --------------------------------------------------------------------------------------------
     * Count all entities of this type where the tag begins with the specified string.
    */
    static inline uint32_t CountWhereTagBegins(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element counter
        CountElem cnt;
        // Process each entity in the pool
        EachBegins(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< CountElem >(cnt), tag, strlen(tag), !neg, cs);
        // Return the count
        return cnt;
    }

    /* --------------------------------------------------------------------------------------------
     * Count all entities of this type where the tag ends or not with the specified string.
    */
    static inline uint32_t CountWhereTagEnds(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element counter
        CountElem cnt;
        // Process each entity in the pool
        EachEnds(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< CountElem >(cnt), tag, strlen(tag), !neg, cs);
        // Return the count
        return cnt;
    }

    /* --------------------------------------------------------------------------------------------
     * Count all entities of this type where the tag contains the specified string.
    */
    static inline uint32_t CountWhereTagContains(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element counter
        CountElem cnt;
        // Process each entity in the pool
        EachContains(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                        std::reference_wrapper< CountElem >(cnt), tag, !neg, cs);
        // Return the count
        return cnt;
    }

    /* --------------------------------------------------------------------------------------------
     * Count all entities of this type where the tag matches the specified filter.
    */
    static inline CountElem CountWhereTagMatches(bool neg, bool cs, const SQChar * tag)
    {
        SQMOD_VALID_TAG_STR(tag)
        // Create a new element counter
        CountElem cnt;
        // Process each entity in the pool
        EachMatches(Inst::CBegin(), Inst::CEnd(), ValidInst(), InstTag(),
                    std::reference_wrapper< CountElem >(cnt), tag, !neg, static_cast< bool >(cs));
        // Return the count
        return cnt;
    }
};

} // Namespace:: Algo
} // Namespace:: SqMod
