//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd  2011
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//  Provided no fee is charged (either by the distributer or user)
//  permission to use, copy, modify, and distribute this software and
//  its documentation for any purpose and without fee is hereby granted,
//  provided that the above copyright notice appear in all copies and
//  that both that copyright notice and this permission notice appear
//  in supporting documentation.
//
//  No waranty or suitablity for purpose is implied.
//
//
//  P2PmsgTreeCtrl implementation.
//  NOTES: All objects within a virtual P2Peer network must derive
//         from P2PeerTarget
//

#include "stdafx.h"
//#include "P2Pexplorer.h"
#include "P2PmsgTreeCtrl.h"
#include "CTreeCtrl_Ext.h"

///////////////////////////////////////////////////////////////////////
//  CTReeCtrl helpers

bool
CTreeCtrl_EverExpanded ( CTreeCtrl *pCTreeCtrl, HTREEITEM hItem )
{
    UINT uiState = pCTreeCtrl->GetItemState ( hItem, TVIF_STATE );
    return (uiState&TVIS_EXPANDEDONCE) ? true : false;
}

void
CTreeCtrl_ClearUserData( CTreeCtrl *pCTreeCtrl, HTREEITEM hItem )
{
    if ( hItem == NULL )
      return;
    pCTreeCtrl -> SetItemData ( hItem, 0 );
    HTREEITEM hItemChild = pCTreeCtrl -> GetChildItem ( hItem );
    while ( hItemChild )
    {
      //pCTreeCtrl -> SetItemData ( hItemChild, 0 );
      CTreeCtrl_ClearUserData ( pCTreeCtrl, hItemChild );
      hItemChild = pCTreeCtrl -> GetNextSiblingItem ( hItemChild );
    }
}

// CP2PmsgTreeCtrl

IMPLEMENT_DYNAMIC(CP2PmsgTreeCtrl, CTreeCtrl)

CP2PmsgTreeCtrl::CP2PmsgTreeCtrl()
{
    m_pP2PmsgMgr_Ext = nullptr;
    m_pP2PmsgMgr     = nullptr;
    m_hRootItem      = 0;
    m_hWndTriggers   = 0;
}

CP2PmsgTreeCtrl::~CP2PmsgTreeCtrl()
{
    if ( m_pP2PmsgMgr != m_pP2PmsgMgr_Ext )
      delete m_pP2PmsgMgr;
    m_pP2PmsgMgr     = 0;
    m_pP2PmsgMgr_Ext = 0;
}

bool
CP2PmsgTreeCtrl::Connect ( P2PmsgMgr *pP2PmsgMgr, P2Pos posRoot
                         , HWND hWndTriggers )
{
    m_hWndTriggers = hWndTriggers;
    if ( pP2PmsgMgr )
    {
      ASSERT(m_pP2PmsgMgr==0);
      m_pP2PmsgMgr     = pP2PmsgMgr;
      ASSERT(m_pP2PmsgMgr_Ext==0);
      m_pP2PmsgMgr_Ext = pP2PmsgMgr;
    }

    // Ensure root visiblity
    if ( m_hRootItem == 0 &&
         m_pP2PmsgMgr        )
    {
      LPCTSTR lpszRootName = m_pP2PmsgMgr->c_name();
	    m_hRootItem = InsertItem ( lpszRootName, TVI_ROOT, 0);
	    SetItemState ( m_hRootItem, TVIS_BOLD, TVIS_BOLD);
      //P2Pos posRoot = m_pP2PmsgMgr->GetP2Pos();
      SetItemData  ( m_hRootItem, posRoot );
      m_oCMapHTREE.SetAt ( posRoot, m_hRootItem );
    }

    // Ensure first child visiblity
    if ( m_hRootItem  &&
           pP2PmsgMgr    )
    {
      P3PmsgNode oNodeRoot = m_pP2PmsgMgr->P2Pos2NodeHdl(posRoot);
LPCTSTR lpszName1=oNodeRoot.c_name();  //TODO debugging only
      P3PmsgCurs oCurs(oNodeRoot);
      for ( int i = 0; oCurs.Goto(i); i++ )
      {
        if ( oCurs.IsNode() )
          InsertP2Pmsg ( m_hRootItem, oCurs.r_node() );
        else if ( oCurs.IsList() )
          ASSERT(0);
        else if ( oCurs.IsField() )
          ASSERT(0);
        else
          ASSERT(0);
      }

      // Create root trigger
      if ( m_hWndTriggers )
        m_pP2PmsgMgr -> CreateTrigger ( P2PmsgTrig_ALL
                , oNodeRoot, m_hWndTriggers, (WPARAM)m_hRootItem );
    }

    // Tidy up, and
    return true;
}

