/*
This file is part of Renoir, a modern graphics library.

Copyright (c) 2012-2016 Coherent Labs AD and/or its licensors. All
rights reserved in all media.

The coded instructions, statements, computer programs, and/or related
material (collectively the "Data") in these files contain confidential
and unpublished information proprietary Coherent Labs and/or its
licensors, which is protected by United States of America federal
copyright law and by international treaties.

This software or source code is supplied under the terms of a license
agreement and nondisclosure agreement with Coherent Labs AD and may
not be copied, disclosed, or exploited except in accordance with the
terms of that agreement. The Data may not be disclosed or distributed to
third parties, in whole or in part, without the prior written consent of
Coherent Labs AD.

COHERENT LABS MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS
SOURCE CODE FOR ANY PURPOSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER, ITS AFFILIATES,
PARENT COMPANIES, LICENSORS, SUPPLIERS, OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OR PERFORMANCE OF THIS SOFTWARE OR SOURCE CODE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#define SAMPLE2D(tex, uv) tex.Sample(TextureSampler0, uv)
#define SAMPLE2D_LOD(tex, uv) tex.Sample(TextureSampler0, uv.xy)
#define COH_A8_SAMPLE_MASK .r

void ShadeGeometry(in PS_Input input, inout float4 outColor, inout float alpha)
{
	// Rect/stroke rect
	if(SHADER_TYPE == 0)
	{
		alpha = min(1.0, input.Additional.z * input.Additional.w);
	}
	// Circle / Rounded rect
	else if (SHADER_TYPE == 1)
	{
		const float2 posPixels = input.ScreenNormalPosition.xy;
		const float distance2edge = length(posPixels - input.Additional.xy) - input.Additional.z;
		alpha = clamp(0.5 - distance2edge, 0, 1);
	}
	// Stroke Circle / Rounded rect
	else if (SHADER_TYPE == 2)
	{
		const float2 posPixels = input.ScreenNormalPosition.xy;
		const float de = length(posPixels - input.Additional.xy);
		const float distance2OuterEdge = de - (input.Additional.z + input.Additional.w / 2);
		const float distance2InnerEdge = de - (input.Additional.z - input.Additional.w / 2);
		alpha = clamp(0.5 - distance2OuterEdge, 0, 1);
		alpha *= 1 - clamp(0.5 - distance2InnerEdge, 0, 1);
	}
	// Image
	else if (SHADER_TYPE == 3)
	{
		outColor = SAMPLE2D(TEXTURE_0, input.Additional.xy);
		alpha = input.Color.a * saturate(input.Additional.z);
	}

	// Ellipse
	else if (SHADER_TYPE == 4)
	{
		const float2 offset = (input.ScreenNormalPosition.xy - input.Additional.xy) * rcp(input.Additional.zw);
		const float test = dot(offset, offset) - 1.0;
		const float2 dudx = ddx(offset);
		const float2 dudy = ddy(offset);
		const float2 gradient = float2(2.0 * offset.x * dudx.x + 2.0 * offset.y * dudx.y,
										2.0 * offset.x * dudy.x + 2.0 * offset.y * dudy.y);
		const float grad_dot = max(dot(gradient, gradient), 1.0e-4);
		const float invlen = rsqrt(grad_dot);

		alpha = saturate(0.5 - test * invlen);
	}

	// Stroke Ellipse
	else if (SHADER_TYPE == 5)
	{
		float2 offset = (input.ScreenNormalPosition.xy - input.Additional.xy) * rcp(input.Additional.zw + (PRIM_PROPS_0.x / 2.0));
		float test = dot(offset, offset) - 1.0;
		float2 dudx = ddx(offset);
		float2 dudy = ddy(offset);
		float2 gradient = float2(2.0 * offset.x * dudx.x + 2.0 * offset.y * dudx.y,
								2.0 * offset.x * dudy.x + 2.0 * offset.y * dudy.y);
		float grad_dot = max(dot(gradient, gradient), 1.0e-4);
		float invlen = rsqrt(grad_dot);

		alpha = saturate(0.5 - test * invlen);

		offset = (input.ScreenNormalPosition.xy - input.Additional.xy) * rcp((input.Additional.zw - (PRIM_PROPS_0.x / 2.0)));
		test = dot(offset, offset) - 1.0;
		dudx = ddx(offset);
		dudy = ddy(offset);
		gradient = float2(2.0 * offset.x * dudx.x + 2.0 * offset.y * dudx.y,
						2.0 * offset.x * dudy.x + 2.0 * offset.y * dudy.y);
		grad_dot = max(dot(gradient, gradient), 1.0e-4);
		invlen = rsqrt(grad_dot);

		alpha *= saturate(0.5 + test * invlen);
	}

	// Blur
	else if (SHADER_TYPE == 6)
	{
		outColor = float4(0, 0, 0, 0);
#if !defined(__DX9__)
		const uint stepsCount = uint(PRIM_PROPS_0.x);
		for (uint i = 0; i < stepsCount; ++i) {
#else
		for (int i = 0; i < PRIM_PROPS_0.x; ++i) {
#endif
			float coeff = COEFFICIENTS[i / 4][i % 4];
			float2 offset;
			offset.x = PIXEL_OFFSETS[(i*2) / 4][(i*2) % 4];
			offset.y = PIXEL_OFFSETS[(i*2 + 1) / 4][(i*2 + 1) % 4];
			
			//float coeff = COEFFICIENTS[i];
			//float2 offset = PIXEL_OFFSETS[i];

			outColor += coeff
				* (SAMPLE2D_LOD(TEXTURE_0, float4(input.Additional.xy + offset, 0, 0))
				+ SAMPLE2D_LOD(TEXTURE_0, float4(input.Additional.xy - offset, 0, 0)));
		}
		alpha = input.Color.a;
	}

	// Image with color matrix
	else if (SHADER_TYPE == 7)
	{
		float4 baseColor = SAMPLE2D(TEXTURE_0, input.Additional.xy);
		float nonZeroAlpha = max(baseColor.a, 0.00001);
		baseColor = float4(baseColor.rgb / nonZeroAlpha, nonZeroAlpha);

		// TODO: Rename the members of the constant buffer so they are not weird for non-blurs
		outColor.r = dot(baseColor, COEFFICIENTS[0]);
		outColor.g = dot(baseColor, COEFFICIENTS[1]);
		outColor.b = dot(baseColor, COEFFICIENTS[2]);
		outColor.a = dot(baseColor, PIXEL_OFFSETS[0]);
		outColor += PIXEL_OFFSETS[1];

		//outColor.r = dot(baseColor, float4(COEFFICIENTS[0], COEFFICIENTS[1], COEFFICIENTS[2], COEFFICIENTS[3]));
		//outColor.g = dot(baseColor, float4(COEFFICIENTS[4], COEFFICIENTS[5], COEFFICIENTS[6], COEFFICIENTS[7]));
		//outColor.b = dot(baseColor, float4(COEFFICIENTS[8], COEFFICIENTS[9], COEFFICIENTS[10], COEFFICIENTS[11]));
		//outColor.a = dot(baseColor, float4(PIXEL_OFFSETS[0].x, PIXEL_OFFSETS[0].y, PIXEL_OFFSETS[1].x, PIXEL_OFFSETS[1].y));
		//outColor += float4(PIXEL_OFFSETS[2].x, PIXEL_OFFSETS[2].y, PIXEL_OFFSETS[3].x, PIXEL_OFFSETS[3].y);

		alpha = outColor.a * input.Color.a;
		outColor.a = 1;
	}
	// YUV2RGB (9) YUVA2RGB (12)
	else if (SHADER_TYPE == 9 || SHADER_TYPE == 12)
	{
		float3 YCbCr;
		YCbCr.x = SAMPLE2D(TEXTURE_0, input.Additional.xy) COH_A8_SAMPLE_MASK;
		YCbCr.y = SAMPLE2D(TEXTURE_1, input.Additional.xy) COH_A8_SAMPLE_MASK;
		YCbCr.z = SAMPLE2D(TEXTURE_2, input.Additional.xy) COH_A8_SAMPLE_MASK;

		YCbCr -= float3(0.0625, 0.5, 0.5);
		float3x3 yuv2rgb = float3x3(float3(1.164, 0, 1.596), float3(1.164, -0.391, -0.813), float3(1.164, 2.018, 0));
		float3 rgb = mul(yuv2rgb, YCbCr);

		alpha = input.Color.a;
		outColor = float4(rgb, 1.0f);

		if (SHADER_TYPE == 12)
		{
			float a = SAMPLE2D(TEXTURE_3, input.Additional.xy) COH_A8_SAMPLE_MASK;
			alpha *= a;
		}
	}
	// Hairline
	else if(SHADER_TYPE == 11)
	{
		const float3 posPixels = float3(input.ScreenNormalPosition.xy, 1);
		const float distance2line = abs(dot(input.Additional.xyz, posPixels));
		alpha = 1 - saturate(distance2line);
	}
}

