//-----------------------------------------------------------------------------
// Purpose: Enumerates a this object's children, only recursing into groups.
//			Children of entities will not be enumerated.
// Input  : pfn - Enumeration callback function. Called once per child.
//			dwParam - User data to pass into the enumerating callback.
//			Type - Unless NULL, only objects of the given type will be enumerated.
// Output : Returns FALSE if the enumeration was terminated early, TRUE if it completed.
//-----------------------------------------------------------------------------
BOOL CMapClass::EnumChildrenRecurseGroupsOnly(ENUMMAPCHILDRENPROC pfn, DWORD dwParam, MAPCLASSTYPE Type)
{
	POSITION pos = Children.GetHeadPosition();
	while (pos)
	{
		CMapClass *pChild = Children.GetNext(pos);

		if (!Type || pChild->IsMapClass(Type))
		{
			if (!(*pfn)(pChild, dwParam))
			{
				return FALSE;
			}
		}

		if (pChild->IsGroup())
		{
			if (!pChild->EnumChildrenRecurseGroupsOnly(pfn, dwParam, Type))
			{
				return FALSE;
			}
		}
	}

	return TRUE;
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pDependent - 
//-----------------------------------------------------------------------------
void CMapClass::AddDependent(CMapClass *pDependent)
{
	//
	// Never add ourselves to our dependents. It creates a circular dependency
	// which is bad.
	//
	if (pDependent != this)
	{
		//
		// Also, never add one of our ancestors as a dependent. This too creates a
		// nasty circular dependency.
		//
		bool bIsOurAncestor = false;
		CMapClass *pTestParent = GetParent();
		while (pTestParent != NULL)
		{
			if (pTestParent == pDependent)
			{
				bIsOurAncestor = true;
				break;
			}

			pTestParent = pTestParent->GetParent();
		}

		if (!bIsOurAncestor)
		{
			m_Dependents.AddTail(pDependent);
		}
	}
}
//------------------------------------------------------------------------------
// Purpose: Set icon being displayed on output button.
//------------------------------------------------------------------------------
void CObjectProperties::UpdateOutputButton(void)
{
	//VPROF_BUDGET( "CObjectProperties::UpdateOutputButton", "Object Properties" );

	if (!m_pOutputButton)
	{
		return;
	}

	bool bHaveConnection = false;
	bool bIgnoreHiddenTargets = false;
	if ( m_pOutput )
		bIgnoreHiddenTargets = !m_pOutput->ShouldShowHiddenTargets();

	FOR_EACH_OBJ( m_DstObjects, pos )
	{
		CMapClass *pObject = m_DstObjects.Element(pos);

		if ((pObject != NULL) && (pObject->IsMapClass(MAPCLASS_TYPE(CMapEntity))))
		{
			CMapEntity *pEntity = (CMapEntity *)pObject;
			int nStatus = CEntityConnection::ValidateOutputConnections(pEntity, true, bIgnoreHiddenTargets);
			if (nStatus == CONNECTION_BAD)
			{
				SetOutputButtonState(CONNECTION_BAD);
				return;
			}
			else if (nStatus == CONNECTION_GOOD)
			{
				bHaveConnection = true;
			}
		}
	}
//-----------------------------------------------------------------------------
// Purpose: Removes an object from the world.
// Input  : pObject - object to remove from the world.
//			bChildren - whether we're removing the object's children as well.
//-----------------------------------------------------------------------------
void CMapWorld::RemoveObjectFromWorld(CMapClass *pObject, bool bRemoveChildren)
{
	Assert(pObject != NULL);
	if (pObject == NULL)
	{
		return;
	}

	//
	// Unlink the object from the tree.
	//
	CMapClass *pParent = pObject->GetParent();
	Assert(pParent != NULL);
	if (pParent != NULL)
	{
		pParent->RemoveChild(pObject);
	}

	//
	// If it (or any of its children) is an entity, remove it from this
	// world's list of entities.
	//
	EntityList_Remove(pObject, bRemoveChildren);

	//
	// Notify the object so it can release any pointers it may have to other
	// objects in the world. We don't do this in RemoveChild because the object
	// may only be changing parents rather than leaving the world.
	//
	pObject->OnRemoveFromWorld(this, bRemoveChildren);

}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pNode - 
//-----------------------------------------------------------------------------
void CMapWorld::CullTree_DumpNode(CCullTreeNode *pNode, int nDepth)
{
	int nChildCount = pNode->GetChildCount();
	char szText[100];

	if (nChildCount == 0)
	{
		// Leaf
		OutputDebugString("LEAF:\n");
		int nObjectCount = pNode->GetObjectCount();
		for (int nObject = 0; nObject < nObjectCount; nObject++)
		{
			CMapClass *pMapClass = pNode->GetCullTreeObject(nObject);
			sprintf(szText, "%*c %p %s\n", nDepth, ' ', pMapClass, pMapClass->GetType());
			OutputDebugString(szText);
		}
	}
	else
	{
		// Node
		sprintf(szText, "%*s\n", nDepth, "+");
		OutputDebugString(szText);

		for (int nChild = 0; nChild < nChildCount; nChild++)
		{
			CCullTreeNode *pChild = pNode->GetCullTreeChild(nChild);
			CullTree_DumpNode(pChild, nDepth + 1);
		}

		OutputDebugString("\n");
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool COP_Groups::SaveData(void)
{
	if (!IsWindow(m_hWnd))
	{
		return(false);
	}

	//
	// Apply the dialog data to all the objects being edited.
	//
	POSITION pos = m_pObjectList->GetHeadPosition();
	while (pos != NULL)
	{
		CMapClass *pObject = m_pObjectList->GetNext(pos);

		// get current selection
		int iSel = m_cGroups.GetCurSel();

		// find ID from selection (in listbox data)
		DWORD id = m_cGroups.GetItemData(iSel);
		if (id == NO_GROUP_ID && pObject->GetVisGroup())
		{
			pObject->SetVisGroup(NULL);
		}
		else if (id != VALUE_DIFFERENT_ID)
		{
			CVisGroup *pGroup = CMapDoc::GetActiveMapDoc()->VisGroups_GroupForID(id);
			pObject->SetVisGroup(pGroup);
		}
	}

	return(true);
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pObject - The object whose bounding box has changed.
//-----------------------------------------------------------------------------
void CCullTreeNode::UpdateAllCullTreeObjectsRecurse(void)
{
	int nChildCount = GetChildCount();
	if (nChildCount != 0)
	{
		for (int nChild = 0; nChild < nChildCount; nChild++)
		{
			CCullTreeNode *pChild = GetCullTreeChild(nChild);
			pChild->UpdateAllCullTreeObjectsRecurse();
		}
	}
	else
	{
		int nObjectCount = GetObjectCount();
		for (int nObject = 0; nObject < nObjectCount; nObject++)
		{
			CMapClass *pObject = GetCullTreeObject(nObject);

			Vector mins;
			Vector maxs;
			pObject->GetCullBox(mins, maxs);
			if (!BoxesIntersect(mins, maxs, bmins, bmaxs))
			{
				RemoveCullTreeObject(pObject);
			}
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: This functions sets up the list of objects to be clipped.  
//          Initially the list is passed in (typically a Selection set).  On 
//          subsequent "translation" updates the list is refreshed from the 
//          m_pOrigObjects list.
//   Input: pList - the list of objects (solids) to be clipped
//-----------------------------------------------------------------------------
void Clipper3D::SetClipObjects( CMapObjectList *pList )
{
    // check for an empty list
    if( !pList )
        return;

    // save the original list
    m_pOrigObjects = pList;

    // clear the clip results list
    ResetClipResults();

    //
    // copy solids into the clip list
    //
    POSITION pos = m_pOrigObjects->GetHeadPosition();
    while( pos )
    {
        CMapClass *pObject = m_pOrigObjects->GetNext( pos );
        if( !pObject )
            continue;

        if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) )
        {
            AddToClipList( ( CMapSolid* )pObject, this );
        }

        pObject->EnumChildren( ENUMMAPCHILDRENPROC( AddToClipList ), DWORD( this ), MAPCLASS_TYPE( CMapSolid ) );
    }

    // the clipping list is not empty anymore
    m_bEmpty = FALSE;
}
//-----------------------------------------------------------------------------
// Purpose: Called after a map file has been completely loaded.
// Input  : pWorld - The world that we are in.
//-----------------------------------------------------------------------------
void CMapClass::PostloadWorld(CMapWorld *pWorld)
{
	POSITION pos = Children.GetHeadPosition();
	while (pos != NULL)
	{
		CMapClass *pChild = Children.GetNext(pos);
		pChild->PostloadWorld(pWorld);
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *szOldName - 
//			*szNewName - 
//-----------------------------------------------------------------------------
void CMapClass::ReplaceTargetname(const char *szOldName, const char *szNewName)
{
	POSITION pos = Children.GetHeadPosition();
	while (pos != NULL)
	{
		CMapClass *pObject = Children.GetNext(pos);
		pObject->ReplaceTargetname(szOldName, szNewName);
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CMapClass::RemoveAllVisGroups(void)
{
	m_VisGroups.RemoveAll();

	// Remove all visgroups from children as well.
	FOR_EACH_OBJ( m_Children, pos )
	{
		CMapClass *pChild = m_Children.Element(pos);
		pChild->RemoveAllVisGroups();
	}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pError - 
//-----------------------------------------------------------------------------
static void FixEmptyEntity(MapError *pError)
{
	CMapClass *pKillMe = pError->pObjects[0];

	if (pKillMe->GetParent() != NULL)
	{
		GetHistory()->KeepForDestruction(pKillMe);
		pKillMe->GetParent()->RemoveChild(pKillMe);
	}
}
//-----------------------------------------------------------------------------
// Purpose: Returns a coordinate frame to render in
// Input  : matrix - 
// Output : returns true if a new matrix is returned, false if it is invalid
//-----------------------------------------------------------------------------
bool CMapClass::GetTransformMatrix( matrix4_t& matrix )
{
	// try and get our parents transform matrix
	CMapClass *p = CMapClass::GetParent();
	if ( p )
	{
		return p->GetTransformMatrix( matrix );
	}

	return false;
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : bVisible - 
//-----------------------------------------------------------------------------
void CMapClass::SetVisible(bool bVisible)
{
	POSITION pos = Children.GetHeadPosition();
	while (pos != NULL)
	{
		CMapClass *pChild = Children.GetNext(pos);
		pChild->SetVisible(bVisible);
	}

	m_bVisible = bVisible;
}
//-----------------------------------------------------------------------------
// Purpose: Flips this object about a given point.
// Input  : RefPoint - Reference point about which to flip. Set any of the indices
//				to COORD_NOTINIT to NOT flip about that axis.
//-----------------------------------------------------------------------------
void CMapClass::DoTransFlip(const Vector &RefPoint)
{
	CMapPoint::DoTransFlip(RefPoint);

	POSITION p = Children.GetHeadPosition();
	while(p)
	{
		CMapClass *pChild = Children.GetNext(p);
		pChild->TransFlip(RefPoint);
	}
}
//-----------------------------------------------------------------------------
// Purpose: Moves all children. Derived implementations should call this,
//			then do their own thing.
// Input  : delta - 
//-----------------------------------------------------------------------------
void CMapClass::DoTransMove(const Vector &delta)
{
	CMapPoint::DoTransMove(delta);

	POSITION p = Children.GetHeadPosition();
	while (p)
	{
		CMapClass *pChild = Children.GetNext(p);
		pChild->TransMove(delta);
	}
}
//-----------------------------------------------------------------------------
// Purpose: Transforms all children. Derived implementations should call this,
//			then do their own thing.
// Input  : t - Pointer to class containing transformation information.
//-----------------------------------------------------------------------------
void CMapClass::DoTransform(Box3D *t)
{
	CMapPoint::DoTransform(t);

	POSITION p = Children.GetHeadPosition();
	while (p != NULL)
	{
		CMapClass *pChild = Children.GetNext(p);
		pChild->Transform(t);
	}
}
//-----------------------------------------------------------------------------
// Purpose: Calls RenderPreload for each of our children. This allows them to
//			cache any resources that they need for rendering.
// Input  : pRender - Pointer to the 3D renderer.
//-----------------------------------------------------------------------------
bool CMapClass::RenderPreload(CRender3D *pRender, bool bNewContext)
{
	POSITION pos = Children.GetHeadPosition();

	while (pos != NULL)
	{
		CMapClass *pChild = Children.GetNext(pos);
		pChild->RenderPreload(pRender, bNewContext);
	}

	return(true);
}
//-----------------------------------------------------------------------------
// Purpose: Copies all children of a given object as children of this object.
//			NOTE: The child objects are replicated, not merely added as children.
// Input  : pobj - The object whose children are to be copied.
//-----------------------------------------------------------------------------
void CMapClass::CopyChildrenFrom(CMapClass *pobj, bool bUpdateDependencies)
{
	POSITION p = pobj->Children.GetHeadPosition();
	while (p != NULL)
	{
		CMapClass *pChild = pobj->Children.GetNext(p);

		CMapClass *pChildCopy = pChild->Copy(bUpdateDependencies);
		pChildCopy->CopyChildrenFrom(pChild, bUpdateDependencies);
		AddChild(pChildCopy);
	}
}
//-----------------------------------------------------------------------------
// Purpose: Called to notify the object that it has just been cloned
//			iterates through and notifies all the children of their cloned state
//			NOTE: assumes that the children are in the same order in both the
//			original and the clone
// Input  : pNewObj - the clone of this object
//			OriginalList - The list of objects that were cloned
//			NewList - The parallel list of clones of objects in OriginalList
//-----------------------------------------------------------------------------
void CMapClass::OnPreClone( CMapClass *pNewObj, CMapWorld *pWorld, CMapObjectList &OriginalList, CMapObjectList &NewList )
{
	POSITION p = Children.GetHeadPosition();
	POSITION q = pNewObj->Children.GetHeadPosition();
	while ( p && q )
	{
		CMapClass *pChild = Children.GetNext( p );
		CMapClass *pNewChild = pNewObj->Children.GetNext( q );

		pChild->OnPreClone( pNewChild, pWorld, OriginalList, NewList );
	}
}
//-----------------------------------------------------------------------------
// Purpose: Called after this object is added to the world.
//
//			NOTE: This function is NOT called during serialization. Use PostloadWorld
//				  to do similar bookkeeping after map load.
//
// Input  : pWorld - The world that we have been added to.
//-----------------------------------------------------------------------------
void CMapClass::OnAddToWorld(CMapWorld *pWorld)
{
	//
	// Notify all our children.
	//
	POSITION pos = Children.GetHeadPosition();
	while (pos != NULL)
	{
		CMapClass *pChild = Children.GetNext(pos);
		pChild->OnAddToWorld(pWorld);
	}
}
//-----------------------------------------------------------------------------
// Purpose: Notifies this object that a copy of itself is being pasted.
//			Allows the object to fixup any references to other objects in the
//			clipboard with references to their copies.
// Input  : pCopy - 
//			pSourceWorld - 
//			pDestWorld - 
//			OriginalList - 
//			NewList - 
//-----------------------------------------------------------------------------
void CMapClass::OnPaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, CMapObjectList &OriginalList, CMapObjectList &NewList)
{
	POSITION p = Children.GetHeadPosition();
	POSITION q = pCopy->Children.GetHeadPosition();
	while (p && q)
	{
		CMapClass *pChild = Children.GetNext(p);
		CMapClass *pCopyChild = pCopy->Children.GetNext(q);

		pChild->OnPaste(pCopyChild, pSourceWorld, pDestWorld, OriginalList, NewList);
	}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the origin of this object and its children.
//			FIXME: Should our children necessarily have the same origin as us?
//				   Seems like we should translate our children by our origin delta
//-----------------------------------------------------------------------------
void CMapClass::SetOrigin( Vector& origin )
{
	CMapPoint::SetOrigin( origin );
 
	POSITION pos = Children.GetHeadPosition();

	while ( pos != NULL )
	{
		CMapClass *pChild = Children.GetNext( pos );

		pChild->SetOrigin( origin );
	}

	PostUpdate(Notify_Changed);
}
BOOL CMapClass::IsChildOf(CMapClass *pObject)
{
	CMapClass *pParent = Parent;

	while(1)
	{
		if(pParent == pObject)
			return TRUE;
		if(pParent->IsWorldObject())
			break;	// world object, not parent .. return false.
		pParent = pParent->Parent;
	}

	return FALSE;
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pView - 
//			point - 
//			nData - 
// Output : 
//-----------------------------------------------------------------------------
CMapClass *CMapClass::HitTest2D(CMapView2D *pView, const Vector2D &point, int &nHitData)
{
	POSITION pos = Children.GetHeadPosition();
	while (pos != NULL)
	{
		CMapClass *pChild = Children.GetNext(pos);
		CMapClass *pHit = pChild->HitTest2D(pView, point, nHitData);
		if (pHit != NULL)
		{
			return pHit;
		}
	}

	return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the world object that the given object belongs to.
// Input  : pStart - Object to traverse up from to find the world object.
//-----------------------------------------------------------------------------
CMapWorld *CMapClass::GetWorldObject(CMapClass *pStart)
{
	CMapClass *pobj = pStart;

	while (pobj != NULL)
	{
		if (pobj->IsWorldObject())
		{
			return((CMapWorld *)pobj);
		}
		pobj = pobj->Parent;
	}

	// has no world:
	return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Iterates through an object, and all it's children, looking for an
//			entity with a matching key and value
// Input  : key - 
//			value - 
// Output : CMapEntity - the entity found
//-----------------------------------------------------------------------------
CMapEntity *CMapClass::FindChildByKeyValue( LPCSTR key, LPCSTR value )
{
	if ( !key || !value )
		return NULL;

	POSITION p = Children.GetHeadPosition();
	while( p )
	{
		CMapClass *pChild = Children.GetNext( p );
		CMapEntity *e = pChild->FindChildByKeyValue( key, value );
		if ( e )
			return e;
	}

	return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the render color of this object and all its children.
// Input  : uchRed, uchGreen, uchBlue - Color components.
//-----------------------------------------------------------------------------
void CMapClass::SetRenderColor(unsigned char uchRed, unsigned char uchGreen, unsigned char uchBlue)
{
	CMapAtom::SetRenderColor(uchRed, uchGreen, uchBlue);

	//
	// Set the render color of all our children.
	//
	POSITION pos = Children.GetHeadPosition();
	while (pos != NULL)
	{
		CMapClass *pChild = Children.GetNext(pos);
		if (pChild != NULL)
		{
			pChild->SetRenderColor(uchRed, uchGreen, uchBlue);
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the render color of this object and all its children.
// Input  : uchRed, uchGreen, uchBlue - Color components.
//-----------------------------------------------------------------------------
void CMapClass::SetRenderColor(color32 rgbColor)
{
	CMapAtom::SetRenderColor(rgbColor);

	//
	// Set the render color of all our children.
	//
	POSITION pos = Children.GetHeadPosition();
	while (pos != NULL)
	{
		CMapClass *pChild = Children.GetNext(pos);
		if (pChild != NULL)
		{
			pChild->SetRenderColor(rgbColor);
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: Recalculate's this object's bounding boxes. CMapClass-derived classes
//			should call this first, then update using their local data.
// Input  : bFullUpdate - When set to TRUE, call CalcBounds on all children
//				before updating our bounds.
//-----------------------------------------------------------------------------
void CMapClass::CalcBounds(BOOL bFullUpdate)
{
	m_CullBox.ResetBounds();
	m_Render2DBox.ResetBounds();

	POSITION p = Children.GetHeadPosition();
	while (p)
	{
		CMapClass *pChild = Children.GetNext(p);
		if (bFullUpdate)
		{
			pChild->CalcBounds(TRUE);
		}

		m_CullBox.UpdateBounds(&pChild->m_CullBox);
		m_Render2DBox.UpdateBounds(&pChild->m_Render2DBox);
	}
}