//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: This is an entity that represents a vgui screen
//
// $NoKeywords: $
//===========================================================================//

#include "cbase.h"
#include "vguiscreen.h"
#include "networkstringtable_gamedll.h"
#include "saverestore_stringtable.h"

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

//-----------------------------------------------------------------------------
// This is an entity that represents a vgui screen
//-----------------------------------------------------------------------------

IMPLEMENT_SERVERCLASS_ST(CVGuiScreen, DT_VGuiScreen)
	SendPropFloat(SENDINFO(m_flWidth),	0, SPROP_NOSCALE ),
	SendPropFloat(SENDINFO(m_flHeight),	0, SPROP_NOSCALE ),
	SendPropInt(SENDINFO(m_nAttachmentIndex), 5, SPROP_UNSIGNED ),
	SendPropInt(SENDINFO(m_nPanelName), MAX_VGUI_SCREEN_STRING_BITS, SPROP_UNSIGNED ),
	SendPropInt(SENDINFO(m_fScreenFlags), VGUI_SCREEN_MAX_BITS, SPROP_UNSIGNED ),
	SendPropInt(SENDINFO(m_nOverlayMaterial), MAX_MATERIAL_STRING_BITS, SPROP_UNSIGNED ),
	SendPropEHandle(SENDINFO(m_hPlayerOwner)),
END_SEND_TABLE();

LINK_ENTITY_TO_CLASS( vgui_screen, CVGuiScreen );
LINK_ENTITY_TO_CLASS( vgui_screen_team, CVGuiScreen );
PRECACHE_REGISTER( vgui_screen );


//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CVGuiScreen )

	DEFINE_CUSTOM_FIELD( m_nPanelName, &g_VguiScreenStringOps ),
	DEFINE_FIELD( m_nAttachmentIndex, FIELD_INTEGER ),
//	DEFINE_FIELD( m_nOverlayMaterial, FIELD_INTEGER ),
	DEFINE_FIELD( m_fScreenFlags, FIELD_INTEGER ),
	DEFINE_KEYFIELD( m_flWidth, FIELD_FLOAT, "width" ),
	DEFINE_KEYFIELD( m_flHeight, FIELD_FLOAT, "height" ),
	DEFINE_KEYFIELD( m_strOverlayMaterial, FIELD_STRING, "overlaymaterial" ),
	DEFINE_FIELD( m_hPlayerOwner, FIELD_EHANDLE ),

	DEFINE_INPUTFUNC( FIELD_VOID, "SetActive", InputSetActive ),
	DEFINE_INPUTFUNC( FIELD_VOID, "SetInactive", InputSetInactive ),

END_DATADESC()


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CVGuiScreen::CVGuiScreen()
{
	m_nOverlayMaterial = OVERLAY_MATERIAL_INVALID_STRING;
	m_hPlayerOwner = NULL;
}


//-----------------------------------------------------------------------------
// Read in worldcraft data...
//-----------------------------------------------------------------------------
bool CVGuiScreen::KeyValue( const char *szKeyName, const char *szValue ) 
{
	//!! temp hack, until worldcraft is fixed
	// strip the # tokens from (duplicate) key names
	char *s = (char *)strchr( szKeyName, '#' );
	if ( s )
	{
		*s = '\0';
	}

	if ( FStrEq( szKeyName, "panelname" ))
	{
		SetPanelName( szValue );
		return true;
	}

	// NOTE: Have to do these separate because they set two values instead of one
	if( FStrEq( szKeyName, "angles" ) )
	{
		Assert( GetMoveParent() == NULL );
		QAngle angles;
		UTIL_StringToVector( angles.Base(), szValue );

		// Because the vgui screen basis is strange (z is front, y is up, x is right)
		// we need to rotate the typical basis before applying it
		VMatrix mat, rotation, tmp;
		MatrixFromAngles( angles, mat );
		MatrixBuildRotationAboutAxis( rotation, Vector( 0, 1, 0 ), 90 );
		MatrixMultiply( mat, rotation, tmp );
		MatrixBuildRotateZ( rotation, 90 );
		MatrixMultiply( tmp, rotation, mat );
		MatrixToAngles( mat, angles );
		SetAbsAngles( angles );

		return true;
	}

	return BaseClass::KeyValue( szKeyName, szValue );
}