void
CP2PmsgTreeCtrl::RecyleThisObject ( )
{
    if ( m_hWnd )
      DeleteAllItems ( );
    m_hRootItem = 0;
    if ( m_pP2PmsgMgr != m_pP2PmsgMgr_Ext )
      delete m_pP2PmsgMgr;
    m_pP2PmsgMgr     = nullptr;
    m_pP2PmsgMgr_Ext = nullptr;
}

///////////////////////////////////////////////////////////////////////
//  Notifications
//  NOTES: Asynchronously reflected back to parent

bool
CP2PmsgTreeCtrl::NotifyP2Pmsg_AddNode ( CWnd *pCWndNotify
                                      , const P3PmsgNode& oNodeParent
                                      , const P3PmsgNode& oNodeChild )
{
    // Locals
    P2Pos posParent = oNodeParent.GetP2Pos ( );
    HTREEITEM hItemParent = 0;
    m_oCMapHTREE.Lookup ( posParent, hItemParent );

    // Parent has no form
    // NOTES: Populated upon parent expansion
    if ( hItemParent == NULL )
      return false;

    // GrandParent has never been expanded
    // NOTES: Populated upon grand parent expansion
    HTREEITEM hItemGParent = GetParentItem ( hItemParent );
    //if (  hItemGParent                              &&
    //     !CTreeCtrl_EverExpanded(this,hItemGParent)    )
    //  return false;

    // Establish visiblity parent node
    // NOTES:
    InsertP2Pmsg ( hItemParent, oNodeChild );
    ASSERT(0);
    pCWndNotify -> PostMessage ( WM_P2Pmsg_AddNode
                               , oNodeParent.GetP2Pos()
                               , oNodeChild.GetP2Pos() );
    return true;
}

bool
CP2PmsgTreeCtrl::NotifyP2Pmsg_MergeNode ( CWnd *pCWndNotify
                                        , const P3PmsgNode& oNode )
{
    // Visiblity
    HTREEITEM   hItem;
    P2Pos     posNode = oNode.GetP2Pos();
    if ( !m_oCMapHTREE.Lookup(posNode,hItem) ||
            hItem == 0                          )
      return false;

    // Tidy up, and
    ASSERT(0);
    pCWndNotify -> PostMessage ( WM_P2Pmsg_MergeNode
                               , oNode.GetP2Pos(), 0 );
    return true;
}

///////////////////////////////////////////////////////////////////////
//  Operations

