ReflectedSet.h

// ReflectedSet.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2022

@import "IObject.h"
@import "ReflectedType.h"
@import "ReflectedMap.h"

namespace ceda
{
template <typename K> class xset;

/*
set insert/delete is treated as an assignment to a boolean, mapped by the key.
*/
typedef AssignValue_params SetInsert_params;
typedef AssignValue_params SetDelete_params;

struct InsertIntoMSet_params : public FieldParams
{
    // prefs to be inserted.
    const prefbase* src;
    ssize_t n;
};

struct DeleteFromMSet_params : public FieldParams
{
    // prefs to be deleted.
    const prefbase* src;
    ssize_t n;
};

@api void OCB_SetInsert(SetInsert_params&);
@api void OCB_SetDelete(SetDelete_params&);

@api void OCB_InsertIntoMSet(InsertIntoMSet_params&);
@api void OCB_DeleteFromMSet(DeleteFromMSet_params&);

template <typename K>
inline bool genop_SetInsert(ptr<IObject> obj, const FieldPath& pathIncludingKey, xset<K>& field, const K& key)
{
    if (field.Insert(key))
    {
        SetInsert_params params;
        params.obj = obj;
        params.fid.path = pathIncludingKey;
        params.addr = &field;
        params.visitPrefsFn = nullptr;         // todo
        OCB_SetInsert(params);
        return true;
    }
    else
    {
        return false;
    }
}

template <typename K>
inline bool genop_SetDelete(ptr<IObject> obj, const FieldPath& pathIncludingKey, xset<K>& field, const K& key)
{
    if (field.Delete(key))
    {
        SetDelete_params params;
        params.obj = obj;
        params.fid.path = pathIncludingKey;
        params.addr = &field;
        params.visitPrefsFn = nullptr;         // todo
        OCB_SetDelete(params);
        return true;
    }
    else
    {
        return false;
    }
}

template <typename K>
inline void genop_InsertIntoMSet(ptr<IObject> obj, const FieldPath& path, xset<K>& field,
    const K* src, ssize_t n)
{
    InsertIntoMSet_params params;
    params.obj = obj;
    params.fid.path = path;
    params.addr = &field;
    params.src = src;
    params.n = n;
    OCB_InsertIntoMSet(params);  // callback must be invoked before insertions are actually made

    for (ssize_t i = 0 ; i < n ; ++i)
    {
        field.Insert(src[i]);
    }
}

template <typename K>
inline void genop_DeleteFromMSet(ptr<IObject> obj, const FieldPath& path, xset<K>& field,
    const K* src, ssize_t n)
{
    for (ssize_t i = 0 ; i < n ; ++i)
    {
        field.Delete(src[i]);
    }
    DeleteFromMSet_params params;
    params.obj = obj;
    params.fid.path = path;
    params.addr = &field;
    params.src = src;
    params.n = n;
    OCB_DeleteFromMSet(params);   // callback must be invoked after deletions are actually made???
}

template <typename K>
inline bool genop_InsertIntoMSet(ptr<IObject> obj, const FieldPath& path, xset<K>& field,
    const K& src)
{
    InsertIntoMSet_params params;
    params.obj = obj;
    params.fid.path = path;
    params.addr = &field;
    params.src = &src;
    params.n = 1;
    OCB_InsertIntoMSet(params);   // callback must be invoked before insertions are actually made
    return field.Insert(src);
}

template <typename K>
inline bool genop_DeleteFromMSet(ptr<IObject> obj, const FieldPath& path, xset<K>& field,
    const K& src)
{
    if (field.Delete(src))
    {
        DeleteFromMSet_params params;
        params.obj = obj;
        params.fid.path = path;
        params.addr = &field;
        params.src = &src;
        params.n = 1;
        OCB_DeleteFromMSet(params);   // callback must be invoked after deletions are actually made???
        return true;
    }
    else
    {
        return false;
    }
}

$interface+ IXSet
{
    // A bit weird to think this as a method!
    void Construct();
    
    // Reads the key from the archive, and inserts an element with that key into the set.
    void Insert(InputArchive& ar);

    // Reads the key from the archive, and deletes the element with that key (if any)
    // from the set.
    void Delete(InputArchive& ar);
    
    // Reads the key from the archive, and sets found according to whether the key is 
    // present in the set.
    void Find(InputArchive& ar, bool& found) const;

    // Total number of elements in the set
    ssize_t size() const;
    
    // Serialise the entire set to the given archive
    void Serialise(Archive& ar) const;

    // Deserialise the entire set from the given archive
    void Deserialise(InputArchive& ar);
    
    ptr<IObject> GetRootNode();
    
    // Retrieves an iterator that can be used to iterate through all key values of the set
    // The returned iterator must be closed after it is no longer required.
    ptr<IBiDirIterator> GetIterator() const;

    /*static*/ void ScanOver(InputArchive& ar);
    
    /*static*/ ptr<IKeyType> GetKeyType();
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// ReflectedSet

struct ReflectedSet
{
    inline ptr<IXSet> make_ptr_IXSet(void* self) const
    {
        cxAssert(self);
        cxAssert(IXSetFnTable);
        return make_ptr<IXSet>(self,IXSetFnTable); 
    }
    inline ptr<const IXSet> make_ptr_IXSet(const void* self) const
    {
        cxAssert(self);
        cxAssert(IXSetFnTable);
        return make_ptr<IXSet>(self,IXSetFnTable); 
    }
    
    inline void Insert(void* data, InputArchive& ar) const
    {
        cxAssert(data);
        cxAssert(IXSetFnTable);
        cxAssert(IXSetFnTable->Insert);
        (IXSetFnTable->Insert)(data, ar);
    }

    inline void Delete(void* data, InputArchive& ar) const
    {
        cxAssert(data);
        cxAssert(IXSetFnTable);
        cxAssert(IXSetFnTable->Delete);
        (IXSetFnTable->Delete)(data, ar);
    }

    const TypeOps& ops;
    ReflectedType element;
    const IXSet::FnTable* IXSetFnTable = nullptr;
};

@api xostream& operator<<(xostream& os, const ReflectedSet& r);

template<typename T> struct is_set { constexpr static bool value = false; };
template<typename K> struct is_set<xset<K>> { constexpr static bool value = true; };

template<typename K>
inline const ReflectedSet& GetReflectedSet()
{
    static ReflectedSet s =
    {
        GetTypeOpsX<xset<K>>(),
        GetReflectedType<K>(),
        &IXSet::Stubs<xset<K>>::GetTable()
    };
    return s;
}

template<typename K>
struct GetReflectedType_class<xset<K>> 
{
    static inline ReflectedType get()
    {
        ReflectedType r;
        r.tag = EReflectedTypeTag::Set;
        r.Set = &GetReflectedSet<K>();
        return r;
    }
};
} // namespace ceda