////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 2012-2019 (c) Baical                                                        /
//                                                                             /
// This library is free software; you can redistribute it and/or               /
// modify it under the terms of the GNU Lesser General Public                  /
// License as published by the Free Software Foundation; either                /
// version 3.0 of the License, or (at your option) any later version.          /
//                                                                             /
// This library is distributed in the hope that it will be useful,             /
// but WITHOUT ANY WARRANTY; without even the implied warranty of              /
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU           /
// Lesser General Public License for more details.                             /
//                                                                             /
// You should have received a copy of the GNU Lesser General Public            /
// License along with this library.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#ifndef PROP_H
#define PROP_H

#include "GTypes.h"
#include "PAtomic.h"
#include "PString.h"
#include "AList.h"
#include "Lock.h"


/// <summary> 
/// Properties class, combine all functionality in single class to simplify usage without casting from base class to Int, 
/// Text, Group, etc ....
/// </summary>
class CProperty
{
    friend class CAList<CProperty*>;

public:
    class IApplyer
    {
    public:
        virtual tBOOL Apply(CProperty *i_pItem, const void *i_pOld_Value) = 0;
    };

    class IData
    {
    public:
        virtual void Release() = 0;
    };

    enum eFlags
    {
        eEmpty        = 0x00000000,
        eReadOnly     = 0x00000001,
        eInvisible    = 0x00000002,
        ePath         = 0x00000004, //specify is text variable = path
        eDir          = 0x00000008, //specify is it path to dir, otherwise - file, have to be used with ePath
        ePathExists   = 0x00000010, //specify is it path has to be existing, has to be used with ePath
        eUserModified = 0x00000020, //specify is parameter has been modified by user

        eFlagsMax
    };

    enum eType
    {
        eGroup,
        eText,
        eInt,
        eEnum,

        eTypeMax
    };
private:
    struct sItem
    {
        tXCHAR *pName;
        tINT64  llValue;
        
        sItem(const tXCHAR *i_pName, tINT64 i_llValue)
        {
            pName   = PStrDub(i_pName);
            llValue = i_llValue;
        }

        ~sItem()
        {
            if (pName)
            {
                PStrFreeDub(pName);
                pName = NULL;
            }
        }
    };

    tINT32 volatile m_lReference;
    eType           m_eType;
    tXCHAR         *m_pName;
    tUINT32         m_uHash;
    tXCHAR         *m_pDesc;
    CLock          *m_pLock;

    IData          *m_iData;
    tUINT8         *m_pData;
    size_t          m_szData;

    IApplyer       *m_pApplyer;
    eFlags          m_eFlags;

    CProperty      *m_pParent;

    union
    {
        struct 
        {
            CAList<CProperty*> *m_pSubItems;
        } m_sGroup;

        struct 
        {
            tINT64  m_llValue; //Int value
            tINT64  m_llMin; 
            tINT64  m_llMax; 
        } m_sInt;

        struct 
        {
            tINT32          m_iCur_Index;
            CAList<sItem*> *m_pItems;
        } m_sEnum;