//-----------------------------------------------------------------------------
// Precache...
//-----------------------------------------------------------------------------
void CVGuiScreen::Precache()
{
	BaseClass::Precache();
	if ( m_strOverlayMaterial != NULL_STRING )
	{
		PrecacheMaterial( STRING(m_strOverlayMaterial) );
	}
}


//-----------------------------------------------------------------------------
// Spawn...
//-----------------------------------------------------------------------------
void CVGuiScreen::Spawn()
{
	Precache();

	// This has no model, but we want it to transmit if it's in the PVS anyways
	AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
	m_nAttachmentIndex = 0;
	SetSolid( SOLID_OBB );
	AddSolidFlags( FSOLID_NOT_SOLID );
	SetActualSize( m_flWidth, m_flHeight );
	m_fScreenFlags.Set( VGUI_SCREEN_ACTIVE );

	m_takedamage = DAMAGE_NO;
	AddFlag( FL_NOTARGET );
}

//-----------------------------------------------------------------------------
// Spawn...
//-----------------------------------------------------------------------------
void CVGuiScreen::Activate()
{
	BaseClass::Activate();

	if ( m_nOverlayMaterial == OVERLAY_MATERIAL_INVALID_STRING && m_strOverlayMaterial != NULL_STRING )
	{
		SetOverlayMaterial( STRING(m_strOverlayMaterial) );
	}
}

void CVGuiScreen::OnRestore()
{
	UpdateTransmitState();

	BaseClass::OnRestore();
}

void CVGuiScreen::SetAttachmentIndex( int nIndex )
{
	m_nAttachmentIndex = nIndex;
}

void CVGuiScreen::SetOverlayMaterial( const char *pMaterial )
{
	int iMaterial = GetMaterialIndex( pMaterial );

	if ( iMaterial == 0 )
	{
		m_nOverlayMaterial = OVERLAY_MATERIAL_INVALID_STRING;
	}
	else
	{
		m_nOverlayMaterial = iMaterial;
	}
}

bool CVGuiScreen::IsActive() const 
{ 
	return (m_fScreenFlags & VGUI_SCREEN_ACTIVE) != 0; 
}

