//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//===========================================================================//

#include "matsys_controls/vmtpanel.h"
#include "materialsystem/imesh.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/itexture.h"
#include "VGuiMatSurface/IMatSystemSurface.h"
#include "vgui_controls/ScrollBar.h"
#include "matsys_controls/matsyscontrols.h"
#include "vgui/IVGui.h"
#include "vgui_controls/ToolWindow.h"
#include "tier2/renderutils.h"

using namespace vgui;

//-----------------------------------------------------------------------------
// Enums
//-----------------------------------------------------------------------------
enum 
{
	SCROLLBAR_SIZE=18,  // the width of a scrollbar
	WINDOW_BORDER_WIDTH=2 // the width of the window's border
};

#define SPHERE_RADIUS 10.0f


//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CVMTPanel::CVMTPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
{
	m_bUseActualSize = true;
	m_pMaterial = NULL;

	m_pHorizontalBar = new ScrollBar( this, "HorizScrollBar", false );
	m_pHorizontalBar->AddActionSignalTarget(this);
	m_pHorizontalBar->SetVisible(false);

	m_pVerticalBar = new ScrollBar( this, "VertScrollBar", true );
	m_pVerticalBar->AddActionSignalTarget(this);
	m_pVerticalBar->SetVisible(false);

	LookAt( SPHERE_RADIUS );

	m_pLightmapTexture.Init( "//platform/materials/debug/defaultlightmap", "editor" );
	m_DefaultEnvCubemap.Init( "editor/cubemap", "editor", true );
}

CVMTPanel::~CVMTPanel()
{
	m_pLightmapTexture.Shutdown();
	m_DefaultEnvCubemap.Shutdown();
	if (m_pMaterial)
	{
		m_pMaterial->DecrementReferenceCount();
	}
}


//-----------------------------------------------------------------------------
// Scheme
//-----------------------------------------------------------------------------
void CVMTPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );
	SetBorder( pScheme->GetBorder( "MenuBorder") );
}


//-----------------------------------------------------------------------------
// Set the material to draw
//-----------------------------------------------------------------------------
void CVMTPanel::SetMaterial( IMaterial *pMaterial )
{
	if (pMaterial)
	{
		pMaterial->IncrementReferenceCount();
	}
	if (m_pMaterial)
	{
		m_pMaterial->DecrementReferenceCount();
	}
	m_pMaterial = pMaterial;
	InvalidateLayout();
}


//-----------------------------------------------------------------------------
// Set rendering mode (stretch to full screen, or use actual size)
//-----------------------------------------------------------------------------
void CVMTPanel::RenderUsingActualSize( bool bEnable )
{
	m_bUseActualSize = bEnable;
	InvalidateLayout();
}


//-----------------------------------------------------------------------------
// Purpose: relayouts out the panel after any internal changes
//-----------------------------------------------------------------------------
void CVMTPanel::PerformLayout()
{
	BaseClass::PerformLayout();
	return;

	// Get the current size, see if it's big enough to view the entire thing
	int iWidth, iHeight;
	GetSize( iWidth, iHeight );

	// In the case of stretching, just stretch to the size and blow off
	// the scrollbars. Same holds true if there's no material
	if (!m_bUseActualSize || !m_pMaterial)
	{
		m_iViewableWidth = iWidth;
		m_iViewableHeight = iHeight;
		m_pHorizontalBar->SetVisible(false);
		m_pVerticalBar->SetVisible(false);
		return;
	}

	// Check the size of the material...
	int iMaterialWidth = m_pMaterial->GetMappingWidth();
	int iMaterialHeight = m_pMaterial->GetMappingHeight();

	// Check if the scroll bars are visible
	bool bHorizScrollVisible = (iMaterialWidth > iWidth);
	bool bVertScrollVisible = (iMaterialHeight > iHeight);

	m_pHorizontalBar->SetVisible(bHorizScrollVisible);
	m_pVerticalBar->SetVisible(bVertScrollVisible);

	// Shrink the bars if both are visible
	m_iViewableWidth = bVertScrollVisible ? iWidth - SCROLLBAR_SIZE - WINDOW_BORDER_WIDTH : iWidth; 
	m_iViewableHeight = bHorizScrollVisible ? iHeight - SCROLLBAR_SIZE - WINDOW_BORDER_WIDTH : iHeight; 

	// Set the position of the horizontal bar...
	if (bHorizScrollVisible)
	{
		m_pHorizontalBar->SetPos(0, iHeight - SCROLLBAR_SIZE);
		m_pHorizontalBar->SetSize( m_iViewableWidth, SCROLLBAR_SIZE );

		m_pHorizontalBar->SetRangeWindow( m_iViewableWidth );
		m_pHorizontalBar->SetRange( 0, iMaterialWidth );	

		// FIXME: Change scroll amount based on how much is not visible?
		m_pHorizontalBar->SetButtonPressedScrollValue( 5 );
	}

	// Set the position of the vertical bar...
	if (bVertScrollVisible)
	{
		m_pVerticalBar->SetPos(iWidth - SCROLLBAR_SIZE, 0);
		m_pVerticalBar->SetSize(SCROLLBAR_SIZE, m_iViewableHeight);

		m_pVerticalBar->SetRangeWindow( m_iViewableHeight );
		m_pVerticalBar->SetRange( 0, iMaterialHeight);	
		m_pVerticalBar->SetButtonPressedScrollValue( 5 );
	}
}