        struct 
        {
            tXCHAR *m_ptValue; //Text value
        } m_sText;
    };

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::CProperty()
    CProperty(CProperty    *i_pParent, 
              eType         i_eType, 
              const tXCHAR *i_pName, 
              tBOOL        &o_rError
             )
        : m_lReference(1)
        , m_eType(i_eType)
        , m_pName(NULL)
        , m_uHash(0)
        , m_pDesc(NULL)
        , m_pLock(NULL)
        , m_iData(NULL)
        , m_pData(NULL)
        , m_szData(0)
        , m_pApplyer(NULL)
        , m_eFlags(eEmpty)
        , m_pParent(NULL)
    {
        CProperty *l_pTmp = NULL;

        o_rError = FALSE;

        //clean biggest union member
        memset(&m_sInt, 0, sizeof(m_sInt));

        if (    (    (eGroup != m_eType)
                  && (!i_pParent)
                )
             || (    (i_pParent)
                  && (eGroup != i_pParent->Type())
                )
             || (!i_pName)
           )
        {
            o_rError = TRUE;
            goto l_lblExit;
        }

        //check - perhaps parent already has sub-item with the same name
        l_pTmp = NULL; 
        if (    (i_pParent)
             && (i_pParent->Get_SubItem(i_pName, l_pTmp))
           )
        {
            if (l_pTmp)
            {
                l_pTmp->Release();
            }

            o_rError = TRUE;
            goto l_lblExit;
        }

        m_pName = PStrDub(i_pName);
        if (NULL == m_pName)
        {
            o_rError = TRUE;
            goto l_lblExit;
        }

        m_uHash = GetHash(m_pName);

        m_pLock = new CLock();

        if (eGroup == m_eType)
        {
            memset(&m_sGroup, 0, sizeof(m_sGroup));
            m_sGroup.m_pSubItems = new CAList<CProperty*>();
        }
        else if (eText == m_eType)
        {
            memset(&m_sText, 0, sizeof(m_sText));
        }
        else if (eInt == m_eType)
        {
            memset(&m_sInt, 0, sizeof(m_sInt));
        }
        else if (eEnum == m_eType)
        {
            memset(&m_sEnum, 0, sizeof(m_sEnum));
            m_sEnum.m_iCur_Index = -1;
            m_sEnum.m_pItems     = new CAList<sItem*>();
        }

     l_lblExit:
        if (    (!o_rError)
             && (i_pParent)
           )
        {
            o_rError = ! i_pParent->Add_SubItem(this);
            if (!o_rError)
            {
                m_pParent = i_pParent;
            }
        }
    }//CProperty::CProperty()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::~CProperty()
    virtual ~CProperty()
    {
        if (m_pLock)
        {
            m_pLock->Lock();
        }

        if (m_pName)
        {
            //OutputDebugStringW(L"Delete property: ");
            //OutputDebugStringW(m_pName);
            //OutputDebugStringW(L"\n");

            PStrFreeDub(m_pName);
            m_pName = NULL;
        }

        if (m_iData)
        {
            m_iData->Release();
            m_iData = NULL;
        }

        if (m_pData)
        {
            free(m_pData);
            m_pData = NULL;
        }

        m_szData = 0;

        if (m_pDesc)
        {
            PStrFreeDub(m_pDesc);
            m_pDesc = NULL;
        }

        if (eGroup == m_eType)
        {
            if (m_sGroup.m_pSubItems)
            {
                pAList_Cell l_pEl = NULL;
                while ((l_pEl = m_sGroup.m_pSubItems->Get_First()))
                {
                    CProperty *l_pItem = m_sGroup.m_pSubItems->Get_Data(l_pEl);
                    if (l_pItem)
                    {
                        l_pItem->Release();
                    }

                    m_sGroup.m_pSubItems->Del(l_pEl, FALSE);
                }

                delete m_sGroup.m_pSubItems;
                m_sGroup.m_pSubItems = NULL;
            }
        }
        else if (eText == m_eType)
        {
            if (m_sText.m_ptValue)
            {
                PStrFreeDub(m_sText.m_ptValue);
            }

            m_sText.m_ptValue = NULL;
        }
        else if (eInt == m_eType)
        {
        }
        else if (eEnum == m_eType)
        {
            if (m_sEnum.m_pItems)
            {
                m_sEnum.m_pItems->Clear(TRUE);
                delete m_sEnum.m_pItems;
            }
            m_sEnum.m_pItems = NULL;
        }

        if (m_pLock)
        {
            m_pLock->Unlock();

            delete m_pLock;
            m_pLock = NULL;
        }

        m_pParent = NULL;
    }//CProperty::~CProperty()

public:
    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Create()
    static tBOOL Create(CProperty    *i_pParent, 
                        eType         i_eType, 
                        const tXCHAR *i_pName, 
                        CProperty   *&o_pProperty
                       )
    {
        tBOOL      l_bError   = FALSE;
        tXCHAR    *l_pName    = PStrDub(i_pName);
        tXCHAR    *l_pSubName = l_pName;
        tXCHAR    *l_pSlash   = PStrChr(l_pName, TM('/'));
        CProperty *l_pReturn  = NULL;
        CProperty *l_pParent  = i_pParent;    

        if (!l_pName)
        {
            return FALSE;
        }

        if (i_pParent)
        {
            i_pParent->Add_Ref();
            l_pParent->m_pLock->Lock();
        }

        while (    (l_pSlash)
                && (FALSE == l_bError)
              )
        {
            while (TM('/') == *l_pSlash)
            {
                *l_pSlash = 0;
                l_pSlash ++;
            }

            if (i_pParent)
            {
                CProperty *l_pChild = NULL;
                CProperty *l_pTemp  = i_pParent;
                i_pParent->Get_SubItem(l_pSubName, l_pChild);
                if (!l_pChild)
                {
                    l_pChild = new CProperty(i_pParent, eGroup, l_pSubName, l_bError);
                }

                i_pParent = l_pChild;
                l_pTemp->Release();
            }


            l_pSubName = l_pSlash;
            l_pSlash   = PStrChr(l_pSubName, TM('/'));
        }

        if (!l_bError)
        {
            l_pReturn = new CProperty(i_pParent, i_eType, l_pSubName, l_bError);

            if (l_bError)
            {
                delete l_pReturn;
                l_pReturn = NULL;
            }
        }

        if (i_pParent)
        {
            i_pParent->Release();
        }

        if (l_pParent)
        {
            l_pParent->m_pLock->Unlock();
        }

        o_pProperty = l_pReturn;

        PStrFreeDub(l_pName);
        l_pName = NULL;
        
        return !l_bError;
    }//CProperty::Create()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Type()
    eType Type() 
    {
        CLock l_cLock(m_pLock);
        return m_eType;
    }//CProperty::Type()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Name()
    const tXCHAR *Name()
    {
        CLock l_cLock(m_pLock);
        return m_pName;
    }//CProperty::Name()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Desc()
    const tXCHAR *Desc()
    {
        CLock l_cLock(m_pLock);
        return m_pDesc;
    }//CProperty::Desc()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Flags()
    CProperty::eFlags Flags()
    {
        CLock l_cLock(m_pLock);
        return m_eFlags;
    }//CProperty::Flags()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Add_Ref()
    tINT32 Add_Ref()
    {
        return ATOMIC_INC(&m_lReference);
    }//CProperty::Add_Ref()

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Release()
    tINT32 Release()                                             
    {
        tINT32 l_lResult = ATOMIC_DEC(&m_lReference);
        if ( 0 >= l_lResult )
        {
            delete this;
        }

        return l_lResult;
    }//CProperty::Release()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Applyer()
    tBOOL Set_Applyer(IApplyer *i_pApplyer, tBOOL i_bRecursive = FALSE)
    {
        CLock l_cLock(m_pLock);

        m_pApplyer = i_pApplyer;

        if (    (eGroup == m_eType)
             && (i_bRecursive)
           )
        {
            pAList_Cell l_pEl = NULL;
            while ((l_pEl = m_sGroup.m_pSubItems->Get_Next(l_pEl)))
            {
                CProperty *l_pItem = m_sGroup.m_pSubItems->Get_Data(l_pEl);
                if (l_pItem)
                {
                    l_pItem->Set_Applyer(i_pApplyer, i_bRecursive);
                }
            }
        }

        return TRUE;
    }//CProperty::Set_Applyer()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Applyer()
    IApplyer* Applyer()
    {
        CLock l_cLock(m_pLock);

        return m_pApplyer;
    }//CProperty::Applyer()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Applyer()
    tBOOL Set_Desc(const tXCHAR *i_pDesc)
    {
        CLock l_cLock(m_pLock);

        if (m_pDesc)
        {
            PStrFreeDub(m_pDesc);
        }

        if (i_pDesc)
        {
            m_pDesc = PStrDub(i_pDesc);
        }
        else
        {
            m_pDesc = NULL;
        }

        return TRUE;
    }//CProperty::Set_Applyer()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Flags()
    void Set_Flags(CProperty::eFlags i_eFlags)
    {
        CLock l_cLock(m_pLock);
        m_eFlags = i_eFlags;
    }//CProperty::Set_Flags()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Flags()
    void Set_Flags(int i_iFlags)
    {
        CLock l_cLock(m_pLock);
        m_eFlags = (CProperty::eFlags)i_iFlags;
    }//CProperty::Set_Flags()

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Flag()
    void Set_Flag(CProperty::eFlags i_eFlags)
    {
        CLock l_cLock(m_pLock);
        m_eFlags = (CProperty::eFlags)(m_eFlags | i_eFlags);
    }//CProperty::Set_Flags()

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Clr_Flag()
    void Clr_Flag(CProperty::eFlags i_eFlags)
    {
        CLock l_cLock(m_pLock);
        m_eFlags = (CProperty::eFlags)(m_eFlags & ~i_eFlags);
    }//CProperty::Set_Flags()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Data()
    tBOOL Set_Data(IData *i_pData)
    {
        CLock l_cLock(m_pLock);
        if (m_iData)
        {
            m_iData->Release();
        }

        m_iData = i_pData;

        return TRUE;
    }//CProperty::Set_Data()

    
    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Data()
    IData *Get_Data()
    {
        CLock l_cLock(m_pLock);
        return m_iData;
    }//CProperty::Get_Data()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Data()
    tBOOL Set_Data(const void *i_pData, size_t i_szData)
    {
        if (    (NULL == i_pData)
             || (0 >= i_szData)
           )
        {
            return FALSE;
        }

        CLock   l_cLock(m_pLock);
        tUINT8 *l_pTemp = (tUINT8*)malloc(i_szData);
        if (l_pTemp)
        {
            if (m_pData)
            {
                free(m_pData);
            }
            m_pData = l_pTemp;

            memcpy(m_pData, i_pData, i_szData);
            m_szData = i_szData;

            return TRUE;
        }

        return FALSE;
    }//CProperty::Set_Data()

    
    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Data()
    tBOOL Get_Data(const void *&o_rData, size_t &o_rSzie)
    {
        if (NULL == m_pData)
        {
            return FALSE;
        }

        CLock l_cLock(m_pLock);
        o_rData = m_pData;
        o_rSzie = m_szData;

        return TRUE;
    }//CProperty::Get_Data()



    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Parent()
    CProperty* Get_Parent()
    {
        CLock l_cLock(m_pLock);
        if (m_pParent)
        {
            m_pParent->Add_Ref();
        }
        return m_pParent;
    }//Get_Parent


