////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 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.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#include "Common.h"

#define MERGE_ROOT                                                  TM("Merge")
#define MERGE_ROOT_ARRT_TARGET                                        TM("target")
#define MERGE_ROOT_ARRT_CURRENT                                       TM("current")

#define MERGE_ELEMENT                                               TM("Element")
#define MERGE_ELEMENT_PATH                                            TM("Path")
#define MERGE_ADD                                                   TM("Add")
#define MERGE_DEL                                                   TM("Delete")
//#define MERGE_UPD                                                   TM("Update")


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//GetPath
static Bk::IBNode *GetPath(Bk::IBNode *i_iSource, Bk::IBNode *i_iPattern)
{
    if (    (NULL == i_iSource)
         || (NULL == i_iPattern)
       )
    {
        return NULL;
    }

    tBOOL       l_bExit   = FALSE;
    Bk::IBNode *l_iCursor = i_iSource;
    Bk::IBNode *l_iReturn = NULL;

    l_iCursor->Add_Ref();

    while (    (FALSE == l_bExit)
            && (l_iCursor)
          )
    {
        tBOOL   l_bMatch = FALSE;
        tXCHAR *l_pName1 = NULL;
        tXCHAR *l_pName2 = NULL;

        l_iCursor->GetName(&l_pName1);
        i_iPattern->GetName(&l_pName2);

        if (    (l_pName1)
             && (l_pName2)
             && (0 == PStrICmp(l_pName1, l_pName2))
           )
        {
            tXCHAR *l_pAttr = NULL;
            tUINT32 l_dwIdx = 0;

            l_bMatch = TRUE; 

            //checking arguments
            while (Bk::eOk == i_iPattern->GetAttr(l_dwIdx++, (const tXCHAR *&)l_pAttr))
            {
                tXCHAR *l_pAttrVal1 = NULL;
                tXCHAR *l_pAttrVal2 = NULL;
                if (    (Bk::eOk != i_iPattern->GetAttrText(l_pAttr, &l_pAttrVal1))
                     || (Bk::eOk != l_iCursor->GetAttrText(l_pAttr, &l_pAttrVal2))
                     || (0 != PStrICmp(l_pAttrVal1, l_pAttrVal2))
                   )
                {
                    l_bMatch = FALSE;
                    break;
                }
            }
        }
         
        if (l_bMatch) //check childes
        {
            Bk::IBNode *l_iPatternChild = NULL;
            i_iPattern->GetChildFirst(&l_iPatternChild);

            if (l_iPatternChild)
            {
                Bk::IBNode *l_iSourceChild = NULL;

                l_iCursor->GetChildFirst(&l_iSourceChild);
                if (l_iSourceChild)
                {
                    l_iReturn = GetPath(l_iSourceChild, l_iPatternChild);
                    if (!l_iReturn)
                    {
                        l_bMatch = FALSE;
                    }

                    l_iSourceChild->Release();
                }

                l_iPatternChild->Release();
                l_iPatternChild = NULL;
            }
            else//else - matched!
            {
                l_iReturn = l_iCursor;
                l_iReturn->Add_Ref();
            }
        }

        if (l_bMatch)
        {
            l_bExit = TRUE;
        }
        else
        {
            Bk::IBNode *l_iNext = NULL;
            l_iCursor->GetNext(&l_iNext);
            l_iCursor->Release();
            l_iCursor = l_iNext;
        }
    }

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

    return l_iReturn;
}//GetPath


