/* Copyright (c) 2002-2012 Croteam Ltd. 
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation


This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */

// DlgLinkTree.cpp : implementation file
//

#include "stdafx.h"
#include "WorldEditor.h"
#include "DlgLinkTree.h"

#ifdef _DEBUG
#undef new
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDlgLinkTree dialog


CDlgLinkTree::CDlgLinkTree(CEntity *pen, CPoint pt, BOOL bWhoTargets, BOOL bPropertyNames,
                           CWnd* pParent /*=NULL*/)
	: CDialog(CDlgLinkTree::IDD, pParent)
{
	//{{AFX_DATA_INIT(CDlgLinkTree)
	m_bClass = FALSE;
	m_bName = FALSE;
	m_bProperty = FALSE;
	m_bWho = FALSE;
	//}}AFX_DATA_INIT
  m_pt=pt;
  m_pen=pen;
  m_bWho=bWhoTargets;
	m_bName=TRUE;
  m_bProperty=bPropertyNames;
  m_bClass=bPropertyNames;
  m_HitItem=NULL;
}


void CDlgLinkTree::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CDlgLinkTree)
	DDX_Control(pDX, IDC_LINK_TREE, m_ctrTree);
	DDX_Check(pDX, IDC_LT_CLASS, m_bClass);
	DDX_Check(pDX, IDC_LT_NAME, m_bName);
	DDX_Check(pDX, IDC_LT_PROPERTY, m_bProperty);
	DDX_Check(pDX, IDC_LT_WHO, m_bWho);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CDlgLinkTree, CDialog)
	//{{AFX_MSG_MAP(CDlgLinkTree)
	ON_NOTIFY(NM_DBLCLK, IDC_LINK_TREE, OnDblclkLinkTree)
	ON_WM_RBUTTONDOWN()
	ON_COMMAND(ID_LT_CONTRACT_ALL, OnLtContractAll)
	ON_COMMAND(ID_LT_CONTRACT_BRANCH, OnLtContractBranch)
	ON_COMMAND(ID_LT_EXPAND_ALL, OnLtExpandAll)
	ON_COMMAND(ID_LT_EXPAND_BRANCH, OnLtExpandBranch)
	ON_COMMAND(ID_LT_LEAVE_BRANCH, OnLtLeaveBranch)
	ON_COMMAND(ID_LT_LAST_LEVEL, OnLtLastLevel)
	ON_BN_CLICKED(IDC_LT_CLASS, OnLtClass)
	ON_BN_CLICKED(IDC_LT_NAME, OnLtName)
	ON_BN_CLICKED(IDC_LT_PROPERTY, OnLtProperty)
	ON_BN_CLICKED(IDC_LT_WHO, OnLtWho)
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDlgLinkTree message handlers

CDynamicContainer<CEntity> _penAdded;
BOOL CDlgLinkTree::OnInitDialog() 
{
  InitializeTree();
	return TRUE;
}

