//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//

#include "cbase.h"
#include "team_control_point_master.h"
#include "teamplayroundbased_gamerules.h"
#include "team_control_point_round.h"

BEGIN_DATADESC( CTeamControlPointRound )
	DEFINE_KEYFIELD( m_bDisabled,			FIELD_BOOLEAN,	"StartDisabled" ),

	DEFINE_KEYFIELD( m_iszCPNames,			FIELD_STRING,	"cpr_cp_names" ),
	DEFINE_KEYFIELD( m_nPriority,			FIELD_INTEGER,	"cpr_priority" ),
	DEFINE_KEYFIELD( m_iInvalidCapWinner,	FIELD_INTEGER,	"cpr_restrict_team_cap_win" ),
	DEFINE_KEYFIELD( m_iszPrintName,		FIELD_STRING,	"cpr_printname" ),
//	DEFINE_FIELD( m_ControlPoints, CUtlVector < CHandle < CTeamControlPoint > > ),

	DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
	DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),

	DEFINE_INPUTFUNC( FIELD_VOID, "RoundSpawn", InputRoundSpawn ),

	DEFINE_OUTPUT( m_OnStart,		"OnStart" ),
	DEFINE_OUTPUT( m_OnEnd,			"OnEnd" ),
	DEFINE_OUTPUT( m_OnWonByTeam1,	"OnWonByTeam1" ),
	DEFINE_OUTPUT( m_OnWonByTeam2,	"OnWonByTeam2" ),
END_DATADESC()