//
//  Inserts new item into the CTreeCtrl
//
//  Parameters:  HTREEITEM hItemParent
//
//               const P3PmsgNode& oNode
//
//  Returns:     HTREEITEM
//               Handle for inserted item
HTREEITEM
CP2PmsgTreeCtrl::InsertP2Pmsg ( HTREEITEM hItemParent, const P3PmsgNode& oNode )
{
    //LPCTSTR  lpszNodename  = oNode.c_name();
	  //HTREEITEM   hItem      = InsertItem ( lpszNodename
    //                                    , I_IMAGECALLBACK | I_CHILDRENCALLBACK
    //                                    , 1, hItemParent );
    //P2Pos     posP2Pobject = oNode.GetP2Pos ( );
    //ASSERT(posP2Pobject);
    //SetItemData ( hItem, posP2Pobject );
    //m_oCMapHTREE.SetAt ( posP2Pobject, hItem );
//-----------------------------------------------------------------------------------
    P2Pos  posP2Pobject = oNode.GetP2Pos ( );
    TVINSERTSTRUCT oTVS = {0};
    oTVS.item.mask      = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_CHILDREN|TVIF_PARAM;
    oTVS.item.pszText   = (LPSTR)oNode.c_name();         //LPSTR_TEXTCALLBACK;
	  oTVS.item.iImage    = I_IMAGECALLBACK;
	  oTVS.item.iSelectedImage = I_IMAGECALLBACK;
    oTVS.item.cChildren = I_CHILDRENCALLBACK;
    oTVS.item.lParam    = posP2Pobject;
    oTVS.hParent        = hItemParent;
    oTVS.hInsertAfter   = TVI_LAST;
    HTREEITEM hItem = InsertItem ( &oTVS ) ;
    ASSERT(hItem);
    m_oCMapHTREE.SetAt ( posP2Pobject, hItem );
//-----------------------------------------------------------------------------------
    // Set icon details
    // NOTES: Derived class specialises GetImage() and GetSelectedImage()
    //        according to requirements.  Default implementation provide
    //        indices of 0 and 1 respectively
    //SetItemImage ( hItem, GetImage(posP2Pobject), GetSelectedImage(posP2Pobject) );

    // Create triggers
    if ( m_hWndTriggers )
      m_pP2PmsgMgr -> CreateTrigger ( P2PmsgTrig_ALL, (P3PmsgNode&)oNode
                                    , m_hWndTriggers, (WPARAM)hItem );
    return hItem;
}

HTREEITEM
CP2PmsgTreeCtrl::InsertP2Pmsg ( HTREEITEM hItemParent, const P3PmsgField& oField )
{
    LPCTSTR  lpszFieldname = oField.c_name();
	  HTREEITEM   hItem      = InsertItem ( lpszFieldname, 1, 1, hItemParent);
    ASSERT(oField.GetP2Pos());
    SetItemData ( hItem, oField.GetP2Pos() );
    m_oCMapHTREE.SetAt ( oField.GetP2Pos(), hItem );

    // Create triggers
    if ( m_hWndTriggers )
      m_pP2PmsgMgr -> CreateTrigger ( P2PmsgTrig_ALL, (P3PmsgField&)oField
                                    , m_hWndTriggers, (WPARAM)hItem );
    return hItem;
}

HTREEITEM
CP2PmsgTreeCtrl::UpdateP2Pmsg ( HTREEITEM hItem, const P3PmsgNode& oNode )
{
    LPCTSTR  lpszNodename = oNode.c_name();
    SetItemText ( hItem, lpszNodename );
    return hItem;
}

///////////////////////////////////////////////////////////////////////
//  CTreeCtrl extensions

LPARAM
CP2PmsgTreeCtrl::MoveItemCB ( CTreeCtrl *pCTreeCtrl
                    , HTREEITEM hItemSource, HTREEITEM hItemDestin )
{
    // Locals
    CP2PmsgTreeCtrl *pThis = dynamic_cast<CP2PmsgTreeCtrl*>(pCTreeCtrl);
    ASSERT(pThis);
    P2PmsgMgr       *pP2PmsgMgr = &pThis -> GetP2PmsgMgr();
    ASSERT(pP2PmsgMgr);

    // Source trigger configuration
    P2Pos posSource  = pThis -> GetItemData ( hItemSource );
                       pThis -> SetItemData ( hItemSource, 0 );
    DWORD dwTrigMask = pP2PmsgMgr -> SelectTrigger ( posSource, pCTreeCtrl->m_hWnd );
    pP2PmsgMgr -> DropTrigger ( posSource, pCTreeCtrl->m_hWnd );

    // Destin trigger configuration
    if ( dwTrigMask )
      pP2PmsgMgr -> CreateTrigger ( dwTrigMask, posSource, pCTreeCtrl->m_hWnd
                                  , (WPARAM)hItemDestin );
    pThis -> SetItemData ( hItemDestin, posSource );
    ASSERT(posSource);
    return posSource;
}

