//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//

#include "cbase.h"
#include "game_controls/slideshowpanel.h"
#include "vgui/IVGui.h"
#include "filesystem.h"
#include "fmtstr.h"

//-----------------------------------------------------------------------------

using namespace vgui;

//-----------------------------------------------------------------------------

DECLARE_BUILD_FACTORY( CCrossfadableImagePanel );
DECLARE_BUILD_FACTORY( CSlideshowPanel );

//-----------------------------------------------------------------------------

CCrossfadableImagePanel::CCrossfadableImagePanel( Panel* pParent, const char *pName )
:	EditablePanel( pParent, pName ),
	m_iSrcImg( 0 ),
	m_flBlend( 0.0f ),
	m_flBlendTime( 0.0f ),
	m_flStartBlendTime( 0.0f ),
	m_bBlending( false )
{
	m_pImages[ 0 ] = new ImagePanel( this, "Image0" );
	m_pImages[ 1 ] = new ImagePanel( this, "Image1" );

	ivgui()->AddTickSignal( GetVPanel(), 10 );
}

CCrossfadableImagePanel::~CCrossfadableImagePanel()
{
	ivgui()->RemoveTickSignal( GetVPanel() );
}


void CCrossfadableImagePanel::ApplySettings( KeyValues *pInResourceData )
{
	BaseClass::ApplySettings( pInResourceData );
}

void CCrossfadableImagePanel::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	SrcImg()->SetTileImage( false );
	DstImg()->SetTileImage( false );
}

void CCrossfadableImagePanel::PerformLayout()
{
	BaseClass::PerformLayout();

	for ( int i = 0; i < 2; ++i )
	{
		m_pImages[ i ]->SetBounds( 0, 0, GetWide(), GetTall() );
		m_pImages[ i ]->SetVisible( true );
	}
}

// Helper macro to perform a call on both images
#define CALL_FUNC_ON_BOTH_IMAGES( _call )	\
	AssertMsg( m_pImages[ 0 ], "m_pImages[ 0 ] is NULL!" ); \
	AssertMsg( m_pImages[ 1 ], "m_pImages[ 1 ] is NULL!" ); \
	m_pImages[0]->_call; \
	m_pImages[1]->_call;

void CCrossfadableImagePanel::SetShouldScaleImage( bool bState )
{
	CALL_FUNC_ON_BOTH_IMAGES( SetShouldScaleImage( bState ) );
}

void CCrossfadableImagePanel::SetScaleAmount( float flScale )
{
	CALL_FUNC_ON_BOTH_IMAGES( SetScaleAmount( flScale ) );
}

void CCrossfadableImagePanel::SetFillColor( Color c )
{
	CALL_FUNC_ON_BOTH_IMAGES( SetFillColor( c ) );
}

void CCrossfadableImagePanel::SetDrawColor( Color clrDrawColor )
{
	CALL_FUNC_ON_BOTH_IMAGES( SetDrawColor( clrDrawColor ) );
}

void CCrossfadableImagePanel::InstallMouseHandler( Panel *pHandler )
{
	CALL_FUNC_ON_BOTH_IMAGES( InstallMouseHandler( pHandler ) );
}

IImage *CCrossfadableImagePanel::GetImage()
{
	return SrcImg()->GetImage();
}

const char *CCrossfadableImagePanel::GetImageName()
{
	return SrcImg()->GetImageName();
}

float CCrossfadableImagePanel::GetScaleAmount()
{
	return SrcImg()->GetScaleAmount();
}

bool CCrossfadableImagePanel::GetShouldScaleImage()
{
	return SrcImg()->GetShouldScaleImage();
}

Color CCrossfadableImagePanel::GetFillColor()
{
	return SrcImg()->GetFillColor();
}

Color CCrossfadableImagePanel::GetDrawColor()
{
	return SrcImg()->GetDrawColor();
}

void CCrossfadableImagePanel::SetImage( IImage *pImage, float flBlendTime/*=0.0f*/ )
{
	DstImg()->SetImage( pImage );
	SetupImageBlend( flBlendTime );
}

void CCrossfadableImagePanel::SetImage( const char *pImageName, float flBlendTime/*=0.0f*/ )
{
	DstImg()->SetImage( pImageName );
	SetupImageBlend( flBlendTime );
}

void CCrossfadableImagePanel::SetupImageBlend( float flBlendTime )
{
	m_bBlending = true;
	m_flBlendTime = flBlendTime;
	m_flStartBlendTime = gpGlobals->realtime;
	m_flBlend = 0.0f;
}

void CCrossfadableImagePanel::OnSizeChanged( int nWide, int nTall )
{
	m_pImages[ 0 ]->SetSize( nWide, nTall );
	m_pImages[ 1 ]->SetSize( nWide, nTall );
}