    ////////////////////////////////////////////////////////////////////////////
    //                              Groups                                    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_SubItems_Count()
    tBOOL Get_SubItems_Count(size_t &o_rCount)           
    {
        o_rCount = 0;

        if (eGroup != m_eType)
        {
            return FALSE;
        }

        CLock l_cLock(m_pLock);
        o_rCount = m_sGroup.m_pSubItems->Count();
        return TRUE;
    }//CProperty::Get_SubItems_Count()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_SubItem()
    tBOOL Get_SubItem(size_t i_szIndex, CProperty *&o_rItem) 
    {
        o_rItem = NULL;

        if (eGroup != m_eType)
        {
            return FALSE;
        }

        CLock       l_cLock(m_pLock);
        pAList_Cell l_pEl = m_sGroup.m_pSubItems->Get_ByIndex((tUINT32)i_szIndex);

        if (NULL == l_pEl)
        {
            return FALSE;
        }

        o_rItem = m_sGroup.m_pSubItems->Get_Data(l_pEl);

        if (o_rItem)
        {
            o_rItem->Add_Ref();
            return TRUE;
        }

        return FALSE;
    }//CProperty::Get_SubItem()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_SubItem()
    virtual tBOOL Get_SubItem(const tXCHAR *i_pPath, CProperty *&o_rItem)
    {
        if (    (eGroup != m_eType)
             || (!i_pPath)
           )
        {
            return FALSE;
        }

        CLock         l_cLock(m_pLock);
        CProperty    *l_pCurr  = this;
        const tXCHAR *l_pName  = i_pPath;

        while (    (l_pName)
                && (l_pCurr)
                && (*l_pName)
              )
        {
            const tXCHAR *l_pSlash = PStrChr(l_pName, TM('/'));
            CProperty    *l_pNext  = NULL;

            if (eGroup == l_pCurr->m_eType)
            {
                pAList_Cell l_pEl    = NULL;
                size_t      l_szName = (l_pSlash) ? (l_pSlash - l_pName) : PStrLen(l_pName);
                tUINT32     l_uHash  = GetHash(l_pName, l_szName);

                while ((l_pEl = l_pCurr->m_sGroup.m_pSubItems->Get_Next(l_pEl)))
                {
                    CProperty *l_pSub = l_pCurr->m_sGroup.m_pSubItems->Get_Data(l_pEl);
                    if (    (l_uHash == l_pSub->m_uHash)
                         && (0 == PStrNiCmp(l_pSub->m_pName, l_pName, l_szName))
                       )
                    {
                        l_pNext = l_pSub;
                        break;
                    }
                }
            }
            else if (    (eGroup != l_pCurr->m_eType)
                      && (l_pSlash)
                    )
            {
                //not found
            }

            l_pName = l_pSlash;
            l_pCurr = l_pNext;

            while (    (l_pName) 
                    && (*l_pName)
                    && (TM('/') == *l_pName)
                  )
            {
                l_pName ++;
            }

        } // while (l_pName) ...

        o_rItem = l_pCurr;

        if (o_rItem)
        {
            o_rItem->Add_Ref();
            return TRUE;
        }

        return FALSE;
    }//CProperty::Get_SubItem()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Add_SubItem()
    tBOOL Add_SubItem(CProperty *o_pItem) 
    {
        if (    (!o_pItem)
             || (eGroup != m_eType)
           )
        {
            return FALSE;
        }

        CLock       l_cLock(m_pLock);
        pAList_Cell l_pEl     = NULL;
        tBOOL       l_bReturn = TRUE;

        while ((l_pEl = m_sGroup.m_pSubItems->Get_Next(l_pEl)))
        {
            CProperty *l_pSub = m_sGroup.m_pSubItems->Get_Data(l_pEl);
            if (0 == PStrICmp(l_pSub->m_pName, m_pName))
            {
                l_bReturn = FALSE;
                break;
            }
        }

        if (l_bReturn)
        {
            m_sGroup.m_pSubItems->Add_After(m_sGroup.m_pSubItems->Get_Last(), o_pItem);
            o_pItem->Add_Ref();
            o_pItem->Set_Parent(this);
        }

        return l_bReturn; 
    }//CProperty::Add_SubItem()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Del_SubItem()
    tBOOL Del_SubItem(size_t i_szIndex)
    {
        CLock       l_cLock(m_pLock);
        pAList_Cell l_pEl   = NULL;
        CProperty  *l_pItem = NULL;

        if (    (eGroup != m_eType)
             || (i_szIndex >= m_sGroup.m_pSubItems->Count())
           )
        {
            return FALSE;
        }

        l_pEl   = m_sGroup.m_pSubItems->Get_ByIndex((tUINT32)i_szIndex);
        l_pItem = m_sGroup.m_pSubItems->Get_Data(l_pEl);

        m_sGroup.m_pSubItems->Del(l_pEl, FALSE);

        if (l_pItem)
        {
            l_pItem->Set_Parent(NULL);
            l_pItem->Release();
            l_pItem = NULL;
        }

        return TRUE;
    }//CProperty::Del_SubItem()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Del_SubItem()
    tBOOL Del_SubItem(CProperty *i_pItem)
    {
        CLock       l_cLock(m_pLock);
        pAList_Cell l_pEl   = NULL;
        CProperty  *l_pItem = NULL;

        if (    (eGroup != m_eType)
             || (!i_pItem)
           )
        {
            return FALSE;
        }

        while ((l_pEl = m_sGroup.m_pSubItems->Get_Next(l_pEl)))
        {
            if (i_pItem == m_sGroup.m_pSubItems->Get_Data(l_pEl))
            {
                m_sGroup.m_pSubItems->Del(l_pEl, FALSE);
                i_pItem->Set_Parent(NULL);
                i_pItem->Release();
                l_pItem = NULL;
                break;
            }
        }

        return (!l_pItem) ? TRUE : FALSE;
    }//CProperty::Del_SubItem()