void CDlgLinkTree::InitializeTree(void)
{
  CWorldEditorDoc *pDoc = theApp.GetDocument();
	CDialog::OnInitDialog();
  
  _penAdded.Clear();
  m_ctrTree.DeleteAllItems();
  if( m_pen==NULL || m_pen->IsSelected( ENF_SELECTED))
  {
    FOREACHINDYNAMICCONTAINER(pDoc->m_selEntitySelection, CEntity, iten)
    {
      CEntity &en=*iten;
      AddEntityPtrsRecursiv( &en, 0, "");
    }
  }
  else
  {
    AddEntityPtrsRecursiv( m_pen, 0, "");
  }

  // get dlg wnd - tree ctrl wnd before resizing
  CRect rectdlg, recttree;
  GetClientRect(recttree);
  GetWindowRect(rectdlg);
  PIX dW=rectdlg.Width()-recttree.Width();
  PIX dH=rectdlg.Height()-recttree.Height();
#define PIX_FLAG_LINE PIX(18)
  // get screen size
  int iScrW = ::GetSystemMetrics(SM_CXSCREEN);	// screen size
	int iScrH = ::GetSystemMetrics(SM_CYSCREEN) - 32;

  // expand all nodes
  HTREEITEM pRootItem = m_ctrTree.GetRootItem();
  ExpandTree(pRootItem, TRUE);
  CRect result=CRect(0,0,0,0);
  CalculateOccupiedSpace(pRootItem, result);
  PIX pixIndent=m_ctrTree.GetIndent();
  PIX pixOffset=pixIndent+1;

  // calculate tree wnd size
  result.left-=pixOffset;
  result.bottom+=pixOffset;
  result.right+=pixOffset;
  PIX pixTreeW=Clamp(PIX(result.Width()), PIX(4*32), iScrW-dW);
  PIX pixTreeH=ClampUp(PIX(result.Height()), iScrH-dH-PIX_FLAG_LINE);

  // now move resulting window so LU-corner would be where mouse was clicked
  CRect rectPopup=CRect(m_pt.x, m_pt.y, m_pt.x+pixTreeW+dW, m_pt.y+pixTreeH+dH+PIX_FLAG_LINE);
  if( rectPopup.right>iScrW)
  {
    rectPopup.left=ClampDn(iScrW-rectPopup.Width(), 0);
    rectPopup.right=iScrW;
  }
  if( rectPopup.bottom>iScrH)
  {
    rectPopup.top=ClampDn( iScrH-rectPopup.Height(), 0);
    rectPopup.bottom=iScrH;
  }
  MoveWindow(rectPopup);
  
  CRect newTreePos;
  newTreePos.left=0;
  newTreePos.top=0;
  newTreePos.right=rectPopup.Width();
  newTreePos.bottom=rectPopup.Height()-dH-PIX_FLAG_LINE;
  m_ctrTree.MoveWindow(newTreePos);

#define MOVE_FLAG(id, no)\
  rectFlag.top=newTreePos.bottom;\
  rectFlag.bottom=newTreePos.bottom+PIX_FLAG_LINE;\
  rectFlag.left=no*32;\
  rectFlag.right=(no+1)*32;\
  GetDlgItem(id)->MoveWindow(rectFlag);
  
  CRect rectFlag;
  MOVE_FLAG(IDC_LT_CLASS, 0);
  MOVE_FLAG(IDC_LT_PROPERTY, 1);
  MOVE_FLAG(IDC_LT_NAME, 2);
  MOVE_FLAG(IDC_LT_WHO, 3);
}