//-----------------------------------------------------------------------------
// paint it stretched to the window size
//-----------------------------------------------------------------------------
void CVMTPanel::DrawStretchedToPanel( CMeshBuilder &meshBuilder )
{
	// Draw a polygon the size of the panel
	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.Position3f( 0, 0, 0 );
	meshBuilder.TexCoord2f( 0, 0, 0 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.Position3f( 0, m_iViewableHeight, 0 );
	meshBuilder.TexCoord2f( 0, 0, 1 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.Position3f( m_iViewableWidth, m_iViewableHeight, 0 );
	meshBuilder.TexCoord2f( 0, 1, 1 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.Position3f( m_iViewableWidth, 0, 0 );
	meshBuilder.TexCoord2f( 0, 0, 1 );
	meshBuilder.AdvanceVertex();
}


//-----------------------------------------------------------------------------
// paint it actual size
//-----------------------------------------------------------------------------
void CVMTPanel::DrawActualSize( CMeshBuilder &meshBuilder )
{
	// Check the size of the material...
	int iMaterialWidth = m_pMaterial->GetMappingWidth();
	int iMaterialHeight = m_pMaterial->GetMappingHeight();

	Vector2D ul;
	Vector2D lr;
	Vector2D tul;
	Vector2D tlr;

	if (m_iViewableWidth >= iMaterialWidth)
	{
		// Center the material if we've got enough horizontal space
		ul.x = (m_iViewableWidth - iMaterialWidth) * 0.5f;
		lr.x = ul.x + iMaterialWidth;
		tul.x = 0.0f; tlr.x = 1.0f;
	}
	else
	{
		// Use the scrollbars here...
		int val = m_pHorizontalBar->GetValue();
		tul.x = (float)val / (float)iMaterialWidth;
		tlr.x = tul.x + (float)m_iViewableWidth / (float)iMaterialWidth;

		ul.x = 0;
		lr.x = m_iViewableWidth;
	}

	if (m_iViewableHeight >= iMaterialHeight)
	{
		// Center the material if we've got enough vertical space
		ul.y = (m_iViewableHeight - iMaterialHeight) * 0.5f;
		lr.y = ul.y + iMaterialHeight;
		tul.y = 0.0f; tlr.y = 1.0f;
	}
	else
	{
		// Use the scrollbars here...
		int val = m_pVerticalBar->GetValue();

		tul.y = (float)val / (float)iMaterialHeight;
		tlr.y = tul.y + (float)m_iViewableHeight / (float)iMaterialHeight;

		ul.y = 0;
		lr.y = m_iViewableHeight;
	}

	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.Position3f( ul.x, ul.y, 0 );
	meshBuilder.TexCoord2f( 0, tul.x, tul.y );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.Position3f( lr.x, ul.y, 0 );
	meshBuilder.TexCoord2f( 0, tlr.x, tul.y );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.Position3f( lr.x, lr.y, 0 );
	meshBuilder.TexCoord2f( 0, tlr.x, tlr.y );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.Position3f( ul.x, lr.y, 0 );
	meshBuilder.TexCoord2f( 0, tul.x, tlr.y );
	meshBuilder.AdvanceVertex();
}


//-----------------------------------------------------------------------------
// Draw it on a sphere
//-----------------------------------------------------------------------------
void CVMTPanel::RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi )
{
	int nVertices =  nTheta * nPhi;
	int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 );
	
	CMatRenderContextPtr pRenderContext( MaterialSystem() );
	pRenderContext->FogMode( MATERIAL_FOG_NONE );
	pRenderContext->SetNumBoneWeights( 0 );
	pRenderContext->Bind( m_pMaterial );
	pRenderContext->BindLightmapTexture( m_pLightmapTexture );
	pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap );

	IMesh* pMesh = pRenderContext->GetDynamicMesh();

	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nVertices, nIndices );

	bool bIsUsingLightmap = m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP );
	bool bIsUsingBumpedLightmap = m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );

	int nLightmapWidth = m_pLightmapTexture->GetActualWidth();
	float flHalfLuxel = 0.5f / nLightmapWidth;

	//
	// Build the index buffer.
	//
	int i, j;
	for ( i = 0; i < nPhi; ++i )
	{
		for ( j = 0; j < nTheta; ++j )
		{
			float u = j / ( float )(nTheta - 1);
			float v = i / ( float )(nPhi - 1);
			float theta = ( j != nTheta-1 ) ? 2.0f * M_PI * u : 0.0f;
			float phi = M_PI * v;

			Vector vecPos;
			vecPos.x = flRadius * sin(phi) * cos(theta);
			vecPos.y = flRadius * sin(phi) * sin(theta); 
			vecPos.z = flRadius * cos(phi);
			    
			Vector vecNormal = vecPos;
			VectorNormalize( vecNormal );

			Vector4D vecTangentS;
			Vector vecTangentT;
			vecTangentS.Init( -vecPos.y, vecPos.x, 0.0f, 1.0f );
			if ( VectorNormalize( vecTangentS.AsVector3D() ) == 0.0f )
			{
				vecTangentS.Init( 1.0f, 0.0f, 0.0f, 1.0f );
			}

			CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT );

			unsigned char red = (int)( u * 255.0f );
			unsigned char green = (int)( v * 255.0f );
			unsigned char blue = (int)( v * 255.0f );
			unsigned char alpha = (int)( v * 255.0f );

			vecPos += vCenter;

			float u1, u2, v1, v2;
			u1 = u2 = u;
			v1 = v2 = v;

			if ( bIsUsingLightmap )
			{
				u1 = RemapVal( u1, 0.0f, 1.0f, flHalfLuxel, 0.25 - flHalfLuxel );

				if ( bIsUsingBumpedLightmap )
				{
					u2 = 0.25f;
					v2 = 0.0f;
				}
			}

			meshBuilder.Position3fv( vecPos.Base() );
			meshBuilder.Normal3fv( vecNormal.Base() );
			meshBuilder.Color4ub( red, green, blue, alpha );
			meshBuilder.TexCoord2f( 0, u, v );
			meshBuilder.TexCoord2f( 1, u1, v1 );
			meshBuilder.TexCoord2f( 2, u2, v2 );
			meshBuilder.TangentS3fv( vecTangentS.Base() );
			meshBuilder.TangentT3fv( vecTangentT.Base() );
			meshBuilder.UserData( vecTangentS.Base() );
			meshBuilder.AdvanceVertex();
		}
	}

	//
	// Emit the triangle strips.
	//
	int idx = 0;
	for ( i = 0; i < nPhi - 1; ++i )
	{
		for ( j = 0; j < nTheta; ++j )
		{
			idx = nTheta * i + j;

			meshBuilder.FastIndex( idx );
			meshBuilder.FastIndex( idx + nTheta );
		}

		//
		// Emit a degenerate triangle to skip to the next row without
		// a connecting triangle.
		//
		if ( i < nPhi - 2 )
		{
			meshBuilder.FastIndex( idx + 1 );
			meshBuilder.FastIndex( idx + 1 + nTheta );
		}
	}

	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
