//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd  2013
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//  P2Peer message definitions and prototypes
//
#include "StdAfx.h"
#include "P2Pmsg.h"
#include "P2Pmsg_Ext.h"
#include "P2Pevent.h"

///////////////////////////////////////////////////////////////////////
//  Recursive P2Pmsg[Field,Node,Attr]_Merge Utilities and helpers
//  NOTES: For clarity and simplicity operations isolated in series
//         of self contained static functions

P3PmsgField&
P2PmsgField_Merge ( P3PmsgField& oField, const P3PmsgField& rhs )
{
    // Field assignment
    (P3PmsgData&)oField = rhs;

    // Attribute assignment
    if ( !((P3PmsgField&)rhs).r_Attr().IsEmpty() )
      P2PmsgAttr_Merge ( oField.r_Attr(P3PmsgField::AttrCMD_Create)
                       , ((P3PmsgField&)rhs).r_Attr() );

    // Tidy up, and
    return oField;
}

P3PmsgNode&
P2PmsgNode_Merge ( P3PmsgNode& oNode, const P3PmsgNode& rhs )
{
    // Recursive copy
    P3PmsgCurs& oCurs = ((P3PmsgNode&)rhs).r_Curs();
    for ( int i = 0; oCurs.Goto(i); i++ )
    {
//oCurs.AssertValid();
ASSERT(oCurs.Item()==i);
      if ( oCurs.IsNode() )
      {
        LPCTSTR lpszNodename = oCurs.r_node().c_name();
        if ( oNode.Exists(lpszNodename) )
          P2PmsgNode_Merge ( oNode.SelectNode(lpszNodename), oCurs.r_node() );
        else
          oNode += oCurs.r_node();
      }
      else if ( oCurs.IsList() )
      {
        ASSERT(0);
        oNode += oCurs.r_list();
      }
      else if ( oCurs.IsVect() )
      {
        ASSERT(0);
        oNode += oCurs.r_vect();
      }
      else if ( oCurs.IsField() )
      {
        LPCTSTR lpszFieldname = oCurs.r_field().c_name();
        if ( oNode.Exists(lpszFieldname) )
          P2PmsgField_Merge ( oNode.SelectItem(lpszFieldname), oCurs.r_field() );
        else
          oNode += oCurs.r_field();
      }
      else
        ASSERT(0);
//oNode.AssertValid();//TODO:LJM delete, testing
    }

    // Tidy up, and
    return oNode;
}

P3PmsgAttr&
P2PmsgAttr_Merge ( P3PmsgAttr& oAttr, const P3PmsgNode& rhs )
{
    // Recursive copy
    P3PmsgCurs& oCurs = ((P3PmsgNode&)rhs).r_Curs();
    for ( int i = 0; oCurs.Goto(i); i++ )
    {
//oCurs.AssertValid();
ASSERT(oCurs.Item()==i);
      if ( oCurs.IsNode() )
      {
        LPCTSTR lpszNodename = oCurs.r_node().c_name();
//oAttr.AssertValid();
        if ( oAttr.Exists(lpszNodename) )
          P2PmsgNode_Merge ( oAttr.SelectNode(lpszNodename), oCurs.r_node() );
        else
          oAttr += oCurs.r_node();
      }
      else if ( oCurs.IsList() )
      {
        ASSERT(0);
        oAttr += oCurs.r_list();
      }
      else if ( oCurs.IsVect() )
      {
        ASSERT(0);
        oAttr += oCurs.r_vect();
      }
      else if ( oCurs.IsField() )
      {
        LPCTSTR lpszFieldname = oCurs.r_field().c_name();
        if ( oAttr.Exists(lpszFieldname) )
          P2PmsgField_Merge ( oAttr.SelectItem(lpszFieldname), oCurs.r_field() );
        else
          oAttr += oCurs.r_field();
      }
      else
        ASSERT(0);
//oAttr.AssertValid();//TODO:LJM delete, testing
    }

    // Tidy up, and
    return oAttr;
}