void CDlgLinkTree::AddEntityPtrsRecursiv(CEntity *pen, HTREEITEM hParent, CTString strPropertyName)
{
  CWorldEditorDoc *pDoc = theApp.GetDocument();
  if( _penAdded.IsMember( pen)) return;
  // Insert entity's ptrs into directory tree
  HTREEITEM InsertedEntity;
  InsertedEntity = m_ctrTree.InsertItem( 0, L"", 0, 0,
    TVIS_SELECTED, TVIF_STATE, 0, hParent, 0);
  m_ctrTree.SetItemData( InsertedEntity, (ULONG)(pen));
  CTString strText="";
  if( m_bClass)
  {
    CTString strPrev="{";
    CTString strPost="}";
    if( !m_bName && !m_bProperty)
    {
      strPrev="";
      strPost="";
    }
    strText=strPrev+pen->GetClass()->GetName().FileName()+strPost+"    ";
  }
  if( m_bProperty && strPropertyName!="")
  {
    CTString strPrev="<";
    CTString strPost=">";
    if( !m_bName && !m_bClass)
    {
      strPrev="";
      strPost="";
    }
    strText=strText+strPrev+strPropertyName+strPost+"    ";
  }
  if( m_bName)
  {
    strText=strText+pen->GetName();
  }
  m_ctrTree.SetItemText( InsertedEntity, CString(strText));
  _penAdded.Add(pen);
  if(_penAdded.Count()>16) return;

  if( m_bWho)
  {
    FOREACHINDYNAMICCONTAINER(pDoc->m_woWorld.wo_cenEntities, CEntity, iten)
    {
      // ---- Add entities that target
      // obtain entity class ptr
      CDLLEntityClass *pdecDLLClass = iten->GetClass()->ec_pdecDLLClass;
      // for all classes in hierarchy of this entity
      for(;pdecDLLClass!=NULL; pdecDLLClass = pdecDLLClass->dec_pdecBase)
      {
        // for all properties
        for(INDEX iProperty=0; iProperty<pdecDLLClass->dec_ctProperties; iProperty++)
        {
          CEntityProperty *pepProperty = &pdecDLLClass->dec_aepProperties[iProperty];
          if( pepProperty->ep_eptType == CEntityProperty::EPT_ENTITYPTR)
          {
            // obtain property ptr
            CEntity *penPtr = ENTITYPROPERTY( &*iten, pepProperty->ep_slOffset, CEntityPointer);
            if( penPtr == pen)
            {
              AddEntityPtrsRecursiv( &*iten, InsertedEntity, pepProperty->ep_strName);
            }
          }
        }
      }
    }
  }
  else
  {
    // ---- Add this entity's non-NULL ptrs recurively
    // obtain entity class ptr
    CDLLEntityClass *pdecDLLClass = pen->GetClass()->ec_pdecDLLClass;
    // for all classes in hierarchy of this entity
    for(;pdecDLLClass!=NULL; pdecDLLClass = pdecDLLClass->dec_pdecBase)
    {
      // for all properties
      for(INDEX iProperty=0; iProperty<pdecDLLClass->dec_ctProperties; iProperty++)
      {
        CEntityProperty *pepProperty = &pdecDLLClass->dec_aepProperties[iProperty];
        if( pepProperty->ep_eptType == CEntityProperty::EPT_ENTITYPTR)
        {
          // obtain property ptr
          CEntity *penPtr = ENTITYPROPERTY( pen, pepProperty->ep_slOffset, CEntityPointer);
          if( penPtr != NULL)
          {
            AddEntityPtrsRecursiv( penPtr, InsertedEntity, pepProperty->ep_strName);
          }
        }
      }
    }
  }
}
void CDlgLinkTree::CalculateOccupiedSpace(HTREEITEM hItem, CRect &rect)
{
  CRect rectThis;
  m_ctrTree.GetItemRect( hItem, &rectThis, TRUE);
  rect|=rectThis;
  HTREEITEM pNext=m_ctrTree.GetNextItem( hItem, TVGN_NEXTVISIBLE);
  if( pNext!=NULL)
  {
    CalculateOccupiedSpace(pNext, rect);
  }
}

INDEX _iMaxLevel=-1;
void CDlgLinkTree::ExpandTree(HTREEITEM pItem, BOOL bExpand, INDEX iMaxLevel/*=-1*/, BOOL bNoNextSibling/*=FALSE*/)
{
  _iMaxLevel=iMaxLevel;
  ExpandRecursivly(pItem, bExpand, bNoNextSibling);
}

void CDlgLinkTree::ExpandRecursivly(HTREEITEM pItem, BOOL bExpand, BOOL bNoNextSibling)
{
  BOOL bForceContract=FALSE;
  
  if(_iMaxLevel!=-1 && GetItemLevel(pItem)>=_iMaxLevel) bForceContract=TRUE;
  
  // obtain next child item in branch
  HTREEITEM pCurrent=pItem;
  do
  {
    if( m_ctrTree.ItemHasChildren( pCurrent))
    {
      if( bExpand&&!bForceContract)
      {
        m_ctrTree.Expand( pCurrent, TVE_EXPAND);
      }
      else
      {
        m_ctrTree.Expand( pCurrent, TVE_COLLAPSE);
      }
      // get its child
      HTREEITEM pChild=m_ctrTree.GetNextItem( pCurrent, TVGN_CHILD);
      ExpandRecursivly(pChild, bExpand, FALSE);
    }
    // get next in dir
    if( bNoNextSibling) break;
    pCurrent=m_ctrTree.GetNextItem( pCurrent, TVGN_NEXT);
  }
  while( pCurrent!=NULL);
}

void CDlgLinkTree::OnDblclkLinkTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
  HTREEITEM item = m_ctrTree.GetSelectedItem();
  if( item!=NULL)
  {
    CWorldEditorDoc *pDoc = theApp.GetDocument();
    pDoc->m_selEntitySelection.Clear();
    CEntity *pen=(CEntity *) m_ctrTree.GetItemData(item);
    pDoc->m_selEntitySelection.Select( *pen);
    pDoc->m_chSelections.MarkChanged();
    EndDialog( IDOK);
  }
	*pResult = 0;
}