void
CP2PmsgTreeCtrl::ShuffleItemUp ( HTREEITEM hItem )
{
    // Isolate selected P2PeerHub
    // NOTES: Skip root item
    //HTREEITEM hItem = m_wndFileView.GetSelectedItem ( );
    if ( hItem == NULL )
      return;
    HTREEITEM hItemParent = GetParentItem ( hItem );
    if ( hItemParent == NULL )
      return;
    P2Pos posItem = GetItemData ( hItem );
    if ( posItem == NULL )
      return;
    HTREEITEM hItemPrev = GetPrevSiblingItem ( hItem );
    if ( hItemPrev == NULL )
      return;
    HTREEITEM hItemAfter = GetPrevSiblingItem ( hItemPrev );
    if ( hItemAfter == NULL )
      hItemAfter = TVI_FIRST;
    P2Pos posItemPrev = GetItemData ( hItemPrev );
    if ( posItemPrev == NULL )
      return;

    // Isolate tree clearance
    try
    {
      //P2PmsgMgr *pP2PmsgMgr = theApp.GetP2PmsgMgr();
      P3PmsgNode oNodeItem  = m_pP2PmsgMgr->P2Pos2NodeHdl ( posItem );
      P3PmsgNode oNodePrev  = m_pP2PmsgMgr->P2Pos2NodeHdl ( posItemPrev );
      // Clear Triggers

      // Move P2Pmsg item up
      P2PmsgNode_Swap ( oNodeItem, oNodePrev );

      // Move visual tree item up
      CTreeCtrl_MoveItem ( this, hItem, hItemParent, hItemAfter
                         , CP2PmsgTreeCtrl::MoveItemCB );
      //m_wndFileView.MoveItemUp ( hItem );

      // Re-establish triggers
    }

    // Exceptions
    catch_pP2Pevent_Cancel
    catch_pCException_Cancel
    catch_ALL_Cancel
}

void
CP2PmsgTreeCtrl::ShuffleItemDown ( HTREEITEM hItem )
{
}

///////////////////////////////////////////////////////////////////////
//  Properties

HTREEITEM
CP2PmsgTreeCtrl::IsItem ( P2Pos posItem )
{
    // Map dependancy
    HTREEITEM hItem = 0;
    if ( !m_oCMapHTREE.Lookup(posItem,hItem) ||
            hItem == 0                          )
      return 0;
    return hItem;
}

P2PmsgMgr&
CP2PmsgTreeCtrl::GetP2PmsgMgr ( )
{
    return *m_pP2PmsgMgr;
}

P3PmsgNode
CP2PmsgTreeCtrl::GetItemNode ( HTREEITEM hItem )
{
    P3PmsgNode oNode;
    // Isolate passed item
    if ( hItem == NULL )
      return oNode;
    P2Pos posItem = GetItemData ( hItem );
    if ( posItem == NULL )
      return oNode;
    return m_pP2PmsgMgr->P2Pos2Node ( posItem );
}

P3PmsgAttr
CP2PmsgTreeCtrl::GetItemAttr ( HTREEITEM hItem )
{
    P3PmsgAttr oAttr;
    // Isolate passed item
    if ( hItem == NULL )
      return oAttr;
    P2Pos posItem = GetItemData ( hItem );
    if ( posItem == NULL )
      return oAttr;
    return m_pP2PmsgMgr->P2Pos2Attr ( posItem );
}

//
//  Fetch normal CImageList index for passed P2Pobject address
//  NOTES: Specialise GetImage() and GetSelectedImage() according
//         to requirements
//
//  Returns:     int
//               Default CImageList index
//int
//CP2PmsgTreeCtrl::GetImage ( P2Pos posP2Pobject )
//{
//    posP2Pobject;
//    return 0;
//}
//int
//CP2PmsgTreeCtrl::GetSelectedImage ( P2Pos posP2Pobject )
//{
//    posP2Pobject;
//    return 1;
//}