    ////////////////////////////////////////////////////////////////////////////
    //                                Text                                    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Value()
    tBOOL Get_Value(const tXCHAR *&o_rText)
    {
        CLock l_cLock(m_pLock);

        if (eText != m_eType)
        {
            return FALSE;
        }

        o_rText = m_sText.m_ptValue;

        return TRUE;
    }//CProperty::Get_Value()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Value()
    tBOOL Set_Value(const tXCHAR *i_pText)
    {
        CLock l_cLock(m_pLock);

        if (eText != m_eType)
        {
            return FALSE;
        }

        tXCHAR *l_pOld    = m_sText.m_ptValue;
        tBOOL   l_bReturn = FALSE;

        if (i_pText)
        {
            m_sText.m_ptValue = PStrDub(i_pText);
        }
        else
        {
            m_sText.m_ptValue = NULL;
        }

        if (Apply(l_pOld))
        {
            if (l_pOld)
            {
                PStrFreeDub(l_pOld);
            }

            l_bReturn = TRUE;
        }
        else
        {
            if (m_sText.m_ptValue)
            {
                PStrFreeDub(m_sText.m_ptValue);
            }

            m_sText.m_ptValue = l_pOld;
        }


        return l_bReturn;
    }//CProperty::Set_Value()


    ////////////////////////////////////////////////////////////////////////////
    //                               Integer                                  //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Range()
    tBOOL Set_Range(tINT64 i_llMin, tINT64 i_llMax)
    {
        CLock l_cLock(m_pLock);

        if (    (eInt != m_eType)
             || (i_llMin >= i_llMax)
           )
        {
            return FALSE;
        }

        m_sInt.m_llMin = i_llMin;
        m_sInt.m_llMax = i_llMax;

        if (m_sInt.m_llValue < m_sInt.m_llMin)
        {
            m_sInt.m_llValue = m_sInt.m_llMin;
        }
        else if (m_sInt.m_llValue > m_sInt.m_llMax)
        {
            m_sInt.m_llValue = m_sInt.m_llMax;
        }

        return TRUE;
    }//CProperty::Set_Range()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Range()
    tBOOL Get_Range(tINT64 &o_rMin, tINT64 &o_rMax)
    {
        CLock l_cLock(m_pLock);

        if (eInt != m_eType)
        {
            return FALSE;
        }

        o_rMin = m_sInt.m_llMin;
        o_rMax = m_sInt.m_llMax;

        return TRUE;
    }//CProperty::Get_Range()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Value()
    tBOOL Get_Value(tINT64  &o_rValue)
    {
        CLock l_cLock(m_pLock);

        if (eInt != m_eType)
        {
            return FALSE;
        }

        o_rValue = m_sInt.m_llValue;

        return TRUE;
    }//CProperty::Get_Value()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Value()
    tBOOL Set_Value(tINT64  i_llValue)
    {
        CLock l_cLock(m_pLock);

        if (eInt != m_eType)
        {
            return FALSE;
        }

        tINT64 l_iOld_Value = m_sInt.m_llValue;

        m_sInt.m_llValue = i_llValue;

        if (!Apply(&l_iOld_Value))
        {
            m_sInt.m_llValue = l_iOld_Value;
            return FALSE;
        }

        return TRUE;
    }//CProperty::Set_Value()