// Power of two FB texture
//-----------------------------------------------------------------------------
static CTextureReference s_pPowerOfTwoFrameBufferTexture;

static ITexture *GetPowerOfTwoFrameBufferTexture( void )
{
	if( !s_pPowerOfTwoFrameBufferTexture )
	{
		s_pPowerOfTwoFrameBufferTexture.Init( materials->FindTexture( "_rt_PowerOfTwoFB", TEXTURE_GROUP_RENDER_TARGET ) );
	}
	
	return s_pPowerOfTwoFrameBufferTexture;
}


//-----------------------------------------------------------------------------
// paint it!
//-----------------------------------------------------------------------------
void CVMTPanel::OnPaint3D()
{
	if (!m_pMaterial)
		return;

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

	// Draw a background (translucent objects will appear that way)

	// FIXME: Draw the outline of this panel?

	pRenderContext->CullMode( MATERIAL_CULLMODE_CW );

	RenderSphere( vec3_origin, SPHERE_RADIUS, 20, 20 );
	/*
	pRenderContext->MatrixMode( MATERIAL_PROJECTION );
	pRenderContext->LoadIdentity();
	pRenderContext->Ortho( 0, 0, m_iViewableWidth, m_iViewableHeight, 0, 1 );

	pRenderContext->Bind( m_pMaterial );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();
	CMeshBuilder meshBuilder;

	meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );

	if (!m_bUseActualSize)
	{
		DrawStretchedToPanel( meshBuilder );
	}
	else
	{
		DrawActualSize( meshBuilder );
	}

	meshBuilder.End();
	pMesh->Draw();
	*/
}