BOOL CDlgLinkTree::PreTranslateMessage(MSG* pMsg) 
{
  if( (pMsg->message==WM_KEYDOWN) && ((int)pMsg->wParam==192))
  {
    EndDialog( IDOK);
  }
  /*
  if( pMsg->message==WM_MOUSEMOVE)
  {
    UINT nKeys = pMsg->wParam;
    PIX xPos = LOWORD(pMsg->lParam);  // horizontal position of cursor 
    PIX yPos = HIWORD(pMsg->lParam);  // vertical position of cursor 
    CPoint point=CPoint(xPos, yPos);
    OnMouseMove(nKeys, point);
    return TRUE;
  }
  */
  if( pMsg->message==WM_LBUTTONDOWN)
  {
    PIX xPos = LOWORD(pMsg->lParam);  // horizontal position of cursor 
    PIX yPos = HIWORD(pMsg->lParam);  // vertical position of cursor 
    CPoint pt=CPoint(xPos, yPos);
    
    GetWindowRect(m_rectWndOnMouseDown);
    m_ptMouseDown=pt;

    UINT nFlags=(int)pMsg->wParam;
    BOOL bShift = nFlags & MK_SHIFT;
    BOOL bCtrl = nFlags & MK_CONTROL;
    if( bShift||bCtrl)
    {
      OnLButtonDown((int)pMsg->wParam, pt);
      return TRUE;
    }
  }
  if( pMsg->message==WM_RBUTTONDOWN)
  {
    PIX xPos = LOWORD(pMsg->lParam);  // horizontal position of cursor 
    PIX yPos = HIWORD(pMsg->lParam);  // vertical position of cursor 
    CPoint pt=CPoint(xPos, yPos);
    OnRButtonDown((int)pMsg->wParam, pt);
    return TRUE;
  }
  
	return CDialog::PreTranslateMessage(pMsg);
}

void CDlgLinkTree::OnLButtonDown(UINT nFlags, CPoint point) 
{
  BOOL bShift = nFlags & MK_SHIFT;
  BOOL bCtrl = nFlags & MK_CONTROL;
	
  TVHITTESTINFO testinfo;
  testinfo.pt=point;
  HTREEITEM item=m_ctrTree.HitTest( &testinfo);
  // allow only string clicks
  if( !(testinfo.flags&TVHT_ONITEMLABEL))
  {
    item=NULL;
  }
  m_HitItem=item;

  if(bCtrl&&bShift)
  {
    OnLtContractAll();
  }
  if( m_HitItem!=NULL)
  {
    if(bCtrl)
    {
      OnLtContractBranch();
    }
    else if(bShift)
    {
      OnLtLeaveBranch();
    }
  }

	CDialog::OnLButtonDown(nFlags, point);
}

void CDlgLinkTree::OnRButtonDown(UINT nFlags, CPoint point) 
{
  BOOL bShift = nFlags & MK_SHIFT;
  BOOL bCtrl = nFlags & MK_CONTROL;

  TVHITTESTINFO testinfo;
  testinfo.pt=point;
  HTREEITEM item=m_ctrTree.HitTest( &testinfo);
  // allow only string clicks
  if( !(testinfo.flags&TVHT_ONITEMLABEL))
  {
    item=NULL;
  }
  m_HitItem=item;

  if(bCtrl&&bShift)
  {
    OnLtExpandAll();
  }
  
  if( m_HitItem!=NULL)
  {
    if(bCtrl&&!bShift)
    {
      OnLtExpandBranch();
    }
    else if(bShift&&!bCtrl)
    {
      OnLtLastLevel();
    }
  }
  if(!bShift&&!bCtrl)
  {
    CMenu menu;
    if( menu.LoadMenu(IDR_LINK_TREE_POPUP))
    {
      CMenu* pPopup = menu.GetSubMenu(0);
      if( item==NULL)
      {
        pPopup->EnableMenuItem(ID_LT_CONTRACT_BRANCH, MF_DISABLED|MF_GRAYED);
        pPopup->EnableMenuItem(ID_LT_EXPAND_BRANCH, MF_DISABLED|MF_GRAYED);
        pPopup->EnableMenuItem(ID_LT_LAST_LEVEL, MF_DISABLED|MF_GRAYED);
        pPopup->EnableMenuItem(ID_LT_LEAVE_BRANCH, MF_DISABLED|MF_GRAYED);
      }
      ClientToScreen(&point);
      pPopup->TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
								   point.x, point.y, this);
    }
  }
	CDialog::OnRButtonDown(nFlags, point);
}

