#include "/Engine/Public/Platform.ush"

#include "/Engine/Private/Common.ush"
#include "/Engine/Generated/UniformBuffers/View.ush"
//#include "SceneTexturesCommon.ush"
//DECLARE_UNIFORM_BUFFER(View);

RWTexture2D<float4> SceneColorTexture;
RWTexture2D<float4> BaseColorTexture;

Texture2D NormalTexture;
Texture2D<float> SceneDepthTexture;

Texture2D CustomDepthTexture;
Texture2D<uint2> CustomStencilTexture;

//Texture2D SceneDepthTexture : register(t0);
//SamplerState DepthSampler : register(s0);

//SamplerState InputSampler;

float3 CameraPosition;
float3 CameraForwardVector;


float UseCustomLightPosition;
float3 LightPosition;
float3 LightDirection;

float UseCustomLightColor;
float3 LightColor;
float LightScale;
float RimEdgeFade;

float UseBinaryEdge;
float RimEdgeBinaryRange;

float SampleScreenScale;
float SampleScale;

int CustomStencilMask;

// for UE5.2
#ifndef DFHackToFloat
#define DFHackToFloat LWCHackToFloat
#endif

#define GROUP_SIZE 8  // スレッドグループサイズ
//#define FILTER_RADIUS 2  // フィルター範囲 (ハロー)

// 共有メモリ: 色データ用
//groupshared float4 SharedColorTile[GROUP_SIZE + FILTER_RADIUS * 2][GROUP_SIZE + FILTER_RADIUS * 2];
// 共有メモリ: Stencilデータ用 (uintで効率化)
//groupshared uint SharedStencilTile[GROUP_SIZE + FILTER_RADIUS * 2][GROUP_SIZE + FILTER_RADIUS * 2];

float GetLinearDepth_raw(uint2 pixelCoord)
{
    float DeviceZ = SceneDepthTexture[pixelCoord];
   	return ConvertFromDeviceZ(DeviceZ);
}

float GetLinearDepth(float2 UV, uint2 pixelCoord)
{
    float DeviceZ = SceneDepthTexture[pixelCoord];
   	return ConvertFromDeviceZ(DeviceZ);
}

float3 LocalGetCameraVector(float2 UV){
    // Convert UV to NDC (Normalized Device Coordinates)
    float2 NDC_xy = UV * 2.0f - 1.0f;
    NDC_xy.y = -NDC_xy.y;  // Flip Y for DirectX coordinate system

    // Assume far plane (Z=1 in clip space for direction)
    float4 ClipSpace = float4(NDC_xy, 1.0f, 1.0f);

    // Unproject to View Space using ClipToView (inverse projection)
    float4 ViewSpace = mul(ClipSpace, View.ClipToView);
    ViewSpace /= ViewSpace.w;  // Perspective divide
    float3 ViewDir = normalize(ViewSpace.xyz);

    // Convert View direction to World space
    float3 WorldDir = mul(ViewDir, (float3x3)View.ViewToTranslatedWorld);
    WorldDir = normalize(WorldDir);
    
    return WorldDir;
}


MaterialFloat2 ViewportUVToSceneTextureUV2(MaterialFloat2 ViewportUV, const uint SceneTextureId)
{
    /*
	if (IsPostProcessInputSceneTexture(SceneTextureId))
	{
		switch (SceneTextureId)
		{
		case PPI_PostProcessInput0:
			return ViewportUV * PostProcessInput_0_UVViewportSize + PostProcessInput_0_UVViewportMin;
		case PPI_PostProcessInput1:
			return ViewportUV * PostProcessInput_1_UVViewportSize + PostProcessInput_1_UVViewportMin;
		case PPI_PostProcessInput2:
			return ViewportUV * PostProcessInput_2_UVViewportSize + PostProcessInput_2_UVViewportMin;
		case PPI_PostProcessInput3:
			return ViewportUV * PostProcessInput_3_UVViewportSize + PostProcessInput_3_UVViewportMin;
		case PPI_PostProcessInput4:
			return ViewportUV * PostProcessInput_4_UVViewportSize + PostProcessInput_4_UVViewportMin;
		default:
			return ViewportUV;
		}
	}
    */

	return ViewportUVToBufferUV(ViewportUV);
}