    ////////////////////////////////////////////////////////////////////////////
    //                               Enum                                     //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Current_Index()
    tBOOL Set_Current_Index(tINT32 i_iIndex)
    {
        CLock l_cLock(m_pLock);

        if (    (eEnum != m_eType)
             || (    (0 <= i_iIndex)
                  && ((tUINT32)i_iIndex >= m_sEnum.m_pItems->Count())
                )
           )
        {
            return FALSE;
        }

        tINT32 l_iOld_Index = m_sEnum.m_iCur_Index;

        if (-1 > i_iIndex)
        {
            i_iIndex = -1;
        }

        m_sEnum.m_iCur_Index = i_iIndex;

        if (!Apply(&l_iOld_Index))
        {
            m_sEnum.m_iCur_Index = l_iOld_Index;
            return FALSE;
        }

        return TRUE;
    }//CProperty::Set_Current_Index()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Current_Index()
    tBOOL Get_Current_Index(tINT32 &o_rIndex)
    {
        CLock l_cLock(m_pLock);

        if (eEnum != m_eType)
        {
            return FALSE;
        }

        o_rIndex = m_sEnum.m_iCur_Index;

        return TRUE;
    }//CProperty::Get_Current_Index()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Items_Count()
    tBOOL Get_Items_Count(tUINT32 &o_rCount)
    {
        CLock l_cLock(m_pLock);

        if (eEnum != m_eType)
        {
            return FALSE;
        }

        o_rCount = m_sEnum.m_pItems->Count();

        return TRUE;
    }//CProperty::Get_Items_Count()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Item()
    tBOOL Get_Item(tUINT32 i_dwIndex, const tXCHAR *&o_pName, tINT64 &o_rValue)
    {
        CLock l_cLock(m_pLock);

        if (    (eEnum != m_eType)
             || (i_dwIndex >= m_sEnum.m_pItems->Count())
           )
        {
            return FALSE;
        }

        pAList_Cell l_pEl   = m_sEnum.m_pItems->Get_ByIndex(i_dwIndex);
        sItem      *l_pItem = m_sEnum.m_pItems->Get_Data(l_pEl);

        if (!l_pItem)
        {
            return FALSE;
        }

        o_pName  = l_pItem->pName;
        o_rValue = l_pItem->llValue;

        return TRUE;
    }//CProperty::Get_Item()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Add_Item()
    tBOOL Add_Item(const tXCHAR *i_pName, tINT64 i_llValue, tUINT32 i_dwIndex = 0xFFFFFFFF)
    {
        CLock l_cLock(m_pLock);

        if (    (eEnum != m_eType)
             || (!i_pName)
           )
        {
            return FALSE;
        }

        pAList_Cell l_pEl = m_sEnum.m_pItems->Get_ByIndex(i_dwIndex);

        if (NULL == l_pEl)
        {
            l_pEl = m_sEnum.m_pItems->Get_Last();

            if (m_sEnum.m_pItems->Count())
            {
                i_dwIndex = m_sEnum.m_pItems->Count() - 1;
            }
            else
            {
                i_dwIndex = 0;
            }
        }
        else
        {
            l_pEl = m_sEnum.m_pItems->Get_Prev(l_pEl);
        }

        m_sEnum.m_pItems->Add_After(l_pEl, new sItem(i_pName, i_llValue));

        if (    (0 <= m_sEnum.m_iCur_Index)
             && ((tINT32)i_dwIndex >= m_sEnum.m_iCur_Index)
           )
        {
            m_sEnum.m_iCur_Index ++;
        }

        return TRUE;
    }//CProperty::Add_Item()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Del_Item()
    tBOOL Del_Item(tUINT32 i_dwIndex) 
    {
        CLock l_cLock(m_pLock);

        if (eEnum != m_eType)
        {
            return FALSE;
        }

        pAList_Cell l_pEl = m_sEnum.m_pItems->Get_ByIndex(i_dwIndex);

        if (NULL == l_pEl)
        {
            return FALSE;
        }

        m_sEnum.m_pItems->Del(l_pEl, TRUE);

        if ((tINT32)i_dwIndex == m_sEnum.m_iCur_Index)
        {
            m_sEnum.m_iCur_Index = -1;
        }
        else if ((tINT32)i_dwIndex < m_sEnum.m_iCur_Index)
        {
            m_sEnum.m_iCur_Index --;
        }

        return TRUE;
    }//CProperty::Del_Item()

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Get_Lock()
    CLock *Get_Lock()
    {
        return m_pLock;
    }//CProperty::Get_Lock()
private:


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Set_Parent()
    void Set_Parent(CProperty *i_pParent)
    {
        CLock l_cLock(m_pLock);
        m_pParent = i_pParent;
    }//CProperty::Set_Parent()