///////////////////////////////////////////////////////////////////////
//  MFC Virtual overrides

BOOL
CP2PmsgTreeCtrl::OnNotify ( WPARAM wParam, LPARAM lParam, LRESULT *pResult )
{
	  BOOL bRes = CTreeCtrl::OnNotify(wParam, lParam, pResult);

	  NMHDR* pNMHDR = (NMHDR*)lParam;
	  ASSERT(pNMHDR != NULL);

	  if ( pNMHDR                    && 
         pNMHDR->code  == TTN_SHOW &&
         GetToolTips() != NULL)
		  GetToolTips()->SetWindowPos(&wndTop, -1, -1, -1, -1, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSIZE);

	return bRes;
}

//  MFC Message handlers
BEGIN_MESSAGE_MAP(CP2PmsgTreeCtrl, CTreeCtrl)
    ON_MESSAGE(WM_P2Pmsg_TrigINSERT,OnP2PmsgTrigINSERT)
    ON_MESSAGE(WM_P2Pmsg_TrigUPDATE,OnP2PmsgTrigUPDATE)
    ON_MESSAGE(WM_P2Pmsg_TrigDELETE,OnP2PmsgTrigDELETE)
    ON_NOTIFY_REFLECT ( TVN_DELETEITEM, &CP2PmsgTreeCtrl::OnTvnDeleteItem )
    ON_NOTIFY_REFLECT_EX ( TVN_GETDISPINFO, &CP2PmsgTreeCtrl::OnTvnGetDispInfo )
    ON_NOTIFY_REFLECT_EX ( TVN_ITEMEXPANDING, &CP2PmsgTreeCtrl::OnTvnItemExpanding)
    ON_NOTIFY_REFLECT(NM_DBLCLK, &CP2PmsgTreeCtrl::OnNMDblclk)
END_MESSAGE_MAP()

LRESULT
CP2PmsgTreeCtrl::OnP2PmsgTrigINSERT ( WPARAM wParam, LPARAM lParam )
{
    HTREEITEM hItem   = (HTREEITEM)wParam;
    P2Pos     posNode = (P2Pos)lParam;
    ASSERT(posNode);ASSERT(hItem);
    ASSERT(0);

    // Tidy up and
    return 0;
}
LRESULT
CP2PmsgTreeCtrl::OnP2PmsgTrigUPDATE ( WPARAM wParam, LPARAM lParam )
{
    HTREEITEM hItem = (HTREEITEM)wParam;
    ASSERT(0);

    // Tidy up and
    return 0;
}
LRESULT
CP2PmsgTreeCtrl::OnP2PmsgTrigDELETE ( WPARAM wParam, LPARAM lParam )
{
    HTREEITEM hItem = (HTREEITEM)wParam;
    ASSERT(0);

    // Tidy up and
    return 0;
}

void
CP2PmsgTreeCtrl::OnTvnDeleteItem ( NMHDR *pNMHDR, LRESULT *pResult )
{
    // Locals
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);

    // House keeping
    P2Pos posP2Pobject = pNMTreeView -> itemOld.lParam;
    m_oCMapHTREE.RemoveKey ( posP2Pobject );
    if ( posP2Pobject &&
         m_pP2PmsgMgr    )
      m_pP2PmsgMgr -> DropTrigger ( posP2Pobject, m_hWnd );

    // Tidy up, and
   *pResult = 0;
}

