//======= Copyright (c) 1996-2009, Valve Corporation, All rights reserved. ======
//
// CSheetSequencePanel - Panel for selecting one sequence from a sprite sheet
//
//===============================================================================

#include "matsys_controls/sheetsequencepanel.h"

#include "matsys_controls/matsyscontrols.h"
#include "VGuiMatSurface/IMatSystemSurface.h"
#include "materialsystem/imaterial.h"
#include "tier1/keyvalues.h"
#include "bitmap/psheet.h"
#include "vgui/IScheme.h"
#include "vgui/IVGui.h"
#include "materialsystem/imaterialvar.h"

// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"

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

// MOC_TODO: Power of two FB texture - do I really need to do this?
static CTextureReference s_pPowerOfTwoFrameBufferTexture_SheetSeq;

static ITexture *GetPowerOfTwoFrameBufferTexture( void )
{
	if ( !s_pPowerOfTwoFrameBufferTexture_SheetSeq )
	{
		s_pPowerOfTwoFrameBufferTexture_SheetSeq.Init( vgui::MaterialSystem()->FindTexture( "_rt_PowerOfTwoFB", TEXTURE_GROUP_RENDER_TARGET ) );
	}

	return s_pPowerOfTwoFrameBufferTexture_SheetSeq;
}

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

// Statics
bool CSheetSequencePanel::m_sMaterialsInitialized = false;
CMaterialReference CSheetSequencePanel::m_sColorMat;
CMaterialReference CSheetSequencePanel::m_sAlphaMat;


const int SEQUENCE_PANEL_BORDER = 2;
const int SEQUENCE_PANEL_MAX_SIZE = 256;

CSheetSequencePanel::CSheetSequencePanel( vgui::Panel *pParent, const char *pPanelName ):
	BaseClass(pParent,pPanelName),
	m_pSheet(NULL),
	m_Material(NULL)
{
	m_nHighlightedSequence = -1;
	m_bSeparateAlphaColorMaterial = false;
	m_bIsSecondSequenceView = false;

	EnsureMaterialsExist();
}


void CSheetSequencePanel::EnsureMaterialsExist()
{
	if ( !m_sMaterialsInitialized )
	{
		KeyValues *pKeyValues = new KeyValues( "DebugTextureView" );
		pKeyValues->SetString( "$basetexture", "" );
		pKeyValues->SetInt( "$ShowAlpha", 1 );
		m_sAlphaMat.Init( "SheetSequenceAlphaMaterial", pKeyValues );
		m_sAlphaMat->Refresh();

		pKeyValues = new KeyValues( "DebugTextureView" );
		pKeyValues->SetString( "$basetexture", "" );
		pKeyValues->SetInt( "$ShowAlpha", 0 );
		m_sColorMat.Init( "SheetSequenceColorMaterial", pKeyValues );
		m_sColorMat->Refresh();

		m_sMaterialsInitialized = true;
	}
}

CSheetSequencePanel::~CSheetSequencePanel()
{
	delete m_pSheet;
	m_pSheet = NULL;
}

void CSheetSequencePanel::PrepareMaterials()
{
	if ( !m_Material )
	{
		return;
	}

	m_bSeparateAlphaColorMaterial = CSheetExtended::IsMaterialSeparateAlphaColorMaterial( m_Material );

	bool bFound = false;
	IMaterialVar *pVar = m_Material->FindVar( "$basetexture", &bFound );
	if ( !pVar || !bFound || !pVar->IsDefined() )
		return;

	ITexture *pTex = pVar->GetTextureValue();
	if ( !pTex || pTex->IsError() )
		return;

	//////////////////////////////

	IMaterialVar *BaseTextureVar = m_sAlphaMat->FindVar( "$basetexture", &bFound );

	if ( !bFound || !BaseTextureVar )
		return;

	BaseTextureVar->SetTextureValue( pTex );

	//////////////////////////////

	BaseTextureVar = m_sColorMat->FindVar( "$basetexture", &bFound );

	if ( !bFound || !BaseTextureVar )
		return;

	BaseTextureVar->SetTextureValue( pTex );
}

void CSheetSequencePanel::SetSecondSequenceView( bool bIsSecondSequenceView )
{
	m_bIsSecondSequenceView = bIsSecondSequenceView;
}