    ////////////////////////////////////////////////////////////////////////////
    //CProperty::Apply()
    tBOOL Apply(const void *i_pOld_Value)
    {
        //No lock, used internally inside locked functions
        if (m_pApplyer)
        {
            return m_pApplyer->Apply(this, i_pOld_Value);
        }

        return TRUE;
    }//Property::Apply()


    ////////////////////////////////////////////////////////////////////////////
    //CProperty::GetHash()
    tUINT32 GetHash(const tXCHAR *i_pStr, size_t i_szLen = 0)
    {
        if (!i_pStr)
        {
            return 0;
        }

        //Hash description: http://isthe.com/chongo/tech/comp/fnv/#FNV-param
        //Hash parameters investigation (collisions, randomnessification)
        //http://programmers.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed
        tUINT32 l_uReturn  = 2166136261ul;
        size_t  l_szLength = i_szLen ? i_szLen : PStrLen(i_pStr);

        while (0 < l_szLength)
        {
            tUINT32 l_uItem = 0;

        #if defined(UTF8_ENCODING)
            for (int l_iI = 0; (l_iI < 4) && (0 < l_szLength); l_iI++)
            {
                l_uItem = (l_uItem << 8) | *i_pStr;
                l_szLength--;
                i_pStr++;
        }
        #else
            for (int l_iI = 0; (l_iI < 2) && (0 < l_szLength); l_iI++)
            {
                l_uItem = (l_uItem << 16) | *i_pStr;
                l_szLength--;
                i_pStr++;
            }
        #endif

            l_uReturn = (l_uReturn ^ l_uItem) * 16777619ul;
        }

        return l_uReturn;
    }


};//CProperty

#endif //PROP_H