P3PmsgAttr&
P2PmsgAttr_Merge ( P3PmsgAttr& oAttr, const P3PmsgAttr& rhs )
{
    // Recursive copy
    P3PmsgCurs& oCurs = ((P3PmsgAttr&)rhs).r_Curs();
    for ( int i = 0; oCurs.Goto(i); i++ )
    {
oCurs.AssertValid();
ASSERT(oCurs.Item()==i);
      if ( oCurs.IsNode() )
      {
        LPCTSTR lpszNodename = oCurs.r_node().c_name();
        if ( oAttr.Exists(lpszNodename) )
          P2PmsgNode_Merge ( oAttr.SelectNode(lpszNodename), oCurs.r_node() );
        else
          oAttr += oCurs.r_node();
      }
      else if ( oCurs.IsList() )
      {
        ASSERT(0);
        oAttr += oCurs.r_list();
      }
      else if ( oCurs.IsVect() )
      {
        ASSERT(0);
        oAttr += oCurs.r_vect();
      }
      else if ( oCurs.IsField() )
      {
        LPCTSTR lpszFieldname = oCurs.r_field().c_name();
        if ( oAttr.Exists(lpszFieldname) )
          P2PmsgField_Merge ( oAttr.SelectItem(lpszFieldname), oCurs.r_field() );
        else
          oAttr += oCurs.r_field();
      }
      else
        ASSERT(0);
//oAttr.AssertValid();//TODO:LJM delete, testing
    }

    // Tidy up, and
    return oAttr;
}

///////////////////////////////////////////////////////////////////////
//  P2Pmsg serialisation helpers
//  NOTES: Perform standard activities

P3PmsgNode&
P3PmsgField_SERIALISE ( P3PmsgNode& oNode
                      , LPCTSTR lpszFieldname, const P3PmsgData& oData
                      , bool bDscAttr, LPCTSTR lpszDescription )
{
    P3PmsgField oField ( lpszFieldname, oData );
    if ( bDscAttr )
      oField.r_Attr(P3PmsgField::AttrCMD_Create)
        += P3PmsgField ( _T("Dsc"), lpszDescription );
    oNode += oField;
    return oNode;
}

P3PmsgAttr&
P2PmsgAttr_SERIALISE  ( P3PmsgAttr& oAttr, bool bOverwrite
                      , LPCTSTR lpszFieldname, const P3PmsgData& oData
                      , bool bDscAttr, LPCTSTR lpszDescription )
{
    P3PmsgField oField ( lpszFieldname, oData );
    if ( bDscAttr )
      oField.r_Attr(P3PmsgField::AttrCMD_Create)
        += P3PmsgField ( _T("Dsc"), lpszDescription );
    if ( !oAttr.Exists(lpszFieldname) )
      oAttr += oField;
    else if ( bOverwrite )
      oAttr.SelectItem(lpszFieldname) = oField;
    else
      EVERR->MODULE
           ->AFP(lpszFieldname)
           ->Message(_T("Field already exists, no overwrite permission") )
           ->Throw();
    return oAttr;
}

P3PmsgField&
Decorate4Grid ( P3PmsgField& oField, UCHAR ucAttributes
              , LPCTSTR lpszGridLabel, LPCTSTR lpszGridDescription )
{
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Lab"), lpszGridLabel ) );
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Dsc"), lpszGridDescription ) );
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Typ"), _T("") ) );
    return oField;
}

P3PmsgField&
Decorate4Grid_FONT ( P3PmsgField& oField, UCHAR ucAttributes
                   , LPCTSTR lpszGridLabel, LPCTSTR lpszGridDescription )
{
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Lab"), lpszGridLabel ) );
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Dsc"), lpszGridDescription ) );
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Typ"), _T("FONT") ) );
    return oField;
}

P3PmsgField&
Decorate4Grid_COLOR ( P3PmsgField& oField, UCHAR ucAttributes
                    , LPCTSTR lpszGridLabel, LPCTSTR lpszGridDescription )
{
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Lab"), lpszGridLabel ) );
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Dsc"), lpszGridDescription ) );
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Typ"), _T("COLOR") ) );
    return oField;
}

P3PmsgField&
Decorate4Grid_FILE ( P3PmsgField& oField, UCHAR ucAttributes
                   , LPCTSTR lpszGridLabel, LPCTSTR lpszGridDescription )
{
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Lab"), lpszGridLabel ) );
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Dsc"), lpszGridDescription ) );
    oField.r_Attr().PushBack ( P3PmsgField ( _T("#Typ"), _T("FILE") ) );
    return oField;
}

///////////////////////////////////////////////////////////////////////
//  P3PmsgObject extractions


