//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#include "stdafx.h"
#include "hammer.h"
#include "FilterControl.h"
#include "ControlBarIDs.h"
#include "MapWorld.h"
#include "GlobalFunctions.h"
#include "EditGroups.h"
#include "CustomMessages.h"
#include "VisGroup.h"
#include "Selection.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>


typedef struct
{
	CVisGroup *pGroup;
	CMapDoc *pDoc;
	SelectMode_t eSelectMode;
} MARKMEMBERSINFO;


static const unsigned int g_uToggleStateMsg = ::RegisterWindowMessage(GROUPLIST_MSG_TOGGLE_STATE);
static const unsigned int g_uLeftDragDropMsg = ::RegisterWindowMessage(GROUPLIST_MSG_LEFT_DRAG_DROP);
static const unsigned int g_uRightDragDropMsg = ::RegisterWindowMessage(GROUPLIST_MSG_RIGHT_DRAG_DROP);


BEGIN_MESSAGE_MAP(CFilterControl, CHammerBar)
	//{{AFX_MSG_MAP(CFilterControl)
	ON_BN_CLICKED(IDC_EDITGROUPS, OnEditGroups)
	ON_BN_CLICKED(IDC_MARKMEMBERS, OnMarkMembers)
	ON_BN_CLICKED(IDC_SHOW_ALL, OnShowAllGroups)
	ON_COMMAND_EX(IDC_VISGROUP_MOVEUP, OnMoveUpDown)
	ON_COMMAND_EX(IDC_VISGROUP_MOVEDOWN, OnMoveUpDown)
	ON_UPDATE_COMMAND_UI(IDC_GROUPS, UpdateControlGroups)
	ON_UPDATE_COMMAND_UI(IDC_EDITGROUPS, UpdateControl)
	ON_UPDATE_COMMAND_UI(IDC_MARKMEMBERS, UpdateControl)
	ON_UPDATE_COMMAND_UI(IDC_SHOW_ALL, UpdateControl)
	ON_UPDATE_COMMAND_UI(IDC_VISGROUP_MOVEUP, UpdateControl)
	ON_UPDATE_COMMAND_UI(IDC_VISGROUP_MOVEDOWN, UpdateControl)		
	ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnSelChangeTab)
	ON_WM_ACTIVATE()
	ON_WM_SHOWWINDOW()
	ON_WM_SIZE()
	ON_WM_WINDOWPOSCHANGED()
	ON_REGISTERED_MESSAGE(g_uToggleStateMsg, OnListToggleState)
	ON_REGISTERED_MESSAGE(g_uLeftDragDropMsg, OnListLeftDragDrop)
	ON_REGISTERED_MESSAGE(g_uRightDragDropMsg, OnListRightDragDrop)	
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pParentWnd - 
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL CFilterControl::Create(CWnd *pParentWnd)
{
	if (!CHammerBar::Create(pParentWnd, IDD_FILTERCONTROL, CBRS_RIGHT | CBRS_SIZE_DYNAMIC, IDCB_FILTERCONTROL, "Filter Control"))
		
	{
		return FALSE;
	}

	m_cGroupBox.SubclassDlgItem(IDC_GROUPS, this);	
	m_cGroupBox.EnableChecks();

	m_cTabControl.SubclassDlgItem(IDC_TAB1, this);
	m_cTabControl.InsertItem(0, "User");
	m_cTabControl.InsertItem(1, "Auto");

	//
	// Set up button icons.
	//
	CWinApp *pApp = AfxGetApp();
	HICON hIcon = pApp->LoadIcon(IDI_MOVE_UP);
	((CButton *)GetDlgItem(IDC_VISGROUP_MOVEUP))->SetIcon(hIcon);

	hIcon = pApp->LoadIcon(IDI_MOVE_DOWN);
	((CButton *)GetDlgItem(IDC_VISGROUP_MOVEDOWN))->SetIcon(hIcon);

    AddControl( IDC_GROUPS, GROUP_BOX );
	AddControl( IDC_VISGROUP_MOVEUP, BOTTOM_JUSTIFY );
	AddControl( IDC_VISGROUP_MOVEDOWN, BOTTOM_JUSTIFY );
	AddControl( IDC_SHOW_ALL, BOTTOM_JUSTIFY );
	AddControl( IDC_EDITGROUPS, BOTTOM_JUSTIFY );
	AddControl( IDC_MARKMEMBERS, BOTTOM_JUSTIFY );
	AddControl( IDC_TAB1, GROUP_BOX );

	// Add all the VisGroups to the list.
	UpdateGroupList();

	bInitialized = TRUE;
	m_bShowingAuto = FALSE;

	return TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : nLength - 
//			dwMode - 
// Output : CSize
//-----------------------------------------------------------------------------
CSize CFilterControl::CalcDynamicLayout(int nLength, DWORD dwMode)
{
	// TODO: make larger / resizable when floating
	//if (IsFloating())
	//{
	//	return CSize(200, 300);
	//}

	return CHammerBar::CalcDynamicLayout(nLength, dwMode);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : nType - 
//			cx - 
//			cy - 
//-----------------------------------------------------------------------------
void CFilterControl::OnSize(UINT nType, int cx, int cy)
{
	// TODO: make larger / resizable when floating
	//if (IsFloating())
	//{
	//	CWnd *pwnd = GetDlgItem(IDC_GROUPS);
	//	if (pwnd && IsWindow(pwnd->GetSafeHwnd()))
	//	{
	//		pwnd->MoveWindow(2, 10, cx - 2, cy - 2, TRUE);
	//	}
	//}

	CHammerBar::OnSize(nType, cx, cy);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CFilterControl::UpdateGroupList(void)
{
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if (pDoc == NULL)
	{
		m_cGroupBox.DeleteAllItems();
		return;
	}

	m_cGroupBox.SaveVisGroupExpandStates();

	CVisGroup *pVisGroup = m_cGroupBox.GetSelectedVisGroup();

	m_cGroupBox.SetRedraw(false);
	m_cGroupBox.DeleteAllItems();

	int nCount = pDoc->VisGroups_GetRootCount();

	for (int i = 0; i < nCount; i++)
	{
		CVisGroup *pGroup = pDoc->VisGroups_GetRootVisGroup(i);
		int compareValue = strcmp( pGroup->GetName(), "Auto" );
		if ( (compareValue == 0 && m_bShowingAuto) ||(compareValue != 0 && !m_bShowingAuto) )
		{
			m_cGroupBox.AddVisGroup(pGroup);
		}
	}

	UpdateGroupListChecks();		

	if (pVisGroup)
	{
		m_cGroupBox.EnsureVisible(pVisGroup);
		m_cGroupBox.SelectVisGroup(pVisGroup);
	}

	m_cGroupBox.RestoreVisGroupExpandStates();
	m_cGroupBox.SetRedraw(true);
	m_cGroupBox.Invalidate();
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pCmdUI - 
//-----------------------------------------------------------------------------
void CFilterControl::UpdateControl(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(GetActiveWorld() ? TRUE : FALSE);
}


//-----------------------------------------------------------------------------
// Purpose: Disables the group list when there's no active world or when the
//			visgroups are currently overridden by the "Show All" button.
//-----------------------------------------------------------------------------
void CFilterControl::UpdateControlGroups(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((GetActiveWorld() != NULL) && !CVisGroup::IsShowAllActive());
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pTarget - 
//			bDisableIfNoHndler - 
//-----------------------------------------------------------------------------
void CFilterControl::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
{
	UpdateDialogControls(pTarget, FALSE);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CFilterControl::OnShowAllGroups(void)
{
	CButton *pButton = (CButton *)GetDlgItem(IDC_SHOW_ALL);
	if (pButton != NULL)
	{
		UINT uCheck = pButton->GetCheck();
		CVisGroup::ShowAllVisGroups(uCheck == 1);

		CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
		pDoc->UpdateVisibilityAll();

		UpdateGroupListChecks();
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
BOOL CFilterControl::OnMoveUpDown(UINT uCmd)
{
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if (!pDoc)
	{
		return TRUE;
	}

	CVisGroup *pVisGroup = m_cGroupBox.GetSelectedVisGroup();
	if (pVisGroup == NULL)
	{
		return TRUE;
	}

	if (uCmd == IDC_VISGROUP_MOVEUP)
	{
		pDoc->VisGroups_MoveUp(pVisGroup);
	}
	else
	{
		pDoc->VisGroups_MoveDown(pVisGroup);
	}

	UpdateGroupList();

	m_cGroupBox.EnsureVisible(pVisGroup);
	m_cGroupBox.SelectVisGroup(pVisGroup);

	pDoc->SetModifiedFlag();

	return TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CFilterControl::OnEditGroups(void)
{
	CEditGroups dlg;
	dlg.DoModal();

	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if (pDoc != NULL)
	{
		pDoc->SetModifiedFlag();
	}

	UpdateGroupList();
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pObject - 
//			pInfo - 
// Output : Returns TRUE to continue enumerating, FALSE to stop.
//-----------------------------------------------------------------------------
static BOOL MarkMembersOfGroup(CMapClass *pObject, MARKMEMBERSINFO *pInfo)
{
	if (pObject->IsInVisGroup(pInfo->pGroup))
	{
		if (!pObject->IsVisible())
		{
			return TRUE;
		}

		CMapClass *pSelectObject = pObject->PrepareSelection(pInfo->eSelectMode);
		if (pSelectObject)
		{
			pInfo->pDoc->SelectObject(pSelectObject, scSelect);
		}
	}

	return TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: Selects all objects that belong to the currently selected visgroup.
//-----------------------------------------------------------------------------
void CFilterControl::OnMarkMembers(void)
{
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if (pDoc)
	{
		CVisGroup *pVisGroup = m_cGroupBox.GetSelectedVisGroup();
		if (pVisGroup)
		{
			pDoc->GetSelection()->SetMode(selectObjects);

			// Clear the selection.
			pDoc->SelectObject(NULL, scClear|scSaveChanges);

			//
			// Select all objects that belong to the visgroup.
			//
			CMapWorld *pWorld = pDoc->GetMapWorld();
			EnumChildrenPos_t pos;
			CMapClass *pChild = pWorld->GetFirstDescendent(pos);
			while (pChild)
			{
				if (pChild->IsInVisGroup(pVisGroup))
				{
					if (pChild->IsVisible())
					{
						CMapClass *pSelectObject = pChild->PrepareSelection(pDoc->GetSelection()->GetMode());
						if (pSelectObject)
						{
							pDoc->SelectObject(pSelectObject, scSelect);
						}
					}
				}
				
				pChild = pWorld->GetNextDescendent(pos);
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pPos - 
//-----------------------------------------------------------------------------
void CFilterControl::OnWindowPosChanged(WINDOWPOS *pPos)
{
	if (bInitialized && pPos->flags & SWP_SHOWWINDOW)
	{
		UpdateGroupList();
	}

	CHammerBar::OnWindowPosChanged(pPos);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : bShow - 
//			nStatus - 
//-----------------------------------------------------------------------------
void CFilterControl::OnShowWindow(BOOL bShow, UINT nStatus)
{
	if (bShow)
	{
		UpdateGroupList();
	}

	CHammerBar::OnShowWindow(bShow, nStatus);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : nState - 
//			pWnd - 
//			bMinimized - 
//-----------------------------------------------------------------------------
void CFilterControl::OnActivate(UINT nState, CWnd* pWnd, BOOL bMinimized)
{
	if (nState == WA_ACTIVE)
	{
		UpdateGroupList();
	}

	CHammerBar::OnActivate(nState, pWnd, bMinimized);
}


//-----------------------------------------------------------------------------
// Purpose: Called when the visibility of a group is toggled in the groups list.
// Input  : wParam - Index of item in the groups list that was toggled.
//			lParam - 0 to hide, 1 to show.
// Output : Returns zero.
//-----------------------------------------------------------------------------
LRESULT CFilterControl::OnListToggleState(WPARAM wParam, LPARAM lParam)
{
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if (pDoc != NULL)
	{
		//
		// Update the visibility of the group.
		//
		CVisGroup *pVisGroup = (CVisGroup *)wParam;
		if (pVisGroup != NULL)
		{
			pDoc->VisGroups_ShowVisGroup(pVisGroup, pVisGroup->GetVisible() == VISGROUP_HIDDEN);
		}

		UpdateGroupListChecks();
	}

	return 0;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : wParam - 
//			lParam - 
// Output : LRESULT
//-----------------------------------------------------------------------------
LRESULT CFilterControl::OnListLeftDragDrop(WPARAM wParam, LPARAM lParam)
{
	if ( m_bShowingAuto == TRUE )
	{
		UpdateGroupList();
		return 0;
	}

	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if (pDoc != NULL)
	{
		CVisGroup *pDragGroup = (CVisGroup *)wParam;
		CVisGroup *pDropGroup = (CVisGroup *)lParam;

		if (pDropGroup != NULL)
		{
			if (pDragGroup->FindDescendent(pDropGroup))
			{
				CString str;
				str.Format("Cannot combine the groups because '%s' is a sub-group of '%s'.", pDropGroup->GetName(), pDragGroup->GetName());
				AfxMessageBox(str);
				UpdateGroupList();
				return 0;
			}

			CString str;
			str.Format("Combine group '%s' into group '%s'?", pDragGroup->GetName(), pDropGroup->GetName());
			if (AfxMessageBox(str, MB_YESNO | MB_ICONQUESTION) == IDNO)
			{
				UpdateGroupList();
				return 0;
			}

			pDoc->VisGroups_CombineGroups(pDragGroup, pDropGroup);
		}
		else
		{
			CString str;
			str.Format("Delete group '%s'?", pDragGroup->GetName());
			if (AfxMessageBox(str, MB_YESNO | MB_ICONQUESTION) == IDNO)
			{
				UpdateGroupList();
				return 0;
			}

			// Show the visgroup that's being deleted so that member objects are shown.
			pDoc->VisGroups_CheckMemberVisibility(pDragGroup);
			pDoc->VisGroups_RemoveGroup(pDragGroup);
		}
	}

		UpdateGroupList();

	return 0;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : wParam - 
//			lParam - 
// Output : LRESULT
//-----------------------------------------------------------------------------
LRESULT CFilterControl::OnListRightDragDrop(WPARAM wParam, LPARAM lParam)
{
	if ( m_bShowingAuto == TRUE )
	{
		UpdateGroupList();
		return 0;
	}

	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if (pDoc != NULL)
	{
		CVisGroup *pDragGroup = (CVisGroup *)wParam;
		CVisGroup *pDropGroup = (CVisGroup *)lParam;

		if (pDragGroup->FindDescendent(pDropGroup))
		{
			CString str;
			str.Format("Cannot move the group because '%s' is a sub-group of '%s'.", pDropGroup->GetName(), pDragGroup->GetName());
			AfxMessageBox(str);
			return 0;
		}

		pDoc->VisGroups_SetParent(pDragGroup, pDropGroup);
		UpdateGroupList();
	}

	return 0;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : wParam - 
//			lParam - 
//			pResult - 
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL CFilterControl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult)
{
	NMHDR *pnmh = (NMHDR *)lParam;

	if (pnmh->idFrom == IDC_GROUPS)
	{
		switch (pnmh->code)
		{
			case TVN_SELCHANGED:
			{
				CVisGroup *pGroup = m_cGroupBox.GetSelectedVisGroup();
				CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
				if (pGroup && pDoc)
				{
					bool bCanMoveUp = pDoc->VisGroups_CanMoveUp(pGroup);
					bool bCanMoveDown = pDoc->VisGroups_CanMoveDown(pGroup);
					GetDlgItem(IDC_VISGROUP_MOVEUP)->EnableWindow(bCanMoveUp);
					GetDlgItem(IDC_VISGROUP_MOVEDOWN)->EnableWindow(bCanMoveDown);
				}

				return(TRUE);
			}
		}
	}

	return(CWnd::OnNotify(wParam, lParam, pResult));
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
BOOL CFilterControl::OnInitDialog(void)
{
	return TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CFilterControl::UpdateGroupListChecks(void)
{
	int nCount = m_cGroupBox.GetVisGroupCount();
	for (int i = 0; i < nCount; i++)
	{
		CVisGroup *pVisGroup = m_cGroupBox.GetVisGroup(i);
		if (pVisGroup->GetVisible() == VISGROUP_HIDDEN)
		{
			m_cGroupBox.SetCheck(pVisGroup, 0);
		}
		else if (pVisGroup->GetVisible() == VISGROUP_SHOWN)
		{
			m_cGroupBox.SetCheck(pVisGroup, 1);
		}
		else
		{
			m_cGroupBox.SetCheck(pVisGroup, -1);
		}
	}
}

void CFilterControl::OnSelChangeTab(NMHDR *header, LRESULT *result) 
{
	if ( m_cTabControl.GetCurSel() == 0 ) 
	{
		m_bShowingAuto = FALSE;				
	} 
	else 
	{
		m_bShowingAuto = TRUE;		
	}
	UpdateGroupList();	
}