/****************************************************************************************/
/*  GROUPS.CPP                                                                          */
/*                                                                                      */
/*  Author:                                                                             */
/*  Description:                                                                        */
/*                                                                                      */
/*  The contents of this file are subject to the Jet3D Public License                   */
/*  Version 1.02 (the "License"); you may not use this file except in                   */
/*  compliance with the License. You may obtain a copy of the License at                */
/*  http://www.jet3d.com                                                                */
/*                                                                                      */
/*  Software distributed under the License is distributed on an "AS IS"                 */
/*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */
/*  the License for the specific language governing rights and limitations              */
/*  under the License.                                                                  */
/*                                                                                      */
/*  The Original Code is Jet3D, released December 12, 1999.                             */
/*  Copyright (C) 1996-1999 Eclipse Entertainment, L.L.C. All Rights Reserved           */
/*                                                                                      */
/****************************************************************************************/

#include "stdafx.h"
#include "jwe.h"
#include "MainFrm.h"
#include "MfcUtil.h"
#include "Resource.h"
#include "Ram.h"
#include "util.h"

#include "Groups.h"
#include "AddModel.h"

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

enum
{
	IMAGE_SELECTED,
	IMAGE_UNSELECTED,
	IMAGE_ACTOR,
	IMAGE_BRUSH,
	IMAGE_ENTITY,
	IMAGE_LAST
} ;

typedef struct tagGroupInfo
{
	CTreeCtrlEx	*	pList ;
	HTREEITEM		hItemGroup ;
} GroupInfo ;

/////////////////////////////////////////////////////////////////////////////
// CGroups dialog


CGroups::CGroups(CWnd* pParent /*=NULL*/)
	: CDialog(CGroups::IDD, pParent)
{
	//{{AFX_DATA_INIT(CGroups)
	m_Lock = FALSE;
	//}}AFX_DATA_INIT
}

CGroups::~CGroups()
{

}

void CGroups::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CGroups)
	DDX_Control(pDX, LIST_TV_ITEMS, m_List);
	DDX_Control(pDX, GRUP_CB_CURRENT, m_CBList);
	DDX_Check(pDX, GROUP_LOCK, m_Lock);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CGroups, CDialog)
	//{{AFX_MSG_MAP(CGroups)
	ON_NOTIFY(TVN_SELCHANGED, LIST_TV_ITEMS, OnSelchangedTvItems)
	ON_BN_CLICKED(IDC_ADDGROUP, OnAddgroup)
	ON_CBN_SELCHANGE(GRUP_CB_CURRENT, OnSelchangeCbCurrent)
	ON_BN_CLICKED(GROUP_LOCK, OnLock)
	ON_WM_SHOWWINDOW()
	ON_WM_DESTROY()
	ON_NOTIFY(TVN_KEYDOWN, LIST_TV_ITEMS, OnKeydownTvItems)
    ON_WM_SIZE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CGroups message handlers