LINK_ENTITY_TO_CLASS( team_control_point_round, CTeamControlPointRound );


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::Spawn( void )
{
	SetTouch( NULL );

	BaseClass::Spawn();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::Activate( void )
{
	BaseClass::Activate();

	FindControlPoints();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::FindControlPoints( void )
{
	// Let out control point masters know that the round has started
	CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;

	if ( pMaster )
	{
		// go through all the points
		CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, pMaster->GetControlPointName() );
		
		while( pEnt )
		{
			CTeamControlPoint *pPoint = assert_cast<CTeamControlPoint *>(pEnt);

			if ( pPoint )
			{
				const char *pString = STRING( m_iszCPNames );
				const char *pName = STRING( pPoint->GetEntityName() );

				// HACK to work around a problem with cp_a being returned for an entity name with cp_A
				const char *pos = Q_stristr( pString, pName );
				if ( pos )
				{
					int len = Q_strlen( STRING( pPoint->GetEntityName() ) );
					if ( *(pos + len) == ' ' || *(pos + len) == '\0' )
					{
						if( m_ControlPoints.Find( pPoint ) == m_ControlPoints.InvalidIndex() )
						{
							DevMsg( 2, "Adding control point %s to control point round %s\n", pPoint->GetEntityName().ToCStr(), GetEntityName().ToCStr() );
							m_ControlPoints.AddToHead( pPoint );
						}
					}
				}
			}

			pEnt = gEntList.FindEntityByClassname( pEnt, pMaster->GetControlPointName() );
		}
	}

	if( m_ControlPoints.Count() == 0 ) 
	{
		Warning( "Error! No control points found in map for team_game_round %s!\n", GetEntityName().ToCStr() );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Check that the points aren't all held by one team if they are 
//			this will reset the round and will reset all the points
//-----------------------------------------------------------------------------
int CTeamControlPointRound::CheckWinConditions( void )
{
	int iWinners = TeamOwnsAllPoints();
	if ( ( iWinners >= FIRST_GAME_TEAM ) && 
		 ( iWinners != m_iInvalidCapWinner ) )
	{
		FireTeamWinOutput( iWinners );
		return iWinners;
	}

	return -1;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::FireTeamWinOutput( int iWinningTeam )
{
	// Remap team so that first game team = 1
	switch( iWinningTeam - FIRST_GAME_TEAM+1 )
	{
	case 1:
		m_OnWonByTeam1.FireOutput( this, this );
		break;
	case 2:
		m_OnWonByTeam2.FireOutput( this, this );
		break;
	default:
		Assert(0);
		break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CTeamControlPointRound::GetPointOwner( int point )
{
	Assert( point >= 0 );
	Assert( point < MAX_CONTROL_POINTS );

	CTeamControlPoint *pPoint = m_ControlPoints[point];

	if ( pPoint )
		return pPoint->GetOwner();

	return TEAM_UNASSIGNED;
}

//-----------------------------------------------------------------------------
// Purpose: This function returns the team that owns all the cap points. 
//			If its not the case that one team owns them all, it returns 0.
//			
//			Can be passed an overriding team. If this is not null, the passed team
//			number will be used for that cp. Used to predict if that CP changing would
//			win the game.
//-----------------------------------------------------------------------------
int CTeamControlPointRound::TeamOwnsAllPoints( CTeamControlPoint *pOverridePoint /* = NULL */, int iOverrideNewTeam /* = TEAM_UNASSIGNED */ )
{
	int iWinningTeam = TEAM_INVALID;

	// if TEAM_INVALID, haven't found a flag yet
	// if TEAM_UNASSIGNED, it's still being contested 

	// for each control point
	for( int i = 0 ; i < m_ControlPoints.Count() ; i++ )
	{
		int owner = m_ControlPoints[i]->GetOwner();

		if ( pOverridePoint == m_ControlPoints[i] )
		{
			owner = iOverrideNewTeam;
		}

		// the first one we find, set the win to true
		if ( iWinningTeam == TEAM_INVALID )
		{
			iWinningTeam = owner;
		}
		// unassigned means this is being contested, move on
		else if ( iWinningTeam == TEAM_UNASSIGNED )
		{
			continue;
		}
		// if we find another one in the group that isn't the same owner, set the win to false
		else if ( owner != iWinningTeam )
		{
			iWinningTeam = TEAM_UNASSIGNED;
		}		
	}

	// report the first win we find as the winner
	if ( iWinningTeam >= FIRST_GAME_TEAM )
	{
		return iWinningTeam;
	}

	// no wins yet
	return TEAM_UNASSIGNED;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTeamControlPointRound::WouldNewCPOwnerWinGame( CTeamControlPoint *pPoint, int iNewOwner )
{
	return ( TeamOwnsAllPoints( pPoint, iNewOwner ) == iNewOwner );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::InputEnable( inputdata_t &input )
{ 
	m_bDisabled = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::InputDisable( inputdata_t &input )
{ 
	m_bDisabled = true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::InputRoundSpawn( inputdata_t &input )
{
	// clear out old control points
	m_ControlPoints.RemoveAll();

	// find the control points
	FindControlPoints();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::SetupSpawnPoints( void )
{
	CTeamplayRoundBasedRules *pRules = TeamplayRoundBasedRules();

	if ( pRules )
	{
		pRules->SetupSpawnPointsForRound();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::SelectedToPlay( void )
{
	SetupSpawnPoints();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::FireOnStartOutput( void )
{
	m_OnStart.FireOutput( this, this );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamControlPointRound::FireOnEndOutput( void )
{
	m_OnEnd.FireOutput( this, this );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTeamControlPointRound::IsControlPointInRound( CTeamControlPoint *pPoint )
{
	if ( !pPoint )
	{
		return false;
	}

	return ( m_ControlPoints.Find( pPoint ) != m_ControlPoints.InvalidIndex() );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTeamControlPointRound::IsPlayable( void )
{
	int iWinners = TeamOwnsAllPoints();

	if ( ( iWinners >= FIRST_GAME_TEAM ) && 
		 ( iWinners != m_iInvalidCapWinner ) )
	{
		return false; // someone has already won this round
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTeamControlPointRound::MakePlayable( void )
{
	CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
	if ( pMaster )
	{
		if ( !IsPlayable() )
		{
			// we need to try switching the owners of the teams to make this round playable
			for ( int i = FIRST_GAME_TEAM ; i < GetNumberOfTeams() ; i++ )
			{
				for ( int j = 0 ; j < m_ControlPoints.Count() ; j++ )
				{
					if ( ( !pMaster->IsBaseControlPoint( m_ControlPoints[j]->GetPointIndex() ) ) && // this is NOT the base point for one of the teams (we don't want to assign the base to the wrong team)
						 ( !WouldNewCPOwnerWinGame( m_ControlPoints[j], i ) ) ) // making this change would make this round playable
					{
						// need to find the trigger area associated with this point
						CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, pMaster->GetTriggerAreaCaptureName() );
						while( pEnt )
						{
							CTriggerAreaCapture *pArea = assert_cast<CTriggerAreaCapture*>( pEnt );
							if ( pArea )
							{	
								if ( pArea->TeamCanCap( i ) )
								{
									CHandle<CTeamControlPoint> hPoint = pArea->GetControlPoint();
									if ( hPoint == m_ControlPoints[j] )
									{
										// found! 
										pArea->ForceOwner( i ); // this updates the trigger_area *and* the control_point
										return true;
									}
								}
							}

							pEnt = gEntList.FindEntityByClassname( pEnt, pMaster->GetTriggerAreaCaptureName() );
						}
					}
				}
			}
		}
	}

	return false;
}


//-----------------------------------------------------------------------------
// Purpose: returns the first point found that the given team owns
//-----------------------------------------------------------------------------
CHandle<CTeamControlPoint> CTeamControlPointRound::GetPointOwnedBy( int iTeam )
{
	for( int i = 0 ; i < m_ControlPoints.Count() ; i++ )
	{
		if ( m_ControlPoints[i]->GetOwner() == iTeam )
		{
			return m_ControlPoints[i];
		}
	}

	return NULL;
}
