//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Tool used for picking brush faces.
//
//			Left click - single select face
//				+Ctrl - multiselect a single face
//				+Shift - all faces on the brush
//
// $NoKeywords: $
//=============================================================================//

#include "stdafx.h"
#include "MapFace.h"
#include "resource.h"
#include "ToolPickFace.h"
#include "MapSolid.h"
#include "MapView3D.h"
#include "mapdoc.h"

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


//-----------------------------------------------------------------------------
// Purpose: Constructor. Inits data members.
//-----------------------------------------------------------------------------
CToolPickFace::CToolPickFace(void)
{
	m_pNotifyTarget = NULL;
	m_bAllowMultiSelect = false;
}


//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CToolPickFace::~CToolPickFace(void)
{
}


//-----------------------------------------------------------------------------
// Purpose: Enables or disables multiselect for this tool.
//-----------------------------------------------------------------------------
void CToolPickFace::AllowMultiSelect(bool bAllow)
{
	m_bAllowMultiSelect = bAllow;

	//
	// Shouldn't ever happen, but you never know.
	//
	if ((!bAllow) && (m_Faces.Count() > 1))
	{
		CMapFace *pFace = m_Faces[0].pFace;
		DeselectAll();
		SelectFace(pFace);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Called when the tool is deactivated.
// Input  : eNewTool - The ID of the tool that is being activated.
//-----------------------------------------------------------------------------
void CToolPickFace::OnDeactivate()
{
	DeselectAll();
}


//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button up message in the 3D view.
// Input  : pView - The view that the event occurred in.
//			nFlags - Flags per the Windows mouse message.
//			point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickFace::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button up message in the 3D view.
// Input  : pView - The view that the event occurred in.
//			nFlags - Flags per the Windows mouse message.
//			point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickFace::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
	bool bControl = ((nFlags & MK_CONTROL) != 0);
	bool bShift = ((nFlags & MK_SHIFT) != 0);

	if (!m_bAllowMultiSelect)
	{
		// Ignore shift click for single selection mode.
		bShift = false;
	}

	unsigned long uFace;
	CMapClass *pObject = pView->NearestObjectAt( vPoint, uFace);
	if (pObject != NULL)
	{
		CMapSolid *pSolid = dynamic_cast <CMapSolid *>(pObject);
		if (pSolid != NULL)
		{
			//
			// We clicked on a solid.
			//
			if (!bShift)
			{
				//
				// Get the face that we clicked on.
				//
				CMapFace *pFace = pSolid->GetFace(uFace);
				Assert(pFace != NULL);

				if (pFace != NULL)
				{
					if ((!m_bAllowMultiSelect) || (!bControl))
					{
						// Single select.
						DeselectAll();
						SelectFace(pFace);
					}
					else
					{
						// Multiselect.
						CycleSelectFace(pFace);
					}
				}
			}
			else
			{
				//
				// Holding down shift. Select or deselect all the faces on the solid.
				// Only deselect if all the faces in the solid were selected.
				//
				bool bAllSelected = true;
				int nFaceCount = pSolid->GetFaceCount();
				for (int nFace = 0; nFace < nFaceCount; nFace++)
				{
					CMapFace *pFace = pSolid->GetFace(nFace);
					int nIndex = FindFace(pFace);
					if ((nIndex == -1) || (m_Faces[nIndex].eState != FaceState_Select))
					{
						bAllSelected = false;
						break;
					}
				}

				if (!bControl)
				{
					DeselectAll();
				}

				nFaceCount = pSolid->GetFaceCount();
				for (int nFace = 0; nFace < nFaceCount; nFace++)
				{
					CMapFace *pFace = pSolid->GetFace(nFace);
					if (bAllSelected)
					{
						DeselectFace(pFace);
					}
					else
					{
						SelectFace(pFace);
					}
				}
			}

			if (m_pNotifyTarget)
			{
				m_pNotifyTarget->OnNotifyPickFace(this);
			}
		}
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button double click message in the 3D view.
// Input  : pView - The view that the event occurred in.
//			nFlags - Flags per the Windows mouse message.
//			point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickFace::OnLMouseDblClk3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Handles the right mouse button up message in the 3D view.
// Input  : pView - The view that the event occurred in.
//			nFlags - Flags per the Windows mouse message.
//			point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickFace::OnRMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Handles the mouse button up message in the 3D view.
// Input  : pView - The view that the event occurred in.
//			nFlags - Flags per the Windows mouse message.
//			point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickFace::OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Handles the mouse move message in the 3D view.
// Input  : pView - The view that the event occurred in.
//			nFlags - Flags per the Windows mouse message.
//			point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickFace::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
	SetEyedropperCursor();
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Sets the cursor to the eyedropper cursor.
//-----------------------------------------------------------------------------
void CToolPickFace::SetEyedropperCursor(void)
{
	static HCURSOR hcurEyedropper = NULL;
	
	if (!hcurEyedropper)
	{
		hcurEyedropper = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_EYEDROPPER));
	}
	
	SetCursor(hcurEyedropper);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pFace - 
//-----------------------------------------------------------------------------
void CToolPickFace::CycleSelectFace(CMapFace *pFace)
{
	int nIndex = FindFace(pFace);
	if (nIndex != -1)
	{
		//
		// The face is in our list, update its selection state.
		//
		if (m_Faces[nIndex].eState == FaceState_Partial)
		{
			DeselectFace(nIndex);
		}
		else if (m_Faces[nIndex].eState == FaceState_Select)
		{
			if (m_Faces[nIndex].eOriginalState == FaceState_Partial)
			{
				m_Faces[nIndex].eState = FaceState_Partial;
				pFace->SetSelectionState(SELECT_MULTI_PARTIAL);
			}
			else
			{
				DeselectFace(nIndex);
			}
		}
		else if (m_Faces[nIndex].eState == FaceState_None)
		{
			m_Faces[nIndex].eState = FaceState_Select;
			pFace->SetSelectionState(SELECT_NORMAL);
		}
	}
	else
	{
		//
		// The face is not in our list, add it.
		//
		AddToList(pFace, FaceState_Select);
		pFace->SetSelectionState(SELECT_NORMAL);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Sets the fully selected and partially selected faces for the picker.
// Input  : FaceListFull - 
//			FaceListPartial - 
//-----------------------------------------------------------------------------
void CToolPickFace::SetSelectedFaces(CMapFaceList &FaceListFull, CMapFaceList &FaceListPartial)
{
	m_Faces.RemoveAll();

	for (int i = 0; i < FaceListFull.Count(); i++)
	{
		CMapFace *pFace = FaceListFull.Element(i);

		AddToList(pFace, FaceState_Select);
		pFace->SetSelectionState(SELECT_NORMAL);
	}

	for (int i = 0; i < FaceListPartial.Count(); i++)
	{
		CMapFace *pFace = FaceListPartial.Element(i);

		AddToList(pFace, FaceState_Partial);
		pFace->SetSelectionState(SELECT_MULTI_PARTIAL);
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pFace - 
//-----------------------------------------------------------------------------
int CToolPickFace::FindFace(CMapFace *pFace)
{
	for (int i = 0; i < m_Faces.Count(); i++)
	{
		if (m_Faces[i].pFace == pFace)
		{
			return i;
		}
	}

	return -1;
}


//-----------------------------------------------------------------------------
// Purpose: Clears the selection set.
//-----------------------------------------------------------------------------
void CToolPickFace::DeselectAll(void)
{
	for (int i = 0; i < m_Faces.Count(); i++)
	{
		m_Faces[i].pFace->SetSelectionState(SELECT_NONE);
	}

	m_Faces.RemoveAll();

	m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_ONLY_3D );
}


//-----------------------------------------------------------------------------
// Purpose: Selects the face unconditionally.
// Input  : pFace - face to select.
//-----------------------------------------------------------------------------
void CToolPickFace::SelectFace(CMapFace *pFace)
{
	int nIndex = FindFace(pFace);
	if (nIndex != -1)
	{
		//
		// The face is in our list, update its selection state.
		//
		m_Faces[nIndex].eState = FaceState_Select;
		pFace->SetSelectionState(SELECT_NORMAL);
	}
	else
	{
		//
		// The face is not in our list, add it.
		//
		AddToList(pFace, FaceState_Select);
		pFace->SetSelectionState(SELECT_NORMAL);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Deselects the given face.
//-----------------------------------------------------------------------------
void CToolPickFace::DeselectFace(CMapFace *pFace)
{
	int nIndex = FindFace(pFace);
	if (nIndex != -1)
	{
		DeselectFace(nIndex);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Removes the face at the given index from the selection set.
// Input  : nIndex - Index of the face in the selection list.
//-----------------------------------------------------------------------------
void CToolPickFace::DeselectFace(int nIndex)
{
	Assert(m_Faces.IsValidIndex(nIndex));

	if (m_Faces.IsValidIndex(nIndex))
	{
		if (m_Faces[nIndex].eOriginalState != FaceState_Partial)
		{
			m_Faces[nIndex].pFace->SetSelectionState(SELECT_NONE);

			//
			// Remove the face from our list.
			//
			RemoveFromList(nIndex);
		}
		else
		{
			//
			// Just set the state to deselected so we can cycle back to partial selection.
			//
			m_Faces[nIndex].pFace->SetSelectionState(SELECT_NONE);
			m_Faces[nIndex].eState = FaceState_None;
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pFace - 
//			eState - 
//-----------------------------------------------------------------------------
void CToolPickFace::AddToList(CMapFace *pFace, FaceState_t eState)
{
	int nIndex = m_Faces.AddToTail();
	m_Faces[nIndex].pFace = pFace;
	m_Faces[nIndex].eState = eState;
	m_Faces[nIndex].eOriginalState = eState;

	m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_ONLY_3D );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : nIndex - 
//-----------------------------------------------------------------------------
void CToolPickFace::RemoveFromList(int nIndex)
{
	Assert(m_Faces.IsValidIndex(nIndex));

	if (m_Faces.IsValidIndex(nIndex))
	{
		m_Faces.FastRemove(nIndex);

		m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_ONLY_3D );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Returns lists of fully selected and partially selected faces.
//-----------------------------------------------------------------------------
void CToolPickFace::GetSelectedFaces(CMapFaceList &FaceListFull, CMapFaceList &FaceListPartial)
{
	FaceListFull.RemoveAll();
	FaceListPartial.RemoveAll();

	for (int i = 0; i < m_Faces.Count(); i++)
	{
		if (m_Faces[i].eState == FaceState_Select)
		{
			FaceListFull.AddToTail(m_Faces[i].pFace);
		}
		else if (m_Faces[i].eState == FaceState_Partial)
		{
			FaceListPartial.AddToTail(m_Faces[i].pFace);
		}
	}
}