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

#include "BaseVSShader.h"

#include "color_projection_ps20.inc"
#include "color_projection_vs20.inc"

#include "../materialsystem_global.h"

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

static ConVar *mat_color_projection = NULL;

typedef struct SProjectionInfo
{
	bool	m_bNeedBlindMK;
	bool	m_bNeedMonochrome;
	bool	m_bNeedAnomylize;
	float	m_flCPU;
	float	m_flCPV;
	float	m_flAM;
	float	m_flAYI;
} TProjectionInfo;

#define MAX_PROJECTIONS	8

TProjectionInfo ProjectionInfo[ MAX_PROJECTIONS ] =
{
	{ true,		false,	false,	0.735f,		0.265f,		1.273463f,	-0.073894f },		// protanopia red-green blindness (no red cones)
	{ true,		false,	false,	1.14f,		-0.14f,		0.968437f,	0.003331f },		// deutanopia red-green blindness (no green cones)
	{ true,		false,	false,	0.171f,		-0.003f,	0.062921f,	0.292119f },		// tritanopia blue-yellow blindness (no blue cones)
	{ false,	true,	false,	0.0f,		0.0f,		0.0f,		0.0f },				// typical achromatopsia (no cones; rod monochromat)
	{ true,		false,	true,	0.735f,		0.265f,		1.273463f,	-0.073894f },		// protanomaly (anomalous red cones)
	{ true,		false,	true,	1.14f,		-0.14f,		0.968437f,	0.003331f },		// deutanomaly (anomalous green cones)
	{ true,		false,	true,	0.171f,		-0.003f,	0.062921f,	0.292119f },		// tritanomaly (anomalous blue cones)
	{ false,	true,	true,	0.0f,		0.0f,		0.0f,		0.0f }				// atypical achromatopsia (low cones; cone monochromat)
};

#if 0

#define cpu									ProjectionInfo[ 2 ].m_flCPU
#define cpv									ProjectionInfo[ 2 ].m_flCPV
#define am									ProjectionInfo[ 2 ].m_flAM
#define ayi									ProjectionInfo[ 2 ].m_flAYI


Vector rgb_from_xyz( Vector vNum )
{
	Vector vResult;

	vResult.x=( 3.063218*vNum.x-1.393325*vNum.y-0.475802*vNum.z);
	vResult.y=(-0.969243*vNum.x+1.875966*vNum.y+0.041555*vNum.z);
	vResult.z=( 0.067871*vNum.x-0.228834*vNum.y+1.069251*vNum.z);

	return vResult;
}

Vector xyz_from_rgb( Vector vNum )
{
	Vector vResult;

	vResult.x=(0.430574*vNum.x+0.341550*vNum.y+0.178325*vNum.z);
	vResult.y=(0.222015*vNum.x+0.706655*vNum.y+0.071330*vNum.z);
	vResult.z=(0.020183*vNum.x+0.129553*vNum.y+0.939180*vNum.z);

	return vResult;
}

Vector anomylize( Vector a, Vector b )
{
	return ( ( 1.75f * b ) + a ) / 2.75f;
}

Vector monochrome( Vector r)
{
	float z = (r.x*0.299+r.y*0.587+r.z*0.114);

	return Vector( z, z, z );;
}


Vector blindMK( Vector vColor )
{
	const float wx=0.312713;
	const float wy=0.329016;
	const float wz=0.358271;

	Vector c_xyz = xyz_from_rgb( vColor );

	float sum_xyz=c_xyz.x+c_xyz.y+c_xyz.z;

	Vector2D c_uv;
	c_uv.x=0;
	c_uv.y=0;

	if ( sum_xyz!=0 )
	{
		c_uv.x=c_xyz.x/sum_xyz;
		c_uv.y=c_xyz.y/sum_xyz;
	}

	float nx=wx*c_xyz.y/wy;
	float nz=wz*c_xyz.y/wy;

	Vector d_xyz;
	d_xyz.y=0;

	float clm;
	if ( c_uv.x< cpu )
	{
		clm=(cpv-c_uv.y)/(cpu-c_uv.x);
	}
	else
	{
		clm=(c_uv.y-cpv)/(c_uv.x-cpu);
	}

	float clyi=c_uv.y-c_uv.x*clm;
	Vector2D d_uv;
	d_uv.x=(ayi-clyi)/(clm-am);
	d_uv.y=(clm*d_uv.x)+clyi;

	Vector s_xyz;
	s_xyz.x=d_uv.x*c_xyz.y/d_uv.y;
	s_xyz.y=c_xyz.y;
	s_xyz.z=(1-(d_uv.x+d_uv.y))*c_xyz.y/d_uv.y;

	Vector s_rgb = rgb_from_xyz( s_xyz );

	d_xyz.x=nx-s_xyz.x;
	d_xyz.z=nz-s_xyz.z;

	Vector d_rgb = rgb_from_xyz( d_xyz );

	Vector adj_rgb;
	
	adj_rgb.Init();

	if ( d_rgb.x!=0 )
	{
		adj_rgb.x=( s_rgb.x<0 ? 0 : 1)-s_rgb.x/d_rgb.x;
	}
	if ( d_rgb.y!=0 )
	{
		adj_rgb.y=( s_rgb.y<0 ? 0 : 1)-s_rgb.y/d_rgb.y;
	}
	if ( d_rgb.z!=0 )
	{
		adj_rgb.z=( s_rgb.z<0 ? 0 : 1)-s_rgb.z/d_rgb.z;
	}

	float adjust = 0;
	if ( adj_rgb.x >= 0 && adj_rgb.x <= 1 )
	{
		adjust = adj_rgb.x;
	}
	if ( adj_rgb.y >= 0 && adj_rgb.y <= 1 && adj_rgb.y > adjust )
	{
		adjust = adj_rgb.y;
	}
	if ( adj_rgb.z >= 0 && adj_rgb.z <= 1 && adj_rgb.z > adjust )
	{
		adjust = adj_rgb.z;
	}

	s_rgb.x=s_rgb.x+(adjust*d_rgb.x);
	s_rgb.y=s_rgb.y+(adjust*d_rgb.y);
	s_rgb.z=s_rgb.z+(adjust*d_rgb.z);

	return s_rgb;
}