P2PmsgKERNEL_EXT P3PmsgObject
P3Pmsg_FindParentWithAttr ( const P3PmsgObject& oObject, LPCTSTR lpszAttributeName )
{
    P3PmsgObject oParentObject = oObject.GetParent();
    if ( oParentObject.IsAttr() )
      oParentObject = oObject.GetParent( );
    if ( !oParentObject )
      return oParentObject;
    if ( oParentObject.IsNode() )
    {
      P3PmsgNode oNodeParent = oParentObject;
      if ( oNodeParent.r_Attr().Exists(lpszAttributeName) )
        return oParentObject;
    }
    else if ( oParentObject.IsField() )
    {
      P3PmsgField oFieldParent = oParentObject;
      if ( oFieldParent.r_Attr().Exists(lpszAttributeName) )
        return oParentObject;
    }
    else if ( oParentObject.IsList() )
    {
      ASSERT(0);
      //P3PmsgList oListParent = oParentObject;
      //if ( oListParent.r_Attr().Exists(lpszAttributeName) )
      //  return oParentObject;
    }
    else if ( oParentObject.IsVect() )
    {
      ASSERT(0);
      //P3PmsgVect oVectParent = oParentObject;
      //if ( oVectParent.r_Attr().Exists(lpszAttributeName) )
      //  return oParentObject;
    }
    return P3Pmsg_FindParentWithAttr ( oParentObject, lpszAttributeName );
}

P2PmsgKERNEL_EXT P3PmsgObject
P3Pmsg_FindChildWithAttr ( const P3PmsgNode& oNode
                         , LPCTSTR lpszAttributeName, LPCTSTR lpszChildname )
{
    // Search all immediate children
    P3PmsgCurs oCurs((P3PmsgNode&)oNode);
    for ( int i = 0; oCurs.Goto(i); i++ )
    {
      if ( !oCurs.r_name().c_stricmp(lpszChildname)  &&
            oCurs.r_attr().Exists(lpszAttributeName)    )
        return oCurs.r_Object();
    }

    // Search all immediate grandchildren
    P3PmsgObject oObject;
    for ( int i = 0; oCurs.Goto(i) && !oObject; i++ )
    {
      if ( oCurs.IsNode() )
        oObject = P3Pmsg_FindChildWithAttr ( oCurs.r_node(), lpszAttributeName, lpszChildname );
    }
    return oObject;
}

///////////////////////////////////////////////////////////////////////
//  Boolean operations

P2PmsgKERNEL_EXT P3PmsgNode&
P2PmsgNode_AND ( P3PmsgNode& oNode, const P3PmsgNode& rhs )
{
    // Immediate P3PmsgData AND operation
    if ( oNode.r_data().DataType() == rhs.r_data().DataType() )
      oNode.r_data() = rhs.r_data();

    // Immediate P3PmsgAttr AND operation
    if ( !oNode.IsAttributed() || !rhs.IsAttributed() )
      P2PmsgAttr_AND ( oNode.r_Attr(), rhs.r_Attr() );

    // Recursive AND operation
    P3PmsgCurs& oCurs = ((P3PmsgNode&)rhs).r_Curs();
    for ( int i = 0; oCurs.Goto(i); i++ )
    {
//oCurs.AssertValid();
ASSERT(oCurs.Item()==i);
      if ( oCurs.IsNode() )
      {
        P3PmsgNode&   oNodeRValue = oCurs.r_node();
        LPCTSTR    lpszNodename   = oNodeRValue.c_name();
        if ( !oNode.Exists(lpszNodename) )
          continue;
        P3PmsgObject oObjectLValue = oNode.SelectObject(lpszNodename);
        if ( !oObjectLValue.IsNode() )
          continue;
        P3PmsgNode oNodeLValue ( oObjectLValue );
        P2PmsgNode_AND ( oNodeLValue, oNodeRValue );
      }
      else if ( oCurs.IsList() )
      {
        ASSERT(0);
      }
      else if ( oCurs.IsVect() )
      {
        ASSERT(0);
      }
      else if ( oCurs.IsField() )
      {
        LPCTSTR lpszFieldname = oCurs.r_field().c_name();
        if ( oNode.Exists(lpszFieldname) )
          continue;
        P2PmsgField_AND ( oNode.SelectItem(lpszFieldname), oCurs.r_field() );
      }
      else
        ASSERT(0);
//oNode.AssertValid();//TODO:LJM delete, testing
    }

    // Tidy up, and
    return oNode;
}

P2PmsgKERNEL_EXT P3PmsgField&
P2PmsgField_AND ( P3PmsgField& oField, const P3PmsgField& rhs )
{
    // Immediate P3PmsgData AND operation
    if ( oField.r_data().DataType() == rhs.r_data().DataType() )
      oField.r_data() = rhs.r_data();

    // Immediate P3PmsgAttr AND operation
    if ( !oField.IsAttributed() || !rhs.IsAttributed() )
      P2PmsgAttr_AND ( oField.r_Attr(), rhs.r_Attr() );

    // Tidy up, and
    return oField;
}