void CVGuiScreen::SetActive( bool bActive )
{
	if (bActive != IsActive())
	{
		if (!bActive)
		{
			m_fScreenFlags &= ~VGUI_SCREEN_ACTIVE;
		}
		else
		{
			m_fScreenFlags.Set(  m_fScreenFlags | VGUI_SCREEN_ACTIVE );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CVGuiScreen::IsAttachedToViewModel() const
{
	return (m_fScreenFlags & VGUI_SCREEN_ATTACHED_TO_VIEWMODEL) != 0; 
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : bAttached - 
//-----------------------------------------------------------------------------
void CVGuiScreen::SetAttachedToViewModel( bool bAttached )
{
	if (bAttached != IsActive())
	{
		if (!bAttached)
		{
			m_fScreenFlags &= ~VGUI_SCREEN_ATTACHED_TO_VIEWMODEL;
		}
		else
		{
			m_fScreenFlags.Set( m_fScreenFlags | VGUI_SCREEN_ATTACHED_TO_VIEWMODEL );

			// attached screens have different transmit rules
			DispatchUpdateTransmitState();
		}

		// attached screens have different transmit rules
		DispatchUpdateTransmitState();
	}
}

void CVGuiScreen::SetTransparency( bool bTransparent )
{
	if (!bTransparent)
	{
		m_fScreenFlags &= ~VGUI_SCREEN_TRANSPARENT;
	}
	else
	{
		m_fScreenFlags.Set( m_fScreenFlags | VGUI_SCREEN_TRANSPARENT );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CVGuiScreen::InputSetActive( inputdata_t &inputdata )
{
	SetActive( true );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CVGuiScreen::InputSetInactive( inputdata_t &inputdata )
{
	SetActive( false );
}

bool CVGuiScreen::IsVisibleOnlyToTeammates() const 
{ 
	return (m_fScreenFlags & VGUI_SCREEN_VISIBLE_TO_TEAMMATES) != 0; 
}

void CVGuiScreen::MakeVisibleOnlyToTeammates( bool bActive )
{
	if (bActive != IsVisibleOnlyToTeammates())
	{
		if (!bActive)
		{
			m_fScreenFlags &= ~VGUI_SCREEN_VISIBLE_TO_TEAMMATES;
		}
		else
		{
			m_fScreenFlags.Set(  m_fScreenFlags | VGUI_SCREEN_VISIBLE_TO_TEAMMATES );
		}
	}
}

bool CVGuiScreen::IsVisibleToTeam( int nTeam )
{
	// FIXME: Should this maybe go into a derived class of some sort?
	// Don't bother with screens on the wrong team
	if ( IsVisibleOnlyToTeammates() && (nTeam > 0) )
	{
		// Hmmm... sort of a hack...
		CBaseEntity *pOwner = GetOwnerEntity();
		if ( pOwner && (nTeam != pOwner->GetTeamNumber()) )
			return false;
	}
	
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Screens attached to view models only go to client if viewmodel is being sent, too.
// Input  : *recipient - 
//			*pvs - 
//			clientArea - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
int CVGuiScreen::UpdateTransmitState()
{
	if ( IsAttachedToViewModel() )
	{
		// only send to the owner, or someone spectating the owner.
		return SetTransmitState( FL_EDICT_FULLCHECK );
	}
	else if ( GetMoveParent() )
	{
		// Let the parent object trigger the send. This is more efficient than having it call CBaseEntity::ShouldTransmit
		// for all the vgui screens in the map.
		return SetTransmitState( FL_EDICT_PVSCHECK );
	}
	else
	{
		return BaseClass::UpdateTransmitState();
	}
}

int CVGuiScreen::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
	Assert( IsAttachedToViewModel() );

	CBaseEntity *pViewModel = GetOwnerEntity();

	if ( pViewModel )
	{
		return pViewModel->ShouldTransmit( pInfo );
	}

	return BaseClass::ShouldTransmit( pInfo );
}

//-----------------------------------------------------------------------------
// Convert the panel name into an integer
//-----------------------------------------------------------------------------
void CVGuiScreen::SetPanelName( const char *pPanelName )
{
	m_nPanelName = g_pStringTableVguiScreen->AddString( CBaseEntity::IsServer(), pPanelName );
}

const char *CVGuiScreen::GetPanelName() const
{
	return g_pStringTableVguiScreen->GetString( m_nPanelName );
}


//-----------------------------------------------------------------------------
// Sets the screen size + resolution
//-----------------------------------------------------------------------------
void CVGuiScreen::SetActualSize( float flWidth, float flHeight )
{
	m_flWidth = flWidth;
	m_flHeight = flHeight;

	Vector mins, maxs;
	mins.Init( 0.0f, 0.0f, -0.1f );
	maxs.Init( 0.0f, 0.0f, 0.1f );
	if (flWidth > 0)
		maxs.x = flWidth;
	else
		mins.x = flWidth;
	if (flHeight > 0)
		maxs.y = flHeight;
	else
		mins.y = flHeight;

	UTIL_SetSize( this, mins, maxs );
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
void CVGuiScreen::SetPlayerOwner( CBasePlayer *pPlayer, bool bOwnerOnlyInput /* = false */ )
{
	m_hPlayerOwner = pPlayer;

	if ( bOwnerOnlyInput )
	{
		m_fScreenFlags.Set( VGUI_SCREEN_ONLY_USABLE_BY_OWNER );
	}
}


//-----------------------------------------------------------------------------
// Precaches a vgui screen
//-----------------------------------------------------------------------------
void PrecacheVGuiScreen( const char *pScreenType )
{
	g_pStringTableVguiScreen->AddString( CBaseEntity::IsServer(), pScreenType );
}


//-----------------------------------------------------------------------------
// Creates a vgui screen, attaches it to another player
//-----------------------------------------------------------------------------
CVGuiScreen *CreateVGuiScreen( const char *pScreenClassname, const char *pScreenType, CBaseEntity *pAttachedTo, CBaseEntity *pOwner, int nAttachmentIndex )
{
	Assert( pAttachedTo );
	CVGuiScreen *pScreen = (CVGuiScreen *)CBaseEntity::Create( pScreenClassname, vec3_origin, vec3_angle, pAttachedTo );

	pScreen->SetPanelName( pScreenType );
	pScreen->FollowEntity( pAttachedTo );
	pScreen->SetOwnerEntity( pOwner );
	pScreen->SetAttachmentIndex( nAttachmentIndex );

	return pScreen;
}

void DestroyVGuiScreen( CVGuiScreen *pVGuiScreen )
{
	if (pVGuiScreen)
	{
		UTIL_Remove( pVGuiScreen );
	}
}