RV_type.h

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

@import "Object.h"
@import "ConstructDestructCopyAssignReflectedVariable.h"
#include "Ceda/cxUtils/StringExt.h"

namespace ceda
{
    class RV_Class;
    class RR_Class;
    
    /*
    $mixin RVmixin
    {
        void Write(xostream& os) const
        {
            if (ops().HasPrint())
            {
                ops().PrintVariable(os, data());
            }
            else
            {
                os << "instance of " << typeName();
            }
        }
    };
    */

    /*
    class RV_base
    {
    public:
        RV_base(ssize_t size) : data_(size) {}
        inline void* data() { return data_.data(); }
        inline const void* data() const { return data_.data(); }
        inline ssize_t size() const { return data_.size(); }
    private:
        VectorOfByte data_;
    };
    */

    /*
    class @api RV_Type
    {
    public:
        RV_Type(const ReflectedType& type);
        RV_Type(const RV_Type& rhs);
        RV_Type& operator=(const RV_Type& rhs);
        ~RV_Type();
        inline void* data() { return data_.data(); }
        inline const void* data() const { return data_.data(); }
        inline const char* TypeName() const { return rc_.name; }
        inline const ReflectedClass& rc() const { return rc_; }
        inline const TypeOps& ops() const { return rc_.ops; }
        inline ssize_t size() const { return data_.size(); }
        bool assign(const RV_Class& rhs, bool allowExplicitConversions);
        bool assign(const RR_Class& rhs, bool allowExplicitConversions);
        void Write(xostream& os) const;
    private:
        VectorOfByte data_;
        const TypeOps& ops_;
        ReflectedType type_;
    };
    */

    class @api RV_Class
    {
    public:
        RV_Class(const ReflectedClass& rc);
        RV_Class(const RV_Class& rhs);
        RV_Class(const RR_Class& rhs);
        RV_Class& operator=(const RV_Class& rhs);
        RV_Class& operator=(const RR_Class& rhs);
        ~RV_Class();
        inline void* data() { return data_.data(); }
        inline const void* data() const { return data_.data(); }
        inline const char* TypeName() const { return rc_.name; }
        inline const ReflectedClass& rc() const { return rc_; }
        inline const TypeOps& ops() const { return rc_.ops; }
        inline ssize_t size() const { return data_.size(); }
        bool assign(const RV_Class& rhs, bool allowExplicitConversions);
        bool assign(const RR_Class& rhs, bool allowExplicitConversions);
        void Write(xostream& os) const;
    private:
        const ReflectedClass& rc_;
        VectorOfByte data_;
    };

    class @api RR_Class
    {
    public:
        RR_Class(const ReflectedClass& rc, void* data);
        RR_Class(RV_Class& rhs);
        RR_Class(RR_Class& rhs);
        RR_Class& operator=(const RV_Class& rhs);
        RR_Class& operator=(const RR_Class& rhs);
        inline void* data() { return data_; }
        inline const void* data() const { return data_; }
        inline const char* TypeName() const { return rc_.name; }
        inline const ReflectedClass& rc() const { return rc_; }
        inline const TypeOps& ops() const { return rc_.ops; }
        ssize_t size() const { return ops().GetSize(); }
        bool assign(const RV_Class& rhs, bool allowExplicitConversions);
        bool assign(const RR_Class& rhs, bool allowExplicitConversions);
        void Write(xostream& os) const;
    private:
        const ReflectedClass& rc_;
        void* data_;
    };

#if 0    
    /*
    It might be a bad idea to have the bool flags 'owned' and 'readOnly'.
    It prevents us from writing copy assignment and copy constructors.

    Instead it might be better to
        1)  Ignore constness - that falls outside the scope
        2)  Have two kinds of reflected variables - one for owned and one for aliases

    RV_Class    - reflected variable which owns the object
    RR_Class    - reference to a reflected variable
    */

    struct RV_base
    {
        void* data;                   // Ptr to the variable.  Never NULL
        bool owned;                   // Does this object own the (heap allocated) variable?
        bool readOnly;                // Do we impose read-only access to the variable?
    };

    struct RV_Type : RV_base
    {
        const ReflectedType* r;
    };

    struct RV_Leaf : RV_base
    {
        const ReflectedLeafType* r;
    };

    struct RV_Class : RV_base
    {
        const ReflectedClass& r;

        // Create mutable owned instance
        RV_Class(const ReflectedClass& r) :
            r(r),
            data( new octet_t[r.size] ),
            owned(true),
            readOnly(false)
        {
            ConstructReflectedClassVariable(r,data);
        }

        // Create mutable aliased instance
        RV_Class(const ReflectedClass& r, void* data) :
            r(r),
            data(data),
            owned(false),
            readOnly(false)
        {
        }

        // Create immutable aliased instance
        RV_Class(const ReflectedClass& r, const void* data) :
            r(r),
            data(const_cast<void*>(data)),
            owned(false),
            readOnly(true)
        {
        }

        RV_Class(const RV_Class& rhs) :
            r(r)
        {
            if (rhs.owned)
            {
                // Create a copy
                data = new octet_t[r.size];
                owned = true;
                readOnly = false;


            }
        }

        /*
        static RV_Class Create(const ReflectedClass& rc)
        {
            RV_Class rv;
            rv.data = new octet_t[rc.size];
            ConstructReflectedClassVariable(rc, rv.data);
            rv.r = &rc;
            rv.owned = true;
            rv.readOnly = false;
            return rv;
        }
        */

        static std::optional<RV_Class> CreateCopy(const ReflectedClass& rc, const RV_Class& rhs, bool allowExplicitConversions)
        {
            RV_Class rv;
            rv.data = new octet_t[rc.size];
            if (CopyConstructReflectedClassVariable(rc, rv.data, *rhs.r, rhs.data, allowExplicitConversions))
            {
                rv.r = &rc;
                rv.owned = true;
                rv.readOnly = false;
                return rv;
            }
            else
            {
                delete [] (octet_t*) rv.data;
                return nullptr;
            }
        }

        ~RV_Class()
        {
            if (owned)
            {
                DestructReflectedClassVariable(*r, data);
                delete [] (octet_t*)data;
            }
        }

        void assign(const RV_Class& rhs)
        {
            if (rhs.r == r)
            {
                AssignReflectedClassVariable(*r, data, rhs.data);
            }
        }

        bool assign(const RV_Class& rhs, bool allowExplicitConversions)
        {
            return AssignReflectedClassVariable(*r, data, *rhs.r, rhs.data, allowExplicitConversions);
        }
    };

    struct RV_Variant : RV_base
    {
        const ReflectedVariant* r;
    };

    struct RV_Enum : RV_base
    {
        const ReflectedEnum* r;
    };
    struct RV_Array : RV_base
    {
        const ReflectedArray* r;
    };

    struct RV_DynArray : RV_base
    {
        const ReflectedDynArray* r;
    };

    struct RV_Vector : RV_base
    {
        const ReflectedVector* r;
    };

    struct RV_Map : RV_base
    {
        const ReflectedMap* r;
    };

    struct RV_Set : RV_base
    {
        const ReflectedSet* r;
    };

    struct RV_Pointer : RV_base
    {
        const ReflectedPointer* r;
    };

    struct RV_Ptr : RV_base
    {
        const ReflectedInterfacePtr* r;
    };

    struct RV_Pref : RV_base
    {
        const ReflectedPref* r;
    };

    struct RV_OpenVariant : RV_base
    {
        const ReflectedOpenVariant* r;
    };
#endif
} // namespace ceda