void CSheetSequencePanel::SetFromMaterialName( const char* pMaterialName )
{
	if ( m_pSheet )
	{
		delete m_pSheet;
	}

	m_Material.Init( pMaterialName, "editor material" );
	m_pSheet = new CSheetExtended(m_Material);

	PrepareMaterials();
	PerformLayout();
}

void CSheetSequencePanel::SetFromMaterial( IMaterial* sourceMaterial )
{
	if ( m_pSheet )
	{
		delete m_pSheet;
	}

	m_Material.Init(sourceMaterial);
	m_pSheet = new CSheetExtended(m_Material);

	PrepareMaterials();
	PerformLayout();
}

void CSheetSequencePanel::PerformLayout()
{
	int newWidth = SequenceGridCount() * SequenceGridSquareSize() + SEQUENCE_PANEL_BORDER*2;
	int newHeight = SequenceGridRows() * SequenceGridSquareSize() + SEQUENCE_PANEL_BORDER*2;

	if ( SequenceGridCount() == 0 )
	{
		newWidth = 32;
		newHeight = 32;
	}

	SetSize( newWidth, newHeight );
	Repaint();
}

void CSheetSequencePanel::OnCursorExited()
{
	m_nHighlightedSequence = -1;
}

void CSheetSequencePanel::OnCursorMoved(int x, int y)
{
	BaseClass::OnCursorMoved(x,y);

	if ( m_pSheet == NULL || m_pSheet->GetSheetSequenceCount() == 0 )
	{
		m_nHighlightedSequence = -1;
		return;
	}

	int nGridCount = SequenceGridCount();
	int nGridSize = SequenceGridSquareSize();

	int nGridX = (x - SEQUENCE_PANEL_BORDER) / nGridSize;
	int nGridY = (y - SEQUENCE_PANEL_BORDER) / nGridSize;

	if ( nGridX >= 0 && nGridY >= 0 &&
		nGridX < nGridCount && nGridY < nGridCount )
	{
		int nSeqIndex = nGridX + nGridY*nGridCount;

		if ( nSeqIndex < m_pSheet->GetSheetSequenceCount() )
		{
			m_nHighlightedSequence = nSeqIndex;
		}
		else
		{
			m_nHighlightedSequence = -1;
		}
	}
	else
	{
		m_nHighlightedSequence = -1;
	}
}

int CSheetSequencePanel::SequenceGridCount()
{
	return m_pSheet ? Ceil2Int(sqrt((float)m_pSheet->GetSheetSequenceCount())) : 0;
}

int CSheetSequencePanel::SequenceGridSquareSize()
{
	int nGridCount = SequenceGridCount();

	if ( nGridCount == 0 )
	{
		return 0;
	}
	else
	{
		return (SEQUENCE_PANEL_MAX_SIZE / nGridCount);
	}
}

int CSheetSequencePanel::SequenceGridRows()
{
	if ( !m_pSheet )
		return 0;

	int nSequences = m_pSheet->GetSheetSequenceCount();
	int nGridCount = SequenceGridCount();

	if ( nSequences == 0 )
	{
		return 0;
	}
	else
	{
		// nSequences / nGridCount, rounded up
		return (nSequences + nGridCount - 1) / nGridCount;
	}
}

void CSheetSequencePanel::OnMouseReleased( vgui::MouseCode mouseCode )
{
	if ( m_nHighlightedSequence != -1 )
	{
		KeyValues *k = new KeyValues("SheetSequenceSelected");
		k->SetPtr("panel", this);
		k->SetInt("nSequenceNumber", m_nHighlightedSequence);
		k->SetBool("bIsSecondSequence", m_bIsSecondSequenceView );
		PostActionSignal( k );
	}

	SetVisible(false);
}