////////////////////////////////////////////////////////////////////////////////
//MergeElements
tBOOL MergeElements(Bk::IBNode *i_iRoot, Bk::IBNode *i_iElement)
{
    tBOOL l_bReturn = TRUE;

    i_iElement->Add_Ref();

    while (i_iElement)
    {
        Bk::IBNode *l_iPath = NULL;
        i_iElement->GetChildFirst(MERGE_ELEMENT_PATH, &l_iPath);
        if (l_iPath)
        {
            Bk::IBNode *l_iPattern = NULL;

            l_iPath->GetChildFirst(&l_iPattern);
            if (l_iPattern)
            {
                Bk::IBNode *l_iTarget = GetPath(i_iRoot, l_iPattern);
                if (l_iTarget)
                {
                    Bk::IBNode *l_iAction = NULL;

                    l_iPath->GetNext(&l_iAction);
                    if (l_iAction)
                    {
                        tXCHAR *l_pActionName = NULL;
                        l_iAction->GetName(&l_pActionName);

                        if (0 == PStrICmp(l_pActionName, MERGE_ADD))
                        {
                            Bk::IBNode *l_iNode = NULL;

                            l_iAction->GetChildFirst(&l_iNode);
                            while (l_iNode)
                            {
                                if (Bk::eOk != l_iTarget->Copy(l_iNode))
                                {
                                    l_bReturn = FALSE;
                                }

                                Bk::IBNode *l_iTemp = NULL;
                                if (l_bReturn)
                                {
                                    l_iNode->GetNext(&l_iTemp);
                                }
                                l_iNode->Release();
                                l_iNode = l_iTemp;
                            }
                        }
                        else if (0 == PStrICmp(l_pActionName, MERGE_DEL))
                        {
                            if (Bk::eOk != l_iTarget->Del())
                            {
                                l_bReturn = FALSE;
                            }
                        }

                        l_iAction->Release();
                    }//if (l_iAction)

                    l_iTarget->Release();
                    l_iTarget = NULL;
                }
                else
                {
                    l_bReturn = FALSE;
                }

                l_iPattern->Release();
            }//if (l_iPath)

            l_iPath->Release();
        }//if (l_iPath)


        Bk::IBNode  *l_iTmp = NULL;
        if (l_bReturn)
        {
            i_iElement->GetNext(MERGE_ELEMENT, &l_iTmp);
        }
        i_iElement->Release();
        i_iElement = l_iTmp;
    }//while (i_iElement)

    return l_bReturn;
}//MergeElements



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Merge
tBOOL Merge(IBDoc *i_iDoc, IBDoc *i_iMergeDoc)
{
    tBOOL        l_bReturn   = TRUE;
    Bk::IBNode  *l_iRoot     = NULL;
    Bk::IBNode  *l_iMerge    = NULL;
    tXCHAR      *l_pVersion  = NULL;

    i_iDoc->GetChildFirst(BNODE_ROOT, &l_iRoot);

    if (!l_iRoot)
    {
        l_bReturn = FALSE;
        goto l_lblExit;
    }

    l_iRoot->GetAttrText(BNODE_ROOT_ATTR_VERSION, &l_pVersion);
    if (!l_pVersion)
    {
        l_pVersion = const_cast<tXCHAR*>(TM("0"));
    }

    i_iMergeDoc->GetChildFirst(MERGE_ROOT, &l_iMerge);
    while (l_iMerge)
    {
        tXCHAR *l_pVerTarget = NULL;
        tXCHAR *l_pVerCur    = NULL;

        l_iMerge->GetAttrText(MERGE_ROOT_ARRT_TARGET,  &l_pVerTarget);
        l_iMerge->GetAttrText(MERGE_ROOT_ARRT_CURRENT, &l_pVerCur);

        if (0 == PStrICmp(l_pVerTarget, l_pVersion))
        {
            Bk::IBNode *l_iElement = NULL;
            l_iMerge->GetChildFirst(MERGE_ELEMENT,  &l_iElement);
            if (l_iElement)
            {
                if (MergeElements(l_iRoot, l_iElement))
                {
                    l_iRoot->SetAttrText(BNODE_ROOT_ATTR_VERSION, l_pVerCur);
                    l_pVersion = l_pVerCur;
                }

                l_iElement->Release();
                l_iElement = NULL;
            }
        }

        Bk::IBNode *l_iMergeN = NULL;
        l_iMerge->GetNext(MERGE_ROOT, &l_iMergeN);
        l_iMerge->Release();
        l_iMerge = l_iMergeN;
    }//while (l_iMerge)

l_lblExit:
    if (l_iRoot)
    {
        l_iRoot->Release();
        l_iRoot = NULL;
    }

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

    return l_bReturn;
}//Merge


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tBOOL Validate(IBDoc *i_iDoc, tINT32 i_iVersion)
{
    tBOOL        l_bReturn  = TRUE;
    Bk::IBNode  *l_iRoot    = NULL;
    tINT32       l_iVersion = NULL;

    i_iDoc->GetChildFirst(BNODE_ROOT, &l_iRoot);

    if (!l_iRoot)
    {
        l_bReturn = FALSE;
        goto l_lblExit;
    }

    if (    (Bk::eOk != l_iRoot->GetAttrInt32(BNODE_ROOT_ATTR_VERSION, &l_iVersion))
         || (l_iVersion < i_iVersion)
       )
    {
        l_bReturn = FALSE;
        goto l_lblExit;
    }

l_lblExit:
    if (l_iRoot)
    {
        l_iRoot->Release();
        l_iRoot = NULL;
    }

    return l_bReturn;
}