#endif


BEGIN_VS_SHADER( color_projection, "Help for deferred color correction" )
	BEGIN_SHADER_PARAMS
		SHADER_PARAM( FRAME_TEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_FullFrameFB1", "" )

		SHADER_PARAM( HSV_CORRECTION, SHADER_PARAM_TYPE_VEC3, "[ 0.0 0.0 0.0 ]", "" )
		SHADER_PARAM( CONTRAST_CORRECTION, SHADER_PARAM_TYPE_FLOAT, "0.0", "" )

	END_SHADER_PARAMS

	SHADER_INIT_PARAMS()
	{
		SET_FLAGS2( MATERIAL_VAR2_NEEDS_FULL_FRAME_BUFFER_TEXTURE );

#if 0
		Vector vResult;
		vResult = blindMK( Vector( 1, 0, 0 ) );
		vResult = blindMK( Vector( 1, 0, 0 ) );
		vResult = blindMK( Vector( 1, 0, 0 ) );
		vResult = blindMK( Vector( 1, 0, 0 ) );
		vResult = blindMK( Vector( 1, 0, 0 ) );
		vResult = blindMK( Vector( 1, 0, 0 ) );

		Msg( "%g %g %g", vResult.x, vResult.y, vResult.z );

#endif
	}

	SHADER_FALLBACK
	{
		return 0;
	}

	SHADER_INIT
	{
		if ( mat_color_projection == NULL )
		{
			mat_color_projection = cvar->FindVar( "mat_color_projection" );
		}

		if ( params[ FRAME_TEXTURE ]->IsDefined() == false )
		{
			params[ FRAME_TEXTURE ]->SetStringValue( "_rt_FullFrameFB1" );
		}
//		params[ FRAME_TEXTURE ]->SetStringValue( "rj/colors" );
		LoadTexture( FRAME_TEXTURE );
	}

	SHADER_DRAW
	{
		SHADOW_STATE
		{
			SetInitialShadowState( );

			pShaderShadow->EnableDepthWrites( false );
			pShaderShadow->EnableDepthTest( false );
//			pShaderShadow->EnableBlending( true );
//			pShaderShadow->BlendOp( SHADER_BLEND_OP_REVSUBTRACT );
//			EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE );

			pShaderShadow->EnableTexture( SHADER_SAMPLER4, true );

			pShaderShadow->EnableSRGBRead( SHADER_SAMPLER4, false );

			pShaderShadow->EnableSRGBWrite( false );
			pShaderShadow->EnableAlphaWrites( true ); // writing water fog alpha always.

			int fmt = VERTEX_POSITION;
			int nTexCoordDims[ 2 ] = { 2, 3 };
			pShaderShadow->VertexShaderVertexFormat( fmt, 2, nTexCoordDims, 0 );

			DECLARE_STATIC_VERTEX_SHADER( color_projection_vs20 );
			SET_STATIC_VERTEX_SHADER( color_projection_vs20 );

			DECLARE_STATIC_PIXEL_SHADER( color_projection_ps20 );
			SET_STATIC_PIXEL_SHADER( color_projection_ps20 );
		}

		DYNAMIC_STATE
		{
			pShaderAPI->SetDefaultState();

			BindTexture( SHADER_SAMPLER4, FRAME_TEXTURE, -1 );

			int nIndex = mat_color_projection->GetInt() - 1;
			if ( nIndex < 0 || nIndex >= MAX_PROJECTIONS )
			{
				nIndex = 0;
			}

			Vector4D vCorrectionParms;

			vCorrectionParms.x = ProjectionInfo[ nIndex ].m_flCPU;
			vCorrectionParms.y = ProjectionInfo[ nIndex ].m_flCPV;
			vCorrectionParms.z = ProjectionInfo[ nIndex ].m_flAM;
			vCorrectionParms.w = ProjectionInfo[ nIndex ].m_flAYI;
			pShaderAPI->SetPixelShaderConstant( 1, vCorrectionParms.Base() );

			DECLARE_DYNAMIC_VERTEX_SHADER( color_projection_vs20 );
			SET_DYNAMIC_VERTEX_SHADER( color_projection_vs20 );

			DECLARE_DYNAMIC_PIXEL_SHADER( color_projection_ps20 );
			SET_DYNAMIC_PIXEL_SHADER_COMBO( NEED_BLINDMK, ProjectionInfo[ nIndex ].m_bNeedBlindMK );
			SET_DYNAMIC_PIXEL_SHADER_COMBO( NEED_MONOCHROME, ProjectionInfo[ nIndex ].m_bNeedMonochrome );
			SET_DYNAMIC_PIXEL_SHADER_COMBO( NEED_ANOMYLIZE, ProjectionInfo[ nIndex ].m_bNeedAnomylize );
			SET_DYNAMIC_PIXEL_SHADER( color_projection_ps20 );
		}
		Draw();
	}
END_SHADER