void CSheetSequencePanel::Paint( void )
{
	int x, y, w, h;
	GetSize(w, h);
	GetPos(x,y);

	vgui::surface()->DrawSetColor( Color(0,0,0,255) );
	vgui::surface()->DrawOutlinedRect( 1, 1, w-1, h-1 );

	if ( m_pSheet == NULL || !m_pSheet->ValidSheetData() )
	{
		return;
	}

	CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() );
	vgui::MatSystemSurface()->Begin3DPaint( 2, 2, w-2, h-2 );

	pRenderContext->MatrixMode( MATERIAL_PROJECTION );
	pRenderContext->PushMatrix();
	pRenderContext->LoadIdentity();
	pRenderContext->Ortho( 2, 2, w-2, h-2, -1.0f, 1.0f );
	pRenderContext->MatrixMode( MATERIAL_MODEL );
	pRenderContext->PushMatrix();
	pRenderContext->LoadIdentity();

	// Deal with refraction
	if ( m_Material->NeedsPowerOfTwoFrameBufferTexture() )
	{
		ITexture *pTexture = GetPowerOfTwoFrameBufferTexture();
		if ( pTexture && !pTexture->IsError() )
		{
			pRenderContext->CopyRenderTargetToTexture( pTexture );
			pRenderContext->SetFrameBufferCopyTexture( pTexture );
		}
	}

	Color bgColor = GetBgColor();
	pRenderContext->ClearColor4ub( bgColor.r(), bgColor.g(), bgColor.b(), 255 );
	pRenderContext->ClearBuffers( true, true );

	pRenderContext->FogMode( MATERIAL_FOG_NONE );
	pRenderContext->SetNumBoneWeights( 0 );

	bool bOverrideSpriteCard = false;
	bool bOnlyColor = false;
	bool bOnlyAlpha = false;
	if ( m_bSeparateAlphaColorMaterial )
	{
		if ( !m_bIsSecondSequenceView )
		{
			pRenderContext->Bind( m_sAlphaMat );
			bOnlyAlpha = true;
		}
		else
		{
			pRenderContext->Bind( m_sColorMat );
			bOnlyColor = true;
		}

		bOverrideSpriteCard = true;
	}
	else
	{
		pRenderContext->Bind( m_Material );
	}

	IMesh *pMesh = pRenderContext->GetDynamicMesh();

	float flAge = fmodf( Plat_FloatTime(), m_pSheet->GetSequenceTimeSpan(0) );

	int nGridCount = SequenceGridCount();
	float flGridSquareSize = SequenceGridSquareSize();
	float flOffset = 0.5f*flGridSquareSize+SEQUENCE_PANEL_BORDER;
	int nSequences = m_pSheet->GetSheetSequenceCount();

	for ( int i = 0; i < nSequences; ++i )
	{
		float x = i % nGridCount;
		float y = i / nGridCount;

		if ( bOnlyColor && !m_pSheet->SequenceHasColorData( i )
		  || bOnlyAlpha && !m_pSheet->SequenceHasAlphaData( i ) )
		{
			continue;
		}

		m_pSheet->DrawSheet( pMesh, Vector(flOffset+x*flGridSquareSize,h-(flOffset+y*flGridSquareSize),0), flGridSquareSize*0.5f, i, flAge, 750.0f, true, -1, bOverrideSpriteCard );
	}

	pRenderContext->PopMatrix();
	pRenderContext->MatrixMode( MATERIAL_PROJECTION );
	pRenderContext->PopMatrix();

	vgui::MatSystemSurface()->End3DPaint( );

	//////////////////////////////////////////////////////////////////////////

	flOffset = SEQUENCE_PANEL_BORDER;
	for ( int i = 0; i < nSequences; ++i )
	{
		float x = i % nGridCount;
		float y = i / nGridCount;

		Color drawColor = Color(0,0,0,255);

		if ( m_nHighlightedSequence == i )
		{
			drawColor = Color(255,255,255,255);
		}

		vgui::surface()->DrawSetColor(drawColor);
		vgui::surface()->DrawSetTextColor(drawColor);

		vgui::surface()->DrawOutlinedRect( flOffset+x*flGridSquareSize, flOffset+y*flGridSquareSize, flOffset+(x+1)*flGridSquareSize, flOffset+(y+1)*flGridSquareSize );

		wchar_t strBuffer[8];
		V_snwprintf( strBuffer, ARRAYSIZE( strBuffer ), L"%d", i );
		vgui::surface()->DrawSetTextFont( vgui::scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultVerySmall" ) );
		vgui::surface()->DrawSetTextPos(flOffset+x*flGridSquareSize+2, flOffset+y*flGridSquareSize+1);
		vgui::surface()->DrawUnicodeString( strBuffer );

		if ( bOnlyColor && !m_pSheet->SequenceHasColorData( i )
		  || bOnlyAlpha && !m_pSheet->SequenceHasAlphaData( i ) )
		{
			vgui::surface()->DrawSetTextColor( Color(255,0,0,255) );
			vgui::surface()->DrawSetTextPos(flOffset+(x+0.5f)*flGridSquareSize, flOffset+(y+0.5f)*flGridSquareSize+1);
			vgui::surface()->DrawUnicodeString( L"x" );
		}
	}
}