Editor/ShaderGraphPlus/Nodes/Variables/SceneColor.cs
using Sandbox.Rendering;

namespace ShaderGraphPlus.Nodes;

/// <summary>
/// Color of the scene.
/// </summary>
[Title( "Scene Color" ), Category( "Variables" ), Icon( "palette" )]
public sealed class SceneColorNode : ShaderNodePlus
{
	[JsonIgnore, Hide, Browsable( false )]
	public override Color NodeTitleColor => ShaderGraphPlusTheme.NodeHeaderColors.FunctionNode;

	[Hide]
	public string MapSceneColorCoords => @"
float2 MapSceneColorCoords( float2 vInput, float2 modes )
{
	float2 result;

	// X
	if ( modes.x == 1 ) // Mirror
	{
		float xx = abs( vInput.x );
		result.x = (fmod( floor( xx ), 2.0 ) == 0.0) ? frac( xx ) : 1.0 - frac( xx );
	}
	else if ( modes.x == 2 ) // Clamp
	{
		result.x = clamp( vInput.x, 0.0, 1.0 );
	}
	else if ( modes.x == 3 ) // Border
	{
		result.x = (vInput.x < 0.0 || vInput.x > 1.0) ? 0.5 : vInput.x;
	}
	else if ( modes.x == 4 ) // MirrorOnce
	{
		float xx = abs( vInput.x );
		float floorX = floor( xx );
		if ( floorX < 1.0 )
		{
			result.x = frac( xx );
		}
		else if ( floorX < 2.0 )
		{
			result.x = 1.0 - frac( xx );
		}
		else
		{
			result.x = vInput.x;
		}
	}
	else // Wrap by default
	{
		result.x = vInput.x;
	}

	// Y
	if ( modes.y == 1 ) // Mirror
	{
		float yy = abs( vInput.y );
		result.y = (fmod( floor( yy ), 2.0 ) == 0.0) ? frac( yy ) : 1.0 - frac( yy );
	}
	else if ( modes.y == 2 ) // Clamp
	{
		result.y = clamp( vInput.y, 0.0, 1.0 );
	}
	else if ( modes.y == 3 ) // Border
	{
		result.y = (vInput.y < 0.0 || vInput.y > 1.0) ? 0.5 : vInput.y;
	}
	else if ( modes.y == 4 ) // MirrorOnce
	{
		float yy = abs( vInput.y );
		float floorY = floor( yy );
		if ( floorY < 1.0 )
		{
			result.y = frac( yy );
		}
		else if ( floorY < 2.0 )
		{
			result.y = 1.0 - frac( yy );
		}
		else
		{
			result.y = vInput.y;
		}
	}
	else // Wrap by default
	{
		result.y = vInput.y;
	}

	return result;
}
";

	[Input( typeof( Vector2 ) )]
	[Hide]
	public NodeInput Coords { get; set; }

	public TextureAddressMode AddressU { get; set; } = TextureAddressMode.Wrap;
	public TextureAddressMode AddressV { get; set; } = TextureAddressMode.Wrap;

	[Output( typeof( Vector3 ) )]
	[Hide]
	public NodeResult.Func SceneColor => ( GraphCompiler compiler ) =>
	{
		var coords = compiler.Result( Coords );
		var graph = compiler.Graph;

		if ( graph.Domain != ShaderDomain.PostProcess && graph.BlendMode != BlendMode.Translucent )
		{
			return NodeResult.Error( $"Graph `{nameof( BlendMode )}` must be set to `{nameof( BlendMode.Translucent )}` in order to use `{DisplayInfo.Name}`" );
		}

		var uvModes = $"float2({(int)AddressU},{(int)AddressV})";
		var func = compiler.RegisterHLSLFunction( MapSceneColorCoords, "MapSceneColorCoords" );

		if ( graph.Domain is ShaderDomain.PostProcess )
		{
			return new NodeResult( ResultType.Vector3, $"g_tColorBuffer.Sample( g_sAniso, {(
				coords.IsValid
				? $"{compiler.ResultHLSLFunction( func, coords.Cast( 2 ), uvModes )}"
				: $"CalculateViewportUv( {compiler.ResultHLSLFunction( func, "i.vPositionSs.xy", uvModes )} )"
			)} ).rgb" );
		}

		compiler.RegisterGlobal( "bWantsFBCopyTexture", "BoolAttribute( bWantsFBCopyTexture, true );" );
		compiler.RegisterGlobal( "g_tFrameBufferCopyTexture", "Texture2D g_tFrameBufferCopyTexture < Attribute( \"FrameBufferCopyTexture\"); SrgbRead( false ); >;" );

		var sample = $"g_tFrameBufferCopyTexture.Sample( g_sAniso, {(coords.IsValid
			? $"{compiler.ResultHLSLFunction( func, coords.Cast( 2 ), uvModes )}"
			: $"CalculateViewportUv( {compiler.ResultHLSLFunction( func, "i.vPositionSs.xy", uvModes )} )")}" +
			$"{(compiler.IsPreview ? "* g_vFrameBufferCopyInvSizeAndUvScale.zw" : "")}).rgb";

		return new NodeResult( ResultType.Vector3, sample );
	};
}

[Title( "Frame Buffer Copy Inv Size And Uv Scale" ), Category( "Variables" )]
public sealed class FrameBufferCopyInvSizeAndUvScaleNode : ShaderNodePlus
{
	[JsonIgnore, Hide, Browsable( false )]
	public override Color NodeTitleColor => Color.Parse( "#803334" )!.Value;

	[Output( typeof( Vector2 ) )]
	[Hide]
	public NodeResult.Func Result => ( GraphCompiler compiler ) =>
	{
		return new NodeResult( ResultType.Vector2, $"g_vFrameBufferCopyInvSizeAndUvScale.zw" );
	};

}