//
//  TVN_GETDISPINFO notification
//  NOTES: Handled prior to equivalent parent notification
//
BOOL
CP2PmsgTreeCtrl::OnTvnGetDispInfo ( NMHDR *pNMHDR, LRESULT *pResult )
{
    // Locals
    LPNMTVDISPINFO   pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
    TVITEM          *pTVITEM     = &pTVDispInfo -> item;
    HTREEITEM        hItem       =  pTVDispInfo -> item.hItem;
//    int              cChildren   = 0;

    P3PmsgNode oNode = GetItemNode(hItem).r_Object();
    if ( !oNode.r_Object() )
      return TRUE;

    // Children
    // NOTES: Item MUST be created and inserted with equivalent mask
    //      : Assigned value may be modified in derived class
    if ( (pTVITEM->mask&TVIF_CHILDREN) == TVIF_CHILDREN )
      pTVITEM -> cChildren = oNode.GetCount();

   *pResult = TRUE;
    return FALSE;                      // Flag parent notification
}
//
//  TVN_ITEMEXPANDING notification
//  NOTES: Handled BEFORE equivalent parent notification
//
BOOL
CP2PmsgTreeCtrl::OnTvnItemExpanding ( NMHDR *pNMHDR, LRESULT *pResult )
{
    // Locals
    LPNMTREEVIEW  pNMTreeView =  reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    TVITEM       *pTVITEM     = &pNMTreeView -> itemNew;
    P2Pos       posParent     =  pTVITEM -> lParam;
    HTREEITEM     hItemParent =  pTVITEM -> hItem;

    // To be sure, to be sure
   *pResult = 0;
    if ( posParent == NULL )
      return FALSE;
//static int iTimes=0;
//if(++iTimes%2==0)
//return;
//CTreeCtrl
    // Confirm children fully populated
    HTREEITEM hItemLoop = GetChildItem ( hItemParent );
hItemLoop=0; //TODO: Resolve masking out loop
    while ( hItemLoop )
    {
      HTREEITEM hItemChild = hItemLoop;
      hItemLoop = GetNextSiblingItem ( hItemLoop );
      if ( ItemHasChildren(hItemChild) )
        continue;
      P2Pos posChild = GetItemData ( hItemChild );
      if ( posChild == NULL )
        continue;

      if ( !m_pP2PmsgMgr->IsNode(posChild) )
        continue;

//problem lies here
// TODO: Displaced by logic below 23/09/2013 vvvvv
//      P3PmsgNode oNodeParent = m_pP2PmsgMgr -> P2Pos2NodeHdl ( posChild );
//      P3PmsgCurs oCurs ( oNodeParent );
//      for ( int i = 0; oCurs.Goto(i); i++ )
//      {
//        if ( oCurs.IsNode() )
//          InsertP2Pmsg ( hItemParent, oCurs.r_node() );
//        else if ( oCurs.IsList() )
//          ASSERT(0);
//        else if ( oCurs.IsField() )
//          InsertP2Pmsg ( hItemChild, oCurs.r_field() );
//        else
//          ASSERT(0);
//      }
// TODO: Displace by logic below 23/09/2013 ^^^^
    }

    // TODO: Added 23/09/2013 vvvv
    // Confirm all P2PmsgNode children exist in the CTreeCtrl
    P3PmsgNode oNodeParent = m_pP2PmsgMgr -> P2Pos2NodeHdl ( posParent );
    P3PmsgCurs oCurs ( oNodeParent );
    for ( int i = 0; oCurs.Goto(i); i++ )
    {
      if ( IsItem(oCurs.r_Object().GetP2Pos()) )
        continue;                      // Already exists in tree
      if ( oCurs.IsNode() )
        InsertP2Pmsg ( hItemParent, oCurs.r_node() );
      else if ( oCurs.IsList() )
        ASSERT(0);
      else if ( oCurs.IsField() )
        InsertP2Pmsg ( hItemParent, oCurs.r_field() );
      else
        ASSERT(0);
    }
    // TODO: Added 23/09/2013 ^^^^

    //if ( pNMTreeView->itemOld.lParam )
   *pResult = TRUE;
    return FALSE;                      // Flag parent notification
}

void
CP2PmsgTreeCtrl::OnNMDblclk ( NMHDR *pNMHDR, LRESULT *pResult )
{
  // TODO: Add your control notification handler code here
  *pResult = 0;
}
