import Common;
import WaterCommon;

float SchlickFresnel(float3 normal, float3 viewDir) {
    // 0.02f comes from the reflectivity bias of water kinda idk it's from a paper somewhere i'm not gonna link it tho lmaooo
    return 0.02f + (1 - 0.02f) * (pow(1 - clamp(dot(normal, viewDir), 0, 1), 5.0f));
}

float SmithMaskingBeckmann(float3 H, float3 S, float roughness) {
    float hdots = max(0.001f, clamp(dot(H, S), 0, 1));
    float a = hdots / (roughness * sqrt(1 - hdots * hdots));
    float a2 = a * a;

    return a < 1.6f ? (1.0f - 1.259f * a + 0.396f * a2) / (3.535f * a + 2.181 * a2) : 0.0f;
}

float Beckmann(float ndoth, float roughness) {
    float exp_arg = (ndoth * ndoth - 1) / (roughness * roughness * ndoth * ndoth);

    return exp(exp_arg) / (PI * roughness * roughness * ndoth * ndoth * ndoth * ndoth);
}

[shader("pixel")]
float4 fragmentMain(WaterVertex vert) : SV_TARGET {
    float3 lightDir = -normalize(pWaterMaterial.sunDirection);
    float3 viewDir = normalize(pViewParams.cameraPos_WS.xyz - vert.position_WS);
    float3 halfwayDir = normalize(lightDir + viewDir);
    float depth = vert.depth;
    float LdotH = clamp(dot(lightDir, halfwayDir), 0, 1);
    float VdotH = clamp(dot(viewDir, halfwayDir), 0, 1);
    
    
    float4 displacementFoam1 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(vert.position_WS.xz * pWaterMaterial.tile1, 0));
    displacementFoam1.a += pWaterMaterial.foamSubtract0;
    float4 displacementFoam2 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(vert.position_WS.xz * pWaterMaterial.tile2, 0));
    displacementFoam2.a += pWaterMaterial.foamSubtract1;
    float4 displacementFoam3 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(vert.position_WS.xz * pWaterMaterial.tile3, 0));
    displacementFoam3.a += pWaterMaterial.foamSubtract2;
    float4 displacementFoam4 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(vert.position_WS.xz * pWaterMaterial.tile4, 0));
    displacementFoam4.a += pWaterMaterial.foamSubtract3;
    float4 displacementFoam = displacementFoam1 + displacementFoam2 + displacementFoam3 + displacementFoam4;

    float2 slopes1 = pWaterMaterial.slopeTextures.Sample(pWaterMaterial.sampler, float3(vert.position_WS.xz * pWaterMaterial.tile1, 0));
    float2 slopes2 = pWaterMaterial.slopeTextures.Sample(pWaterMaterial.sampler, float3(vert.position_WS.xz * pWaterMaterial.tile2, 1));
    float2 slopes3 = pWaterMaterial.slopeTextures.Sample(pWaterMaterial.sampler, float3(vert.position_WS.xz * pWaterMaterial.tile3, 2));
    float2 slopes4 = pWaterMaterial.slopeTextures.Sample(pWaterMaterial.sampler, float3(vert.position_WS.xz * pWaterMaterial.tile4, 3));
    float2 slopes = slopes1 + slopes2 + slopes3 + slopes4;
    
    slopes *= pWaterMaterial.normalStrength;
    float foam = lerp(0.0f, saturate(displacementFoam.a), pow(depth, pWaterMaterial.foamDepthAttenuation));

    float3 macroNormal = float3(0, 1, 0);
    float3 mesoNormal = normalize(float3(-slopes.x, 1.0f, -slopes.y));
    mesoNormal = normalize(lerp(macroNormal, mesoNormal, pow(saturate(depth), pWaterMaterial.normalDepthAttenuation)));
    mesoNormal = normalize(mesoNormal);

    float NdotL = clamp(dot(mesoNormal, lightDir), 0, 1);

    
    float a = pWaterMaterial.roughness + foam * pWaterMaterial.foamRoughnessModifier;
    float ndoth = max(0.0001f, dot(mesoNormal, halfwayDir));

    float viewMask = SmithMaskingBeckmann(halfwayDir, viewDir, a);
    float lightMask = SmithMaskingBeckmann(halfwayDir, lightDir, a);
    
    float G = rcp(1 + viewMask + lightMask);

    float eta = 1.33f;
    float R = ((eta - 1) * (eta - 1)) / ((eta + 1) * (eta + 1));
    float thetaV = acos(viewDir.y);

    float numerator = pow(1 - dot(mesoNormal, viewDir), 5 * exp(-2.69 * a));
    float F = R + (1 - R) * numerator / (1.0f + 22.7f * pow(a, 1.5f));
    F = saturate(F);
    
    float3 specular = pWaterMaterial.sunIrradiance * F * G * Beckmann(ndoth, a);
    specular /= 4.0f * max(0.001f, clamp(dot(macroNormal, lightDir), 0, 1));
    specular *= clamp(dot(mesoNormal, lightDir), 0, 1);

    float3 envReflection = pWaterMaterial.environmentMap.Sample(pWaterMaterial.sampler, reflect(-viewDir, mesoNormal)).rgb;
    envReflection *= pWaterMaterial.environmentLightStrength;

    float H = max(0.0f, displacementFoam.y) * pWaterMaterial.heightModifier;
    float3 scatterColor = pWaterMaterial.scatterColor;
    float3 bubbleColor = pWaterMaterial.bubbleColor;
    float bubbleDensity = pWaterMaterial.bubbleDensity;

    
    float k1 = pWaterMaterial.wavePeakScatterStrength * H * pow(clamp(dot(lightDir, -viewDir), 0, 1), 4.0f) * pow(0.5f - 0.5f * dot(lightDir, mesoNormal), 3.0f);
    float k2 = pWaterMaterial.scatterStrength * pow(clamp(dot(viewDir, mesoNormal), 0, 1), 2.0f);
    float k3 = pWaterMaterial.scatterShadowStrength * NdotL;
    float k4 = bubbleDensity;

    float3 scatter = (k1 + k2) * scatterColor * pWaterMaterial.sunIrradiance * rcp(1 + lightMask);
    scatter += k3 * scatterColor * pWaterMaterial.sunIrradiance + k4 * bubbleColor * pWaterMaterial.sunIrradiance;

    
    float3 output = (1 - F) * scatter + specular + F * envReflection;
    output = max(0.0f, output);
    output = lerp(output, pWaterMaterial.foamColor, saturate(foam));
    return float4(output, 1);

    //if(vert.lod == 1)
    //{
    //    return float4(1, 0, 0, 1.0f);
    //}
    //if(vert.lod == 2)
    //{
    //    return float4(0, 1, 0, 1.0f);
    //}
    //if(vert.lod == 3)
    //{
    //    return float4(0, 0, 1, 1.0f);
    //}
    //if(vert.lod == 4)
    //{
    //    return float4(1, 1, 1, 1.0f);
    //}
    //return float4(0, 0, 0, 1);
}