float2 LocalGetScreenPositionForProjectionType(float2 ScreenPosition, float SceneDepth)
{
	// For perspective projection matrix, the scene depth is required as part of the clip to view calculations
	// For orthogonal projection matrix, scene depth should not be used
	//return select(IsOrthoProjection(), ScreenPosition, ScreenPosition * SceneDepth);
	return ScreenPosition * SceneDepth;
}


float3 LocalGetWorldPositionFromScreenUV_raw(float2 ScreenUV, uint2 pixelCoord)
{
    float2 uv = ViewportUVToSceneTextureUV2((ScreenUV), 1);
    float3 depth = GetLinearDepth(ScreenUV, pixelCoord);//SceneTextureLookup(uv, 1, false).rgb;
    float2 screen =  (uv - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;

    return mul(float4(LocalGetScreenPositionForProjectionType(screen, depth.x), depth.x, 1),  DFHackToFloat(PrimaryView.ScreenToWorld)).xyz;
}

float3 LocalGetWorldPositionFromScreenUV(float2 ScreenUV)
{
    return LocalGetWorldPositionFromScreenUV_raw(ScreenUV, ScreenUV * View.ViewSizeAndInvSize.xy);
}




[numthreads(GROUP_SIZE, GROUP_SIZE, 1)]
void MainCS(uint3 DispatchThreadID : SV_DispatchThreadID)
{
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM6

    // テクスチャ全体サイズ
    uint width, height;
    SceneColorTexture.GetDimensions(width, height);

    // ビューに表示されているピクセル数：View.ViewSizeAndInvSize.xy
    const float2 ViewSize = View.ViewSizeAndInvSize.xy;

    // このスレッドで書き込み対象にしている座標
    const uint2 pixelCoord = DispatchThreadID.xy;

    // ビューに対して 0-1 で示したもの
    const float2 ScreenUV = float2(pixelCoord.x, pixelCoord.y) / View.ViewSizeAndInvSize.xy;

    // WorldPos
    const float3 WorldPosition = LocalGetWorldPositionFromScreenUV_raw(ScreenUV, pixelCoord);

    const float LinearDepth = GetLinearDepth(ScreenUV, pixelCoord);

    // 
    if ((CustomStencilTexture[pixelCoord].y & CustomStencilMask)) {
    if (SceneDepthTexture[pixelCoord] <= CustomDepthTexture[pixelCoord].r) {


        // Normal
        float3 PackedNormal = NormalTexture[pixelCoord].xyz;
        float3 WorldNormal = PackedNormal * 2.0 - 1.0; // [-1, 1]に変換

        // 光源への方向ベクトル
        //float3 LocalLightDirection = View.DirectionalLightDirection;
        float3 LocalLightDirection = LightDirection;
        
        if (UseCustomLightPosition) {
            LocalLightDirection = normalize(LightPosition - WorldPosition);
        }
        LocalLightDirection = normalize(LocalLightDirection);


        // 法線とライトを考慮してフェード            
        float NormalWeight = 1.f;
        if (dot(PackedNormal, PackedNormal) == 0.f) {
            // for unlit (no normal)
            // 常にrimを出す 1
                
        } else {
            NormalWeight = dot(WorldNormal, LocalLightDirection);
            NormalWeight = saturate(NormalWeight);

            if (UseBinaryEdge) {
                // エッジを2値にする
                NormalWeight = (NormalWeight >= RimEdgeBinaryRange) ? 1.f : 0.f;
            } else {
                // エッジ フェード
                NormalWeight = pow(NormalWeight, RimEdgeFade);
            }
        }
        
        {
            //SceneColorTexture[pixelCoord].xyz = fmod(abs(WorldPosition), 10) / 10;
            //return;
        }
    
            
        {
            float a = normalize(CameraPosition  - WorldPosition);
                
            if (dot(LocalLightDirection, a) <= 0) {
                    //NormalWeight = 0;
            }
        }

        if (NormalWeight > 0.f) {
            // 平面にて ライトベクトル
            float4 ClipLightDir = mul(float4(-LocalLightDirection, 0.0), View.RelativeWorldToClip);

            ClipLightDir.xyz = normalize(ClipLightDir.xyz);

            //float SampleRange = float(height) * SampleScreenScale;
            //float SampleRange = View.BufferSizeAndInvSize.y * SampleScreenScale;

            // サーチ距離
            float SampleRange = View.ViewSizeAndInvSize.y * SampleScreenScale;
            SampleRange = SampleRange / LinearDepth * 10.f * (3.14159265f/ 2.f / View.FieldOfViewWideAngles.x);
                
            //float2 UnscaledMin = View.UnscaledViewRect.Min.xy;
            //float2 UnscaledMax = View.UnscaledViewRect.Max.xy;
            //float2 UnscaledSize = UnscaledMax - UnscaledMin;  // フル幅/高さ (pixels)
            //float SampleRange = UnscaledSize.y * SampleScreenScale;
                
            bool bEdge = false;
                
            float LocalSampleScale = max(SampleScale, 1.0);
                
            float EdgeColorScale = 1.0f;
            {
                float fade = 0.01f;
                float2 OutArea = float2(width, height) * fade;
                    
                // エッジサーチ先の座標
                //float2 checkEnd = float2(pixelCoord) + SampleRange;
                float2 checkEnd = (ClipLightDir.xy * float2(SampleRange,SampleRange)) + pixelCoord;

                // 左上のはみ出し
                float2 out1 = abs( min(float2(0,0), checkEnd - OutArea));

                // 右下のはみ出し
                float2 out2 = abs(max(float2(width, height), checkEnd + OutArea));
                    
                // はみ出し 最大
                float2 out3 = max(out1, out2-float2(width, height));
                    
                out3.x = saturate(max(out3.x, out3.y));
           
                //EdgeColorScale = 1.f - out3.x;
                    
            }
                
            // Stencilのエッジを検出
            for (float i = 0; i < SampleRange; i += LocalSampleScale) {
                int2 target = int2(ClipLightDir.xy * float2(i,i)) + pixelCoord;
                target = clamp(target, int2(0,0), int2(width-1, height-1));

                if (CustomStencilTexture[target].y == 0) {
                    bEdge = true;
                    break;
                }
            }
            if (bEdge) {
                // エッジ
                float3 col = UseCustomLightColor ? LightColor : View.DirectionalLightColor.xyz;
                    
                SceneColorTexture[pixelCoord].xyz = max(SceneColorTexture[pixelCoord].xyz, col * LightScale * EdgeColorScale * NormalWeight);
            }
                
                
            //for (int i = 0; i < 100; ++i) {
            //    SceneColorTexture[pixelCoord + int2((ClipLightDir.xy * float2(i,i)))] = 10;
            //}

        }// 法線判定 end
    }} // mask end
    
    return;
    
  
    /*
    float LinearDepth = 1;
    {
        float2 UV = ViewSize / float2(width, height);
        
        float Depth = SceneDepthTexture[pixelCoord]; //SceneDepthTexture.SampleLevel(DepthSampler, UV, 0);
        
        LinearDepth = GetLinearDepth(UV, pixelCoord);

        float t = saturate(LinearDepth / 5000.f);
        
        float3 color = lerp(float3(0, 0, 1), float3(0, 1, 0), t); // 青→緑
        color = lerp(color, float3(1, 0, 0), t); // 緑→赤
        
        //SceneColorTexture[pixelCoord].xyz = color; //        frac(LinearDepth / 5000.f);
        //SceneColorTexture[pixelCoord] = float4(LinearDepth / 1000.0, 0.0, 0.0, 1.0);

      
    }
    //    CustomStencilTexture
    //SceneColorTexture[pixelCoord].xyz = color; //        frac(LinearDepth / 5000.f);

    if (CustomStencilTexture[pixelCoord].y & CustomStencilMask) {
        if (SceneDepthTexture[pixelCoord] <= CustomDepthTexture[pixelCoord].r) {
            //float2 UV = ViewSize / float2(width, height);
            float2 UV = View.BufferSizeAndInvSize.zw;
            
            float3 PackedNormal = NormalTexture[pixelCoord].xyz;
            float3 WorldNormal = PackedNormal * 2.0 - 1.0; // [-1, 1]に変換

            float3 LocalLightDirection = View.DirectionalLightDirection;
            if (UseCustomLightPosition) {

                LocalLightDirection = normalize(ReconstructWorldPosition(UV, pixelCoord) - LightPosition);
            }
            LocalLightDirection = normalize(LightDirection);

       
            float t = 1.f;

            if (dot(PackedNormal, PackedNormal) == 0.f) {
                // for unlit (no normal)
                // always 1
                
            } else {
                t = dot(WorldNormal, LocalLightDirection);
                t = saturate(t);

                if (UseBinaryEdge) {
                    // エッジを2値にする
                    t = (t >= RimEdgeBinaryRange) ? 1.f : 0.f;
                } else {
                    // エッジ フェード
                    t = pow(t, RimEdgeFade);
                }
            }


            if (t > 0.f) {
                float4 ClipLightDir = mul(float4(-LocalLightDirection, 0.0), View.RelativeWorldToClip);

                ClipLightDir.xyz = normalize(ClipLightDir.xyz);

                //float SampleRange = float(height) * SampleScreenScale;
                //float SampleRange = View.BufferSizeAndInvSize.y * SampleScreenScale;
                float SampleRange = View.ViewSizeAndInvSize.y * SampleScreenScale;

                SampleRange = SampleRange / LinearDepth * 10.f * (3.14159265f/ 2.f / View.FieldOfViewWideAngles.x);
                
                //float2 UnscaledMin = View.UnscaledViewRect.Min.xy;
                //float2 UnscaledMax = View.UnscaledViewRect.Max.xy;
                //float2 UnscaledSize = UnscaledMax - UnscaledMin;  // フル幅/高さ (pixels)
                //float SampleRange = UnscaledSize.y * SampleScreenScale;
                
                bool bEdge = false;
                
                float LocalSampleScale = max(SampleScale, 1.0);
                
                float EdgeColorScale = 1.0f;
                {
                    float fade = 0.01f;
                    float2 OutArea = float2(width, height) * fade;
                    
                    // エッジサーチ先の座標
                    //float2 checkEnd = float2(pixelCoord) + SampleRange;
                    float2 checkEnd = (ClipLightDir.xy * float2(SampleRange,SampleRange)) + pixelCoord;

                    // 左上のはみ出し
                    float2 out1 = abs( min(float2(0,0), checkEnd - OutArea));

                    // 右下のはみ出し
                    float2 out2 = abs(max(float2(width, height), checkEnd + OutArea));
                    
                    // はみ出し 最大
                    float2 out3 = max(out1, out2-float2(width, height));
                    
                    out3.x = saturate(max(out3.x, out3.y));
           
                    //EdgeColorScale = 1.f - out3.x;
                    
                }
                
                // Stencilのエッジを検出
                for (float i = 0; i < SampleRange; i += LocalSampleScale) {
                    int2 target = int2(ClipLightDir.xy * float2(i,i)) + pixelCoord;
                    target = clamp(target, int2(0,0), int2(width-1, height-1));

                    if (CustomStencilTexture[target].y == 0) {
                        bEdge = true;
                        break;
                    }
                }
                if (bEdge) {
                    // エッジ
                    float3 col = UseCustomLightColor ? LightColor : View.DirectionalLightColor.xyz;
                    
                    SceneColorTexture[pixelCoord].xyz = max(SceneColorTexture[pixelCoord].xyz, col * LightScale * EdgeColorScale * t);
                }
                
                
                //for (int i = 0; i < 100; ++i) {
                //    SceneColorTexture[pixelCoord + int2((ClipLightDir.xy * float2(i,i)))] = 10;
                //}
            }
        }
    }

    {
        //float2 UV = ViewSize / float2(width, height);

        //SceneColorTexture[pixelCoord].xyz = saturate(ReconstructWorldPosition(UV, pixelCoord));
    }
*/
    
#endif
}

