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

#include "cbase.h"
#include "hud.h"
#include "hudelement.h"
#include <vgui_controls/Panel.h>
#include <vgui/ISurface.h>
#include <vgui/IVGui.h>
#include <vgui_controls/Label.h>
#include <igameevents.h>
#include "hl2wars/rts_shareddefs.h"
#include <KeyValues.h>
#include <filesystem.h>
#include "iclientmode.h"

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

#define TEXTPOSX vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), 25 )
#define TEXTPOSY vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), 7 )

#define OBJELHEIGHT vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), 25 )

#define BRCORRECTION vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), 5 )

using namespace vgui;

const int NumSegments = 7;
static int coord[NumSegments+1] = {
	0,
	1,
	2,
	3,
	4,
	6,
	9,
	10
};

//-----------------------------------------------------------------------------
// Purpose: Helper class
//-----------------------------------------------------------------------------
class CObjectiveElement : public vgui::Panel
{
	DECLARE_CLASS_SIMPLE( CObjectiveElement, vgui::Panel );
public:
	CObjectiveElement();
	~CObjectiveElement();

	//virtual void	OnRestore();

	void SetText( const char *pText );
	bool ShouldBeDeleted() { return m_bShouldBeDeleted; };
	void MarkForDeletion();
	
	bool ShouldBeVisible() { return m_bShouldBeVisible; };
	void SetShouldBeVisible( bool visible ) { m_bShouldBeVisible = visible; };

	bool GetInitPos() { return m_bInitPos; };
	void SetInitPos( bool init ) { m_bInitPos = init; };
	

protected:
	virtual void	ApplySchemeSettings( vgui::IScheme *scheme );
	virtual void	Paint();
	virtual void	OnThink();

	// vgui overrides for rounded corner background
	virtual void PaintBackground();
	virtual void PaintBorder();

private:
	Label *pText;
	CHudTexture *m_pObjIcon;
	Color m_clrObjIcon;

	// rounded corners
	Color					 m_bgColor;
	Color					 m_borderColor;

	bool m_bInitPos;	// init pos
	bool m_bFadeIn;		// add creation, we fade in.

	// 
	bool m_bShouldBeVisible;

	// deletion
	bool m_bShouldTransToDeletion;
	bool m_bShouldBeDeleted;
};

//-----------------------------------------------------------------------------
// Purpose: The class containing the objectives
//-----------------------------------------------------------------------------
class CHudObjectives : public CHudElement, public vgui::Panel
{
	DECLARE_CLASS_SIMPLE( CHudObjectives, vgui::Panel );
public:
	CHudObjectives( const char *pElementName );
	~CHudObjectives();

protected:
	virtual void	ApplySchemeSettings( vgui::IScheme *scheme );
	virtual void	Paint();
	virtual void	OnThink();
	virtual void	Reset();
	void			LevelShutdown( void );

	// IGameEventListener interface:
	virtual void FireGameEvent( IGameEvent *event);
	void LoadObjectiveData( const char * levelname );

private:	
	CUtlVector < CObjectiveElement *> m_utlObjElements;

	// keyvalues objectives
	KeyValues * m_ObjectivesKeyValues; // keyvalues describing overview parameters
	int m_iNumObjectives;
};

DECLARE_HUDELEMENT( CHudObjectives );

// ##################################################################################
//	>> Helper class
//
// ##################################################################################

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CObjectiveElement::CObjectiveElement()
{
	//SetPaintBackgroundType( 2 );
	m_bShouldBeDeleted = false;
	m_bShouldTransToDeletion = false;
	m_bInitPos = true;
	m_bFadeIn = true;

	m_pObjIcon = NULL;
	
	SetSize( 200, OBJELHEIGHT );
	SetScheme( vgui::scheme()->LoadSchemeFromFile("resource/HL2Wars.res", "HL2Wars") );

	SetPaintBorderEnabled( false );

	pText = vgui::SETUP_PANEL(new vgui::Label(this,"textobj",""));		
	pText->SetScheme(vgui::scheme()->LoadSchemeFromFile("resource/ClientScheme.res", "ClientScheme"));	//Set it's scheme...
	pText->SetBgColor(Color(0,0,0,0));	//Translucent BG
	pText->SetPaintBackgroundEnabled( false );
	pText->SetPaintBorderEnabled( false );	
	pText->SetContentAlignment( vgui::Label::a_west );
	pText->SetVisible( false );
}