BOOL CGroups::OnInitDialog() 
{
	CBitmap             bitmap;

	CDialog::OnInitDialog();
	
	PositionDialogUnderTabs( this ) ;
/*
	pImageList = new CImageList();
	pImageList->Create(32, 16, ILC_MASK, 6, 4);

		bitmap.LoadBitmap( IDB_ADDTOCURRENTGROUP );
		pImageList->Add(&bitmap, (COLORREF)0xFFFFFF);
		bitmap.DeleteObject();

		bitmap.LoadBitmap( IDB_REMOVEFROMCURRENT );
		pImageList->Add(&bitmap, (COLORREF)0xFFFFFF);
		bitmap.DeleteObject();
*/
	m_ImageList.Create( IDR_IMAGELIST1, 16, 4, RGB(255,0,255) ) ;
	m_List.SetImageList( &m_ImageList, TVSIL_NORMAL    );

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CGroups::OnSize(UINT nType, int cx, int cy) 
{
    CDialog::OnSize(nType, cx, cy);
    CWnd * pWnd = GetDlgItem(LIST_TV_ITEMS);
    if(pWnd)
        pWnd->MoveWindow(4,32,cx-8,cy-36);
    pWnd = GetDlgItem(IDC_ADDGROUP);
    if(pWnd)
        pWnd->MoveWindow(cx-68,4,64,24);
}

void CGroups::SetCurrentDocument(CJweDoc *pDoc)
{
	Group *pGroup;
	int		ComboItem;


	// Doc has changed, lists need complete rebuild
	ASSERT( pDoc != NULL ) ;

	Reset();
	GroupList_EnumGroups( pDoc->GetGroupList(), &m_List, CGroups::GroupListCB ) ;
	GroupList_EnumGroups( pDoc->GetGroupList(), &m_CBList, CGroups::GroupComboCB ) ;
	pGroup = pDoc->GetCurrentGroup();
	if( pGroup != NULL )
	{
		ComboItem = m_CBList.SelectString( 0, Group_GetName(pGroup) );
		UpdateData( true ) ;
		m_Lock= Group_IsLocked( pGroup );
		UpdateData( false );
	}
}// SetCurrentDocument

jeBoolean CGroups::AddObject( Object* pObject )
{
	HTREEITEM hItem;
	HTREEITEM hObjectItem;
	Group *pGroup;
	char  *	pszDisplayName ;
	
	ASSERT( pObject );

	hItem = m_List.GetRootItem();

	while( hItem != NULL )
	{
		pGroup = (Group*)m_List.GetItemData( hItem );
		ASSERT( pGroup != NULL );
		if( Object_GetGroup( pObject ) == pGroup )
		{
			pszDisplayName = Object_GetNameAndTag( pObject ) ;
			if( pszDisplayName == NULL )
				return( JE_FALSE );
			hObjectItem	= m_List.InsertItem( pszDisplayName, hItem, TVI_SORT );
			jeRam_Free( pszDisplayName );
			if( hObjectItem == NULL )
				return( JE_FALSE );
			m_List.SetItemData( hObjectItem, (DWORD)pObject ) ;
			Object_AddRef( pObject );

			return( JE_TRUE );
		}
		hItem = m_List.GetNextSiblingItem(hItem );
	}
	return( JE_FALSE );
}


void CGroups::RenameObject( Object *pObject )
{
	HTREEITEM hItem;
	char * pszDisplayName;
	
	hItem = GetObjectItem( &m_List, pObject );
	if( hItem == NULL )
		return;
	pszDisplayName = Object_GetNameAndTag( pObject ) ;
	if( pszDisplayName != NULL )
	{
		m_List.SetItemText( hItem, pszDisplayName );
		jeRam_Free( pszDisplayName ) ;
	}

}

jeBoolean CGroups::GroupListCB( Group *pGroup, void *lParam)
{
	HTREEITEM			hItem = NULL ;
	CTreeCtrlEx		*	pList = (CTreeCtrlEx*)lParam ;
	const char			*	pszDisplayName ;
	GroupInfo			GroupInfoData;

	pszDisplayName = Group_GetName( pGroup ) ;
	if( pszDisplayName != NULL )
	{
		hItem = pList->InsertItem( pszDisplayName, TVI_ROOT, TVI_SORT ) ;

		if( hItem != NULL )
		{
			GroupInfoData.hItemGroup = hItem;
			GroupInfoData.pList = pList;
			pList->SetItemData( hItem, (DWORD)pGroup ) ;
			if( ObjectList_EnumObjects( Group_GetObjectList( pGroup ), &GroupInfoData, CGroups::ObjectCB ) == JE_FALSE )
				hItem = NULL ;
		}
	}
	return ( hItem == NULL ) ? JE_FALSE : JE_TRUE ;
}// GroupCB

jeBoolean CGroups::GroupComboCB( Group *pGroup, void *lParam)
{
	int					nIndex = CB_ERR ;
	CComboBox			*pCBList = (CComboBox*)lParam ;
	const char			*	pszDisplayName ;
	

	pszDisplayName = Group_GetName( pGroup ) ;
	if( pszDisplayName != NULL )
	{
		pCBList->AddString( pszDisplayName );
		nIndex = pCBList->FindString( 0, pszDisplayName );
		if( nIndex != CB_ERR )
			pCBList->SetItemDataPtr( nIndex, pGroup );

	}
	return ( nIndex != CB_ERR ) ? JE_FALSE : JE_TRUE ;
}// GroupCB

jeBoolean CGroups::ObjectCB( Object *pObject, void *lParam)
{
	HTREEITEM			hItem = NULL ;
	GroupInfo		*	pGroupInfo = (GroupInfo*)lParam ;
	char			*	pszDisplayName ;

	pszDisplayName = Object_GetNameAndTag( pObject ) ;
	if( pszDisplayName != NULL )
	{
		hItem = pGroupInfo->pList->InsertItem( pszDisplayName, pGroupInfo->hItemGroup, TVI_SORT ) ;

		if( hItem != NULL )
		{
			pGroupInfo->pList->SetItemData( hItem, (DWORD)pObject ) ;
			Object_AddRef( pObject );
		}
		jeRam_Free( pszDisplayName ) ;
	}
	return ( hItem == NULL ) ? JE_FALSE : JE_TRUE ;
}// ObjectCB

jeBoolean CGroups::AddSelectionCB(Object *pObject, void *lParam)
{
	HTREEITEM hItem;
	HTREEITEM hObjectItem;
	Group *pGroup;
	char  *	pszDisplayName ;
	CTreeCtrlEx	*pList = (CTreeCtrlEx	*)lParam;
	
	ASSERT( pObject );
	ASSERT( pList );

	hItem = pList->GetRootItem();

	while( hItem != NULL )
	{
		pGroup = (Group*)pList->GetItemData( hItem );
		ASSERT( pGroup != NULL );
		if( Object_GetGroup( pObject ) == pGroup )
		{
			pszDisplayName = Object_GetNameAndTag( pObject ) ;
			if( pszDisplayName == NULL )
				return( JE_FALSE );
			hObjectItem	= pList->InsertItem( pszDisplayName, hItem, TVI_SORT );
			jeRam_Free( pszDisplayName );
			if( hObjectItem == NULL )
				return( JE_FALSE );
			pList->SetItemData( hObjectItem, (DWORD)pObject ) ;
			Object_AddRef( pObject );
			return( JE_TRUE );
		}
		hItem = pList->GetNextSiblingItem(hItem );
	}
	return( JE_FALSE );
}

HTREEITEM CGroups::GetObjectItem( CTreeCtrl *pList, Object *pObject )
{
	HTREEITEM hGroupItem;
	HTREEITEM hObjectItem;
	Object *pListObject;
	Group  *pGroup;
	
	ASSERT( pObject );
	ASSERT( pList );

	hGroupItem = pList->GetRootItem();

	while( hGroupItem != NULL )
	{
		pGroup = (Group*)pList->GetItemData( hGroupItem );
		ASSERT( hGroupItem != NULL );
		if( pGroup == Object_GetGroup( pObject ) )
		{
			hObjectItem = pList->GetChildItem( hGroupItem );
			while( hObjectItem )
			{
				pListObject = (Object*)pList->GetItemData( hObjectItem );
				if( pListObject == pObject )
				{
					return( hObjectItem );
				}
				hObjectItem = pList->GetNextSiblingItem( hObjectItem );
			}
			return( 0 );
		}
		hGroupItem = pList->GetNextSiblingItem(hGroupItem );
	}
	return( 0 );
}


void CGroups::AddSelection(CJweDoc *pDoc)
{

	pDoc->EnumSelected( &m_List, AddSelectionCB ) ;
}// AddSelection

void CGroups::RemoveDeleted( )
{
	HTREEITEM hGroupItem;
	HTREEITEM hObjectItem;
	HTREEITEM hNextItem;
	Object *pListObject;
	
	hGroupItem = m_List.GetRootItem();

	while( hGroupItem != NULL )
	{
		hObjectItem = m_List.GetChildItem( hGroupItem );
		while( hObjectItem )
		{
			pListObject = (Object*)m_List.GetItemData( hObjectItem );
			ASSERT( pListObject );
			hNextItem = m_List.GetNextSiblingItem( hObjectItem );
			if( !Object_IsInLevel( pListObject ) )
			{
				m_List.DeleteItem( hObjectItem );
				Object_Free( &pListObject );
			}
			hObjectItem = hNextItem;
		}
		hGroupItem = m_List.GetNextSiblingItem(hGroupItem );
	}
	return;
}

void CGroups::ChangeGroups( HTREEITEM hItem )
{
	HTREEITEM	hParentItem;
	Group	* pGroup;
	Group	* pOldGroup;
	Object  * pObject;

	hParentItem = m_List.GetParentItem( hItem );
	ASSERT( hParentItem );

	pObject = (Object*)m_List.GetItemData( hItem );
	ASSERT( pObject );

	pGroup = (Group*)m_List.GetItemData( hParentItem );
	ASSERT( pGroup );

	pOldGroup = Object_GetGroup( pObject );
	ASSERT( pOldGroup );

	Group_RemoveObject( pOldGroup, pObject );
	Group_AddObject( pGroup, pObject );
	Object_SetGroup( pObject, pGroup );
}

void CGroups::SelectGroup( HTREEITEM hGroupItem, jeBoolean bSelect )
{
	HTREEITEM		hItem;
	LEVEL_STATE		State;
	Object *		pObject;
	CJweDoc	*		pDoc ;


	pDoc = ((CMainFrame*)AfxGetMainWnd())->GetCurrentDocument();
	if( bSelect ) 
		State = LEVEL_SELECT;
	else
		State = LEVEL_DESELECT;

	hItem = m_List.GetChildItem( hGroupItem );
	while( hItem )
	{
		m_List.SelectItemEx( hItem, bSelect );
		pObject = (Object*)m_List.GetItemData( hItem ) ;
		if( pObject == NULL )
			return;
		pDoc->SelectObject( pObject, State ) ;
		hItem = m_List.GetNextSiblingItem( hItem );
	}
}

void CGroups::OnSelchangedTvItems(NMHDR* pNMHDR, LRESULT* pResult) 
{	
	HTREEITEM		hItem ;
	NM_TREEVIEW*	pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	Object	*		pObject ;
	CJweDoc	*		pDoc ;
	int				State;

	if(  pNMTreeView->action == TVC_DRAG_END )
	{
		ChangeGroups( pNMTreeView->itemNew.hItem );
	}
	if( pNMTreeView->action != TVC_BYCTREECTRL )
		return ;

	pDoc = ((CMainFrame*)AfxGetMainWnd())->GetCurrentDocument();
	if( pDoc == NULL )
		return ;
	
	// I added TVC_BYTREECTRL to try to filter out the duplicate messages
	// I'm still not getting correct multi-sel messages
	
	if( !Util_IsKeyDown( VK_CONTROL ) )
		pDoc->DeselectAll( JE_FALSE );
	hItem = pNMTreeView->itemNew.hItem ;
	if( hItem != NULL )
	{
		pObject = (Object*)m_List.GetItemData( hItem ) ;
		if( pObject == NULL )
			return;
		if( Object_GetKind(pObject) == KIND_GROUP )
		{
			State = m_List.GetItemState( hItem, TVIS_SELECTED );
			if(  State & TVIS_SELECTED )
			{
				SelectGroup( hItem, JE_TRUE );
			}
			else
			{
				SelectGroup( hItem, JE_FALSE );
			}
		}
		else
		{
			pDoc->SelectObject( pObject, LEVEL_TOGGLE ) ;
		}
	}
/*	
	hItem = pNMTreeView->itemOld.hItem ;
	if( hItem != NULL ) 
	{
		pObject = (Object*)m_List.GetItemData( hItem ) ;
		if( pObject != NULL && Object_GetKind(pObject) != KIND_GROUP )
		{
			pDoc->SelectObject( pObject, LEVEL_DESELECT ) ;
		}
	}
*/
	*pResult = 0;
}

jeBoolean CGroups::SelectCB(Object *pObject, void *lParam)
{
	HTREEITEM hObjectItem;
	CTreeCtrlEx	*pList = (CTreeCtrlEx	*)lParam;
	
	ASSERT( pObject );
	ASSERT( pList );

	hObjectItem = GetObjectItem( pList, pObject );
	if( hObjectItem )
	{
		pList->SelectItemEx( hObjectItem );
		return( JE_TRUE );
	}
	return( JE_FALSE );
}
// SELECTION has changed


void CGroups::Update(CJweDoc *pDoc)
{
	LEVEL_SEL	SelType ;
	m_List.SelectItem( NULL );

	SelType = pDoc->GetSelType( ) ;
	if( (SelType & LEVEL_SELTEMPLATE) == 0 )
	{
		pDoc->EnumSelected(&m_List, SelectCB ) ;	
	}
}// Update

void CGroups::Reset()
{
	DeRefAllObjects();
	m_List.DeleteAllItems( );
	m_CBList.ResetContent();
}

void CGroups::OnAddgroup() 
{
	HTREEITEM		hItem ;
	CAddModel		AddModelDialog ;
	Group *			pGroup;
	CJweDoc	*		pDoc ;

	pDoc = ((CMainFrame*)AfxGetMainWnd())->GetCurrentDocument();

	AddModelDialog.m_nTitleID = IDS_NEWGROUP ;
	if( AddModelDialog.DoModal( ) == IDOK )
	{
		TrimString( AddModelDialog.m_csName ) ;
		if( AddModelDialog.m_csName.IsEmpty() )
		{
			AfxMessageBox( IDS_MUSTSUPPLYNAME, MB_OK, 0 ) ;
			return ;		
		}


		pGroup = pDoc->AddGroup( AddModelDialog.m_csName  );
		hItem = m_List.InsertItem( AddModelDialog.m_csName, TVI_ROOT, TVI_SORT ) ;

		if( hItem != NULL )
		{
			m_List.SetItemData( hItem, (DWORD)pGroup ) ;
		}

		GroupComboCB( pGroup, &m_CBList );
		m_CBList.SelectString( 0, Group_GetName( pGroup  ) );
		pDoc->SetCurrentGroup( pGroup );
		UpdateData( true ) ;
		m_Lock= Group_IsLocked( pGroup );
		UpdateData( false );
	}
	
}

void CGroups::OnSelchangeCbCurrent() 
{
	int nIndex;
	Group * pGroup;
	CJweDoc	*		pDoc ;

	pDoc = ((CMainFrame*)AfxGetMainWnd())->GetCurrentDocument();
	nIndex = m_CBList.GetCurSel();
	pGroup = (Group*)m_CBList.GetItemData( nIndex );
	pDoc->SetCurrentGroup( pGroup );	
	UpdateData( true ) ;
	m_Lock= Group_IsLocked( pGroup );
	UpdateData( false );
}

void CGroups::OnLock() 
{
	CJweDoc	*		pDoc ;
	Group * pGroup;

	pDoc = ((CMainFrame*)AfxGetMainWnd())->GetCurrentDocument();
	pGroup = pDoc->GetCurrentGroup();
	UpdateData( true );
	Group_SetLocked( pGroup, m_Lock );
	UpdateData( false );
	
}

void CGroups::OnShowWindow(BOOL bShow, UINT nStatus) 
{
	UpdateData( true );
	UpdateData( false );
	CDialog::OnShowWindow(bShow, nStatus);
	
	// TODO: Add your message handler code here
	
}


void CGroups::DeRefAllObjects()
{
	HTREEITEM hItem;
	HTREEITEM hObjectItem;
	Object *pObject;
	

	hItem = m_List.GetRootItem();

	while( hItem != NULL )
	{
		hObjectItem = m_List.GetChildItem( hItem );
		while( hObjectItem )
		{
			pObject = (Object*)m_List.GetItemData( hObjectItem );
			if( pObject )
				Object_Free( &pObject );
			hObjectItem = m_List.GetNextSiblingItem(hObjectItem );
		}
		hItem = m_List.GetNextSiblingItem(hItem );
	}
}

void CGroups::OnDestroy() 
{
	DeRefAllObjects();
	CDialog::OnDestroy();
		
}


void CGroups::OnKeydownTvItems(NMHDR* pNMHDR, LRESULT* pResult) 
{
	TV_KEYDOWN* pTVKeyDown = (TV_KEYDOWN*)pNMHDR;

	((CMainFrame*)AfxGetMainWnd())->PostMessage( WM_KEYDOWN, pTVKeyDown->wVKey, 0 );

	*pResult = 0;
}
