//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "gamerect.h"
// To handle scaling
#include "materialsystem/imaterialsystem.h"
#include "animdata.h"
#include "Color.h"
#include "gameuisystemmgr.h"



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


BEGIN_DMXELEMENT_UNPACK ( CGameRect )
	DMXELEMENT_UNPACK_FIELD_UTLSTRING( "name", "", m_pName ) 
	DMXELEMENT_UNPACK_FIELD( "center", "0 0", Vector2D, m_Geometry.m_Center ) 
	DMXELEMENT_UNPACK_FIELD( "scale", "0 0", Vector2D, m_Geometry.m_Scale ) 
	DMXELEMENT_UNPACK_FIELD( "rotation", "0", float, m_Geometry.m_Rotation )
	DMXELEMENT_UNPACK_FIELD( "maintainaspectratio", "0", bool, m_Geometry.m_bMaintainAspectRatio )
	DMXELEMENT_UNPACK_FIELD( "sublayertype", "0", int, m_Geometry.m_Sublayer )
	DMXELEMENT_UNPACK_FIELD( "visible", "1", bool, m_Geometry.m_bVisible )
	DMXELEMENT_UNPACK_FIELD( "initialstate", "-1", int, m_CurrentState )
	DMXELEMENT_UNPACK_FIELD( "horizgradient", "0", bool, m_Geometry.m_bHorizontalGradient )
	DMXELEMENT_UNPACK_FIELD( "color", "255 255 255 255", Color, m_Geometry.m_Color )
	DMXELEMENT_UNPACK_FIELD( "topcolor", "255 255 255 255", Color, m_Geometry.m_TopColor )
	DMXELEMENT_UNPACK_FIELD( "bottomcolor", "255 255 255 255", Color, m_Geometry.m_BottomColor )


	// color is gotten from log.
	// sheet seq number is gotten from log.
	
END_DMXELEMENT_UNPACK( CGameRect, s_GameRectUnpack )

//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CGameRect::CGameRect( const char *pName ) 
{
	m_Geometry.m_SheetSequenceNumber = 0; // FIXME, not updating seq numbers yet.
	m_bCanAcceptInput = false;

	// DME default values.
	m_pName = pName;
	m_Geometry.m_Center.x = 0;
	m_Geometry.m_Center.y = 0;
	m_Geometry.m_Scale.x = 0;
	m_Geometry.m_Scale.y = 0;
	m_Geometry.m_Rotation = 0;
	m_Geometry.m_bMaintainAspectRatio = 0;
	m_Geometry.m_Sublayer = 0;
	m_Geometry.m_bVisible = true;
	m_CurrentState = -1;
	m_Geometry.m_bHorizontalGradient = false;
	m_Geometry.m_Color.r = 255;
	m_Geometry.m_Color.g = 255;
	m_Geometry.m_Color.b = 255;
	m_Geometry.m_Color.a = 255;
	m_Geometry.m_TopColor.r = 255;
	m_Geometry.m_TopColor.g = 255;
	m_Geometry.m_TopColor.b = 255;
	m_Geometry.m_TopColor.a = 255;
	m_Geometry.m_BottomColor.r = 255;
	m_Geometry.m_BottomColor.g = 255;
	m_Geometry.m_BottomColor.b = 255;
	m_Geometry.m_BottomColor.a = 255;

	m_Geometry.m_RelativePositions.AddToTail( Vector2D( -.5, -.5 ) );
	m_Geometry.m_RelativePositions.AddToTail( Vector2D( .5, -.5 ) );
	m_Geometry.m_RelativePositions.AddToTail( Vector2D( .5, .5 ) );
	m_Geometry.m_RelativePositions.AddToTail( Vector2D( -.5, .5 ) );

	m_Geometry.m_TextureCoords.AddToTail( Vector2D( 0.0, 0.0 ) );
	m_Geometry.m_TextureCoords.AddToTail( Vector2D( 1.0, 0.0 ) );
	m_Geometry.m_TextureCoords.AddToTail( Vector2D( 1.0, 1.0 ) );
	m_Geometry.m_TextureCoords.AddToTail( Vector2D( 0.0, 1.0 ) );

	SetupVertexColors();

	CTriangle triangle;
	triangle.m_PointIndex[0] = 0;
	triangle.m_PointIndex[1] = 1;
	triangle.m_PointIndex[2] = 2;
	m_Geometry.m_Triangles.AddToTail( triangle );
	triangle.m_PointIndex[0] = 0;
	triangle.m_PointIndex[1] = 2;
	triangle.m_PointIndex[2] = 3;
	m_Geometry.m_Triangles.AddToTail( triangle );
}


CGameRect::~CGameRect() 
{
}