CObjectiveElement::~CObjectiveElement()
{
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectiveElement::SetText( const char *Text )
{
	pText->SetText( Text );
	pText->SetVisible( true );
	pText->SizeToContents();
	
	SetWide( pText->GetWide() + TEXTPOSX + BRCORRECTION );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectiveElement::ApplySchemeSettings( vgui::IScheme *scheme )
{
	BaseClass::ApplySchemeSettings( scheme );

	// background
	m_bgColor = GetSchemeColor("ObjElement.BgColor", GetBgColor(), scheme);
	m_borderColor = scheme->GetColor( "ObjElement.BrColor", Color( 0, 0, 0, 0 ) );

	SetBgColor( Color(0, 0, 0, 0) );
	SetBorder( scheme->GetBorder( "BaseBorder" ) );

	// set fonts and colors or labels
	HFont font = scheme->GetFont("Default", IsProportional() );
	pText->SetFont( font );
	pText->SetFgColor(GetSchemeColor("ObjElement.TextColor", scheme));
	pText->SetPos( TEXTPOSX, TEXTPOSY );
	pText->MoveToFront();

	// set objective icon
	m_pObjIcon = new CHudTexture();
	m_pObjIcon = gHUD.GetIcon("objectivebig_icon");
	m_clrObjIcon = Color( 255, 255, 255, 255 );

	SetAlpha( 0 );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectiveElement::MarkForDeletion( )
{
	m_bShouldTransToDeletion = true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectiveElement::Paint()
{
	BaseClass::Paint();

	PaintBorder();	// paint border should be behind the obj icon

	if( !m_pObjIcon )
		return;

	m_pObjIcon->DrawSelf( 0, 0,OBJELHEIGHT, OBJELHEIGHT, m_clrObjIcon );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectiveElement::OnThink()
{
	if( m_bShouldTransToDeletion )
	{
		SetAlpha( GetAlpha() -1 );

		if( GetAlpha() == 0 )
		{
			m_bShouldBeDeleted = true;
		}
	}
	else if( m_bFadeIn )
	{
		SetAlpha( GetAlpha() + 1 );

		if( GetAlpha() == 255 )
		{
			m_bFadeIn = false;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Paint background for rounded corners
//-----------------------------------------------------------------------------
void CObjectiveElement::PaintBackground()
{
	int x1, x2, y1, y2;
	surface()->DrawSetColor(m_bgColor);
	surface()->DrawSetTextColor(m_bgColor);

	int wide, tall;
	GetSize( wide, tall );
	tall -= 2 * BRCORRECTION;

	int i;

	// top-right corner -------------------------------------------------------
	int xDir = 1;
	int yDir = -1;
	int xIndex = 0;
	int yIndex = NumSegments - 1;
	int x = wide;
	int y = BRCORRECTION;
	int xMult = -1;
	int yMult = 1;
	for ( i=0; i<NumSegments; ++i )
	{
		x1 = min( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult );
		x2 = max( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult );
		y1 = max( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult );
		y2 = y + coord[NumSegments];
		surface()->DrawFilledRect( x1, y1, x2, y2 );
		xIndex += xDir;
		yIndex += yDir;
	}

	// bottom-right corner ----------------------------------------------------
	xDir = 1;
	yDir = -1;
	xIndex = 0;
	yIndex = NumSegments - 1;
	x = wide;
	y = tall + BRCORRECTION;
	xMult = -1;
	yMult = -1;
	for ( i=0; i<NumSegments; ++i )
	{
		x1 = min( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult );
		x2 = max( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult );
		y1 = y - coord[NumSegments];
		y2 = min( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult );
		surface()->DrawFilledRect( x1, y1, x2, y2 );
		xIndex += xDir;
		yIndex += yDir;
	}

	// paint between top left and bottom left ---------------------------------
	x1 = 0;
	x2 = coord[NumSegments];
	y1 = coord[NumSegments] + BRCORRECTION;
	y2 = tall + BRCORRECTION - coord[NumSegments];
	surface()->DrawFilledRect( x1, y1, x2, y2 );

	// paint between left and right -------------------------------------------
	x1 = coord[NumSegments];
	x2 = wide - coord[NumSegments];
	y1 = BRCORRECTION;
	y2 = tall + BRCORRECTION;
	surface()->DrawFilledRect( x1, y1, x2, y2 );
	
	// paint between top right and bottom right -------------------------------
	x1 = wide - coord[NumSegments];
	x2 = wide;
	y1 = coord[NumSegments] + BRCORRECTION;
	y2 = tall + BRCORRECTION - coord[NumSegments];
	surface()->DrawFilledRect( x1, y1, x2, y2 );
}

//-----------------------------------------------------------------------------
// Purpose: Paint border for rounded corners
//-----------------------------------------------------------------------------
void CObjectiveElement::PaintBorder()
{
	int x1, x2, y1, y2;
	surface()->DrawSetColor(m_borderColor);
	surface()->DrawSetTextColor(m_borderColor);

	int wide, tall;
	GetSize( wide, tall );
	tall -= 2 * BRCORRECTION;

	int i;

	// top-right corner -------------------------------------------------------
	int xDir = 1;
	int yDir = -1;
	int xIndex = 0;
	int yIndex = NumSegments - 1;
	int x = wide;
	int y = BRCORRECTION;
	int xMult = -1;
	int yMult = 1;
	for ( i=0; i<NumSegments; ++i )
	{
		x1 = min( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult );
		x2 = max( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult );
		y1 = min( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult );
		y2 = max( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult );
		surface()->DrawFilledRect( x1, y1, x2, y2 );
		xIndex += xDir;
		yIndex += yDir;
	}

	// bottom-right corner ----------------------------------------------------
	xDir = 1;
	yDir = -1;
	xIndex = 0;
	yIndex = NumSegments - 1;
	x = wide;
	y = tall + BRCORRECTION;
	xMult = -1;
	yMult = -1;
	for ( i=0; i<NumSegments; ++i )
	{
		x1 = min( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult );
		x2 = max( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult );
		y1 = min( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult );
		y2 = max( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult );
		surface()->DrawFilledRect( x1, y1, x2, y2 );
		xIndex += xDir;
		yIndex += yDir;
	}

	// top --------------------------------------------------------------------
	x1 = coord[NumSegments];
	x2 = wide - coord[NumSegments];
	y1 = BRCORRECTION;
	y2 = 1 + BRCORRECTION;
	surface()->DrawFilledRect( x1, y1, x2, y2 );

	// bottom -----------------------------------------------------------------
	x1 = coord[NumSegments];
	x2 = wide - coord[NumSegments];
	y1 = tall + BRCORRECTION - 1;
	y2 = tall + BRCORRECTION;
	surface()->DrawFilledRect( x1, y1, x2, y2 );

	// left -------------------------------------------------------------------
	x1 = 0;
	x2 = 1;
	y1 = coord[NumSegments] + BRCORRECTION;
	y2 = tall + BRCORRECTION - coord[NumSegments];
	surface()->DrawFilledRect( x1, y1, x2, y2 );

	// right ------------------------------------------------------------------
	x1 = wide - 1;
	x2 = wide;
	y1 = coord[NumSegments] + BRCORRECTION;
	y2 = tall + BRCORRECTION - coord[NumSegments];
	surface()->DrawFilledRect( x1, y1, x2, y2 );
}

// ##################################################################################
//	>> The actual class. Controls and create the objective elements. 
//
// ##################################################################################

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CHudObjectives::CHudObjectives( const char *pElementName ) :
  CHudElement( pElementName ), BaseClass( NULL, "HudObjectives" )
{
	vgui::Panel *pParent = g_pClientMode->GetViewport();
	SetParent( pParent );

	SetHiddenBits( HIDEHUD_RTS | HIDEHUD_PLAYERDEAD );

	SetScheme( vgui::scheme()->LoadSchemeFromFile("resource/HL2Wars.res", "HL2Wars") );

	gameeventmanager->AddListener( this, "game_newmap", false );
	gameeventmanager->AddListener( this, "objective_updated", false );

	SetPaintBackgroundEnabled( false );
	SetPaintBorderEnabled( false );

	m_iNumObjectives = 0;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CHudObjectives::~CHudObjectives()
{
	gameeventmanager->RemoveListener(this);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudObjectives::ApplySchemeSettings( vgui::IScheme *scheme )
{
	BaseClass::ApplySchemeSettings( scheme );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudObjectives::Paint()
{
	BaseClass::Paint();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudObjectives::Reset()
{
	/*Msg("Reset objectives\n");
	for( int i = 0; i < m_utlObjElements.Count(); i++ )
	{
		if( m_utlObjElements.Element( i ) )
		{
			m_utlObjElements.Element( i )->SetVisible( false );
			m_utlObjElements.Element( i )->SetShouldBeVisible( false );
		}
	}*/
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudObjectives::LevelShutdown( void )
{
	for( int i = 0; i < m_utlObjElements.Count(); i++ )
	{
		if( m_utlObjElements.Element( i ) )
		{
			m_utlObjElements.Element( i )->SetVisible( false );
			m_utlObjElements.Element( i )->SetShouldBeVisible( false );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudObjectives::OnThink()
{
	int counter = 0;
	for( int i = 0; i < m_utlObjElements.Count(); i++ )
	{
		if( m_utlObjElements.Element( i ) && m_utlObjElements.Element( i )->ShouldBeVisible() )
		{
			if( m_utlObjElements.Element( i )->ShouldBeDeleted() )
			{
				m_utlObjElements.Element( i )->SetShouldBeVisible( false );	
				m_utlObjElements.Element( i )->SetVisible( false );
			}
			else if( counter > 3 )
			{
				m_utlObjElements.Element( i )->SetVisible( false );
			}
			else if( m_utlObjElements.Element( i )->GetInitPos() )
			{
				m_utlObjElements.Element( i )->SetPos( 0, ( OBJELHEIGHT * counter ) + ( BRCORRECTION * counter ) );		
				m_utlObjElements.Element( i )->SetInitPos( false );
			}
			else 
			{
				m_utlObjElements.Element( i )->SetVisible( true );
				int x, y;
				m_utlObjElements.Element( i )->GetPos( x, y );
				if(  y > (OBJELHEIGHT * counter) + ( BRCORRECTION * counter ) )
				{
					m_utlObjElements.Element( i )->SetPos( 0, y - 1 );
				}
				else if( y < (OBJELHEIGHT * counter) + ( BRCORRECTION * counter ) )
				{
					m_utlObjElements.Element( i )->SetPos( 0, y + 1 );
				}
			}

			counter++;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Game event
//-----------------------------------------------------------------------------
void CHudObjectives::FireGameEvent( IGameEvent *event )
{
	const char * type = event->GetName();

	if ( Q_strcmp(type, "game_newmap") == 0 )
	{
		LoadObjectiveData( event->GetString("mapname") );
	}
	else if( Q_strcmp(type, "objective_updated") == 0 )
	{
		int objective = event->GetInt("objectiveID");
		switch( event->GetInt("type") )
		{
		case OBJECTIVE_COMPLETED:
		case OBJECTIVE_FAILED:
		case OBJECTIVE_HIDE:
			if( !m_utlObjElements.IsValidIndex( objective ) )
				return;

			m_utlObjElements.Element( objective )->MarkForDeletion();
			break;
		case OBJECTIVE_SETVISIBLE:
			{
			if( !m_utlObjElements.IsValidIndex( objective ) )
				return;

			m_utlObjElements.Element( objective )->SetShouldBeVisible( true );
			break;
			}
		default:
			break;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: load the map description and objectives from the script file
//-----------------------------------------------------------------------------
void CHudObjectives::LoadObjectiveData( const char * levelname )
{
  	// load new KeyValues
	if ( m_ObjectivesKeyValues && Q_strcmp( levelname, m_ObjectivesKeyValues->GetName() ) == 0 )
	{
		return;	// map didn't change
	}

	if ( m_ObjectivesKeyValues )
		m_ObjectivesKeyValues->deleteThis();

	m_ObjectivesKeyValues = new KeyValues( levelname );

	char tempfile[MAX_PATH];
	Q_snprintf( tempfile, sizeof( tempfile ), "resource/maps/%s.txt", levelname );
	
	if ( !m_ObjectivesKeyValues->LoadFromFile( filesystem, tempfile, "GAME" ) )
	{
		DevMsg( 1, "Error! CRTSObjectiveDialog::CreateObjectives: couldn't load file %s.\n", tempfile );
		m_iNumObjectives = 0;
		return;
	}

	// find out the amount of objectives
	m_iNumObjectives = m_ObjectivesKeyValues->GetInt("num_objectives", -1);

	for( int i = 0; i < m_iNumObjectives; i++ )
	{
		char temp[MAX_PATH];
		Q_snprintf( temp, sizeof( temp ), "objective_%d_short", i );

		CObjectiveElement *pEl = new CObjectiveElement();
		pEl->SetParent( this );
		pEl->SetText( m_ObjectivesKeyValues->GetString(temp) );
		pEl->SetVisible( false );
		pEl->SetShouldBeVisible( false );
		m_utlObjElements.AddToTail( pEl );		
	}

	Reset();
}