void CDlgLinkTree::OnLtExpandAll() 
{
  // expand all nodes
  HTREEITEM pRootItem = m_ctrTree.GetRootItem();
  ExpandTree(pRootItem, TRUE);
}

void CDlgLinkTree::OnLtContractAll() 
{
  // contract all nodes
  HTREEITEM pRootItem = m_ctrTree.GetRootItem();
  ExpandTree(pRootItem, FALSE);
}

void CDlgLinkTree::OnLtExpandBranch() 
{
  ExpandTree(m_HitItem, TRUE, 10000, TRUE);	
}

void CDlgLinkTree::OnLtContractBranch() 
{
  ExpandTree(m_HitItem, FALSE, 10000, TRUE);	
}

void CDlgLinkTree::OnLtLastLevel() 
{
  OnLtContractAll();
  INDEX iLevel=GetItemLevel(m_HitItem);
  HTREEITEM pRootItem = m_ctrTree.GetRootItem();
  ExpandTree(pRootItem, TRUE, iLevel);
}

void CDlgLinkTree::OnLtLeaveBranch() 
{
  OnLtContractAll();
  OnLtExpandBranch();	
  
  INDEX iLevel=-1;
  HTREEITEM item = m_HitItem;
  while( item!=NULL)
  {
    item=m_ctrTree.GetParentItem( item);
    if( item!=NULL)
    {
      m_ctrTree.Expand( item, TVE_EXPAND);
    }
  }
}

INDEX CDlgLinkTree::GetItemLevel(HTREEITEM item)
{
  INDEX iLevel=-1;
  while( item!=NULL)
  {
    item=m_ctrTree.GetParentItem( item);
    iLevel++;
  }
  return iLevel;
}

void CDlgLinkTree::SetNewWindowOrigin(void)
{
  CRect rectWnd;
  GetWindowRect(rectWnd);
  m_pt.x=rectWnd.left;
  m_pt.y=rectWnd.top;
}

void CDlgLinkTree::OnLtClass() 
{
  m_bClass=!m_bClass;
  UpdateData(FALSE);
  SetNewWindowOrigin();
  InitializeTree();
}

void CDlgLinkTree::OnLtName() 
{
  m_bName=!m_bName;
  UpdateData(FALSE);
  SetNewWindowOrigin();
  InitializeTree();
}

void CDlgLinkTree::OnLtProperty() 
{
  m_bProperty=!m_bProperty;
  UpdateData(FALSE);
  SetNewWindowOrigin();
  InitializeTree();
}

void CDlgLinkTree::OnLtWho() 
{
  m_bWho=!m_bWho;
  UpdateData(FALSE);
  SetNewWindowOrigin();
  InitializeTree();
}


void CDlgLinkTree::OnMouseMove(UINT nFlags, CPoint point) 
{
/*
  BOOL bSpace = (GetKeyState( VK_SPACE)&0x8000) != 0;	
  BOOL bLMB = nFlags & MK_LBUTTON;

  PIX dx=point.x-m_ptMouseDown.x;
  PIX dy=point.y-m_ptMouseDown.y;

  if( bSpace && bLMB)
  {
    CRect rectWnd=m_rectWndOnMouseDown;
    rectWnd.left+=dx;
    rectWnd.right+=dx;
    rectWnd.top+=dy;
    rectWnd.bottom+=dy;
    MoveWindow( rectWnd);
  }

  m_ptLastMouse = point;
*/
	CDialog::OnMouseMove(nFlags, point);
}