//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
bool CGameRect::Unserialize( CDmxElement *pGraphic )
{
	pGraphic->UnpackIntoStructure( this, s_GameRectUnpack );


	// GEOMETRY
	CDmxAttribute *pRelativePositions = pGraphic->GetAttribute( "relativepositions" );
	if ( !pRelativePositions || pRelativePositions->GetType() != AT_VECTOR2_ARRAY )
    {
		return false;
    }
	const CUtlVector< Vector2D > &relpositions = pRelativePositions->GetArray< Vector2D >( );
	int nCount = relpositions.Count();
	m_Geometry.m_RelativePositions.RemoveAll();
    for ( int i = 0; i < nCount; ++i )
    {
		m_Geometry.m_RelativePositions.AddToTail( Vector2D( relpositions[i].x, relpositions[i].y ) );	
    }



	CDmxAttribute *pTexCoords = pGraphic->GetAttribute( "texcoords" );
	if ( !pTexCoords || pTexCoords->GetType() != AT_VECTOR2_ARRAY )
    {
		return false;
    }
	const CUtlVector< Vector2D > &texcoords = pTexCoords->GetArray< Vector2D >( );
	nCount = texcoords.Count();
	m_Geometry.m_TextureCoords.RemoveAll();
    for ( int i = 0; i < nCount; ++i )
    {
		m_Geometry.m_TextureCoords.AddToTail( Vector2D( texcoords[i].x, texcoords[i].y ) );	
    }

	SetupVertexColors();


	CDmxAttribute *pTriangles = pGraphic->GetAttribute( "triangles" );
	if ( !pTriangles || pTriangles->GetType() != AT_ELEMENT_ARRAY )
    {
		return false;
    }
	const CUtlVector< CDmxElement * > &triangles = pTriangles->GetArray< CDmxElement * >( );
	nCount = triangles.Count();
	m_Geometry.m_Triangles.RemoveAll();
    for ( int i = 0; i < nCount; ++i )
    {
		CDmxAttribute *pPoints = triangles[i]->GetAttribute( "positionindexes" );
		const CUtlVector< int > &points = pPoints->GetArray< int >( );

		CTriangle triangle;
		triangle.m_PointIndex[0] = points[0];
		triangle.m_PointIndex[1] = points[1];
		triangle.m_PointIndex[2] = points[2];

		m_Geometry.m_Triangles.AddToTail( triangle );	
    }



	// ANIMSTATES
	CDmxAttribute *pImageAnims = pGraphic->GetAttribute( "imageanims" );
	if ( !pImageAnims || pImageAnims->GetType() != AT_ELEMENT_ARRAY )
    {
		return false;
    }
	const CUtlVector< CDmxElement * > &imageanims = pImageAnims->GetArray< CDmxElement * >( );
	nCount = imageanims.Count();
    for ( int i = 0; i < nCount; ++i )
    {
		CAnimData *pAnimData = new CAnimData;
		if ( !pAnimData->Unserialize( imageanims[i] ) )
		{
			delete pAnimData;
			return false;
		}
		m_Anims.AddToTail( pAnimData );
    }

	// Ok the initial state is 0, which is (usually ) default.
	// default could be aliased to another state though so if it is fix the initial state here.
	// default might also not be the state that is 0 so this sets the graphic's initial 
	// state to be the default one.
	SetState( "default" );

	return true;
}


//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
void CGameRect::UpdateGeometry()
{	
	if ( m_CurrentState == -1 )
		return;

	Assert( m_CurrentState < m_Anims.Count() );

	DmeTime_t flAnimTime = GetAnimationTimePassed();

	// Update texture
	m_Geometry.m_SheetSequenceNumber = m_Anims[ m_CurrentState ]->m_TextureAnimSheetSeqNumber;
	m_Geometry.m_AnimationRate = m_Anims[ m_CurrentState ]->m_AnimationRate;

	// Update color
	m_Anims[ m_CurrentState ]->m_ColorAnim.GetValue( flAnimTime, &m_Geometry.m_Color );
	
	// Update center location
	m_Anims[ m_CurrentState ]->m_CenterPosAnim.GetValue( flAnimTime, &m_Geometry.m_Center );
	
	// Update scale
	m_Anims[ m_CurrentState ]->m_ScaleAnim.GetValue( flAnimTime, &m_Geometry.m_Scale );

	// Update rotation
	m_Anims[ m_CurrentState ]->m_RotationAnim.GetValue( flAnimTime, &m_Geometry.m_Rotation );
}


//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
void CGameRect::UpdateRenderData( color32 parentColor, CUtlVector< RenderGeometryList_t > &renderGeometryLists, int firstListIndex )
{
	if ( !m_Geometry.m_bVisible )
		return;

	m_Geometry.SetResultantColor( parentColor );
	m_Geometry.UpdateRenderData( renderGeometryLists, firstListIndex );

	// Now transform our array of positions into local graphic coord system.
	int nCount = m_Geometry.m_RelativePositions.Count();
	m_ScreenPositions.RemoveAll();
	for ( int i = 0; i < nCount; ++i )
	{
		// Position
		Vector relativePosition( m_Geometry.m_RelativePositions[i].x, m_Geometry.m_RelativePositions[i].y, 0 );
		Vector screenpos;
		VectorTransform( relativePosition, m_Geometry.m_RenderToScreen, screenpos );
		m_ScreenPositions.AddToTail( Vector2D( screenpos.x, screenpos.y ) );
	}	
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
void CGameRect::SetupVertexColors()
{
	m_Geometry.m_VertexColors.RemoveAll();

	// Create 4 vertex colors for this rect.
	color32 c;
	c.r = 255;
	c.g = 255;
	c.b = 255;
	c.a = 255;
	m_Geometry.m_VertexColors.AddToTail( c );
	m_Geometry.m_VertexColors.AddToTail( c );
	m_Geometry.m_VertexColors.AddToTail( c );
	m_Geometry.m_VertexColors.AddToTail( c );
}

//-----------------------------------------------------------------------------
//	Determine if x,y is inside the graphic.
//-----------------------------------------------------------------------------
bool CGameRect::HitTest( int x, int y )
{
	if ( !m_Geometry.m_bVisible ) 
		return false;

	if ( m_ScreenPositions.Count() == 0 )
		return false;

	for ( int i = 0; i < m_Geometry.GetTriangleCount(); ++i )
	{
		if ( PointTriangleHitTest( 
			m_ScreenPositions[ m_Geometry.m_Triangles[i].m_PointIndex[0] ],
			m_ScreenPositions[ m_Geometry.m_Triangles[i].m_PointIndex[1] ],
			m_ScreenPositions[ m_Geometry.m_Triangles[i].m_PointIndex[2] ],
			Vector2D( x, y ) ) )
		{
			//Msg( "%d, %d hit\n", x, y );
			return true;
		}
	}

	return false;
}