void CCrossfadableImagePanel::OnTick()
{
	if ( m_bBlending )
	{
		// Compute current blend value
		if ( m_flBlendTime == 0.0f )
		{
			m_flBlend = 1.0f;
		}
		else
		{
			float t = clamp( ( gpGlobals->realtime - m_flStartBlendTime ) / m_flBlendTime, 0.0f, 1.0f );
			m_flBlend = clamp( t * t * (3 - 2*t), 0.0f, 1.0f );	// S-curve
		}

		// Set alpha channel on source image
		Color clrDraw;
		clrDraw = SrcImg()->GetDrawColor();
		clrDraw[ 3 ] = ( int )( 255 * ( 1 - m_flBlend ) );
		SrcImg()->SetDrawColor( clrDraw );

		// Set alpha channel on destination image
		clrDraw = DstImg()->GetDrawColor();
		clrDraw[ 3 ] = ( int )( 255 * m_flBlend );
		DstImg()->SetDrawColor( clrDraw );

		// If we're done, don't blend next think
		if ( m_flBlend >= 1.0f )
		{
			m_bBlending = false;
			m_iSrcImg = !m_iSrcImg;
			m_flBlend = 0.0f;
		}
	}
}

//-----------------------------------------------------------------------------

CSlideshowPanel::CSlideshowPanel( Panel *pParent, const char *pName )
:	EditablePanel( pParent, pName ),
	m_iCurImg( 0 ),
	m_flInterval( 3.0f ),
	m_flTransitionLength( 0.5f )
{
	m_pImagePanel = new CCrossfadableImagePanel( this, "CrossfadableImage" );

	// Setup the first transition time
	UpdateNextTransitionTime();

	// Add tick signal
	ivgui()->AddTickSignal( GetVPanel(), 10 );
}

CSlideshowPanel::~CSlideshowPanel()
{
	// Remove tick signal
	ivgui()->RemoveTickSignal( GetVPanel() );
}

void CSlideshowPanel::SetInterval( float flInterval )
{
	m_flInterval = flInterval;
	UpdateNextTransitionTime();
}

void CSlideshowPanel::SetTransitionTime( float flTransitionLength )
{
	m_flTransitionLength = flTransitionLength;
	UpdateNextTransitionTime();
}

void CSlideshowPanel::UpdateNextTransitionTime()
{
	m_flNextTransitionTime = gpGlobals->realtime + m_flInterval;
}

void CSlideshowPanel::AddImage( const char *pImageName )
{
	if ( pImageName && strlen( pImageName ) > 0 )
	{
		AddImage( scheme()->GetImage( pImageName, m_pImagePanel->GetShouldScaleImage() ) );
	}
}

void CSlideshowPanel::AddImage( IImage *pImage )
{
	// Cache a pointer to the image
	m_vecImages.AddToTail( pImage );

	if ( m_vecImages.Count() == 1 )
	{
		GetImagePanel()->SetImage( pImage );
	}
}

void CSlideshowPanel::FillWithImages( const char *pBasePath )
{
	int i = 0;
	while ( 1 )
	{
		CFmtStr fmtImagePath( "materials/vgui/%s%i.vmt", pBasePath, i );
		V_FixDoubleSlashes( fmtImagePath.Access() );
		if ( !g_pFullFileSystem->FileExists( fmtImagePath.Access() ) )
			break;
		
		fmtImagePath.sprintf( "%s%i.vmt", pBasePath, i );
		AddImage( fmtImagePath.Access() );

		++i;
	}
}

void CSlideshowPanel::ApplySettings( KeyValues *pInResourceData )
{
	BaseClass::ApplySettings( pInResourceData );

	int iDefaultImage = pInResourceData->GetInt( "default_index", 0 ); 

	int i = 0;
	while ( 1 )
	{
		CFmtStr fmtImageKeyName( "image_%i", i );
		const char *pImagePath = pInResourceData->GetString( fmtImageKeyName.Access(), NULL );
		if ( !pImagePath )
			break;

		AddImage( pImagePath );

		if ( iDefaultImage == i )
		{
			GetImagePanel()->SetImage( pImagePath );
		}

		++i;
	}

	GetImagePanel()->SetSize(
		XRES( pInResourceData->GetInt( "wide" ) ),
		YRES( pInResourceData->GetInt( "tall" ) )
	);

	GetImagePanel()->SetShouldScaleImage( pInResourceData->GetBool( "scaleImage" ) );
	GetImagePanel()->SetScaleAmount( pInResourceData->GetFloat( "scaleAmount" ) );
}

void CSlideshowPanel::OnSizeChanged( int nWide, int nTall )
{
	GetImagePanel()->SetSize( nWide, nTall );
}

void CSlideshowPanel::OnTick()
{
	if ( GetImageCount() > 1 && gpGlobals->realtime >= m_flNextTransitionTime )
	{
		// Iterate to next image
		m_iCurImg = ( m_iCurImg + 1 ) % GetImageCount();

		// Setup new image
		GetImagePanel()->SetImage( m_vecImages[ m_iCurImg ], m_flTransitionLength );

		// Set transition time to be the end of the blend
		m_flNextTransitionTime = gpGlobals->realtime + m_flInterval;
	}
}