P2PmsgKERNEL_EXT P3PmsgAttr&
P2PmsgAttr_AND ( P3PmsgAttr& oAttr, const P3PmsgAttr& rhs )
{
    // Recursive copy
    P3PmsgCurs& oCurs = ((P3PmsgAttr&)rhs).r_Curs();
    for ( int i = 0; oCurs.Goto(i); i++ )
    {
oCurs.AssertValid();
ASSERT(oCurs.Item()==i);
      if ( oCurs.IsNode() )
      {
        P3PmsgNode&   oNodeRValue = oCurs.r_node();
        LPCTSTR    lpszNodename   = oNodeRValue.c_name();
        if ( !oAttr.Exists(lpszNodename) )
          continue;
        P3PmsgObject oObjectLValue = oAttr.SelectObject(lpszNodename);
        if ( !oObjectLValue.IsNode() )
          continue;
        P3PmsgNode oNodeLValue ( oObjectLValue );
        P2PmsgNode_AND ( oNodeLValue, oNodeRValue );
      }
      else if ( oCurs.IsList() )
      {
        ASSERT(0);
        oAttr += oCurs.r_list();
      }
      else if ( oCurs.IsVect() )
      {
        ASSERT(0);
        oAttr += oCurs.r_vect();
      }
      else if ( oCurs.IsField() )
      {
        LPCTSTR lpszFieldname = oCurs.r_field().c_name();
        if ( oAttr.Exists(lpszFieldname) )
          continue;
        P2PmsgField_AND ( oAttr.SelectItem(lpszFieldname), oCurs.r_field() );
      }
      else
        ASSERT(0);
//oAttr.AssertValid();//TODO:LJM delete, testing
    }

    // Tidy up, and
    return oAttr;
}

CString bytetohex(byte item)
{
CString result;
result.Format(_T("%02.2x"),item);
return result.Right(2);
}

void
GuidToString ( GUID& oGUID, LPTSTR lpszGUID )
{
    byte *pbyGUID = (byte *)&oGUID;

    lpszGUID [0] = _T('0') +  pbyGUID [3] / 16;
    lpszGUID [1] = _T('0') +  pbyGUID [3] % 16;
    lpszGUID [2] = _T('0') +  pbyGUID [2] / 16;
    lpszGUID [3] = _T('0') +  pbyGUID [2] % 16;
    lpszGUID [4] = _T('0') +  pbyGUID [1] / 16;
    lpszGUID [5] = _T('0') +  pbyGUID [1] % 16;
    lpszGUID [6] = _T('0') +  pbyGUID [0] / 16;
    lpszGUID [7] = _T('0') +  pbyGUID [0] % 16;
    lpszGUID [8] = _T('-');

    lpszGUID [9] = _T('0') +  pbyGUID [5] / 16;
    lpszGUID[10] = _T('0') +  pbyGUID [5] % 16;
    lpszGUID[11] = _T('0') +  pbyGUID [4] / 16;
    lpszGUID[12] = _T('0') +  pbyGUID [4] % 16;
    lpszGUID[13] = _T('-');

    lpszGUID[14] = _T('0') +  pbyGUID [7] / 16;
    lpszGUID[15] = _T('0') +  pbyGUID [7] % 16;
    lpszGUID[16] = _T('0') +  pbyGUID [6] / 16;
    lpszGUID[17] = _T('0') +  pbyGUID [6] % 16;
    lpszGUID[18] = _T('-');

    lpszGUID[19] = _T('0') +  pbyGUID [8] / 16;
    lpszGUID[20] = _T('0') +  pbyGUID [8] % 16;
    lpszGUID[21] = _T('0') +  pbyGUID [9] / 16;
    lpszGUID[22] = _T('0') +  pbyGUID [9] % 16;
    lpszGUID[23] = _T('-');

    lpszGUID[24] = _T('0') +  pbyGUID[10] / 16;
    lpszGUID[25] = _T('0') +  pbyGUID[10] % 16;
    lpszGUID[26] = _T('0') +  pbyGUID[11] / 16;
    lpszGUID[27] = _T('0') +  pbyGUID[11] % 16;
    lpszGUID[28] = _T('0') +  pbyGUID[12] / 16;
    lpszGUID[29] = _T('0') +  pbyGUID[12] % 16;
    lpszGUID[30] = _T('0') +  pbyGUID[13] / 16;
    lpszGUID[31] = _T('0') +  pbyGUID[13] % 16;
    lpszGUID[32] = _T('0') +  pbyGUID[14] / 16;
    lpszGUID[33] = _T('0') +  pbyGUID[14] % 16;
    lpszGUID[34] = _T('0') +  pbyGUID[15] / 16;
    lpszGUID[35] = _T('0') +  pbyGUID[15] % 16;
    lpszGUID[36] =     0;
}

