#include "material.glsl"

#define PI 3.14159265359
#define INVPI 1.0 / PI

float GTR1(float NDotH, float a)
{
    if (a >= 1)
    return INVPI;
    float a2 = a * a;
    float t = 1 + (a2 - 1) * NDotH * NDotH;
    return (a2 - 1) / (PI * log(a2) * t);
}

float GTR2(float NDotH, float a)
{
    float a2 = a * a;
    float t = 1.0f + (a2 - 1.0f) * NDotH * NDotH;
    return a2 / (PI * t * t);
}

float SmithGGX(const float NDotv, const float alphaG)
{
    float a = alphaG * alphaG;
    float b = NDotv * NDotv;
    return 1 / (NDotv + sqrt(a + b - a * b));
}

float Fr(const float VDotN, const float eio)
{
    float SinThetaT2 = (eio * eio) * (1.0f - VDotN * VDotN);
    if (SinThetaT2 > 1.0f)
    return 1.0f;// TIR
    float LDotN = sqrt(1.0f - SinThetaT2);
    float eta = 1.0f / eio;
    float r1 = (VDotN - eta * LDotN) / (VDotN + eta * LDotN);
    float r2 = (LDotN - eta * VDotN) / (LDotN + eta * VDotN);
    return 0.5f * ((r1 * r1) + (r2 * r2));
}

float SchlickFresnel(const float u)
{
    float m = clamp(1.0f - u, 0.0f, 1.0f);
    return float(m * m) * (m * m) * m;
}

vec3 BSDFEval(ShadingData data, const vec3 N, const vec3 wo, const vec3 wi)
{
    float NDotL = dot(N, wi);
	float NDotV = dot(N, wo);
	vec3 H = normalize(wi + wo);
	float NDotH = dot(N, H);
	float LDotH = dot(wi, H);
	vec3 Cdlin = data.color.xyz;
	float Cdlum = .3f * Cdlin.x + .6f * Cdlin.y + .1f * Cdlin.z; // luminance approx.
	vec3 Ctint = Cdlum > 0.0f ? Cdlin / Cdlum : vec3(1.0f);	  // normalize lum. to isolate hue+sat
	vec3 Cspec0 = mix(data.specular * .08f * mix(vec3(1.0f), Ctint, data.specular_tint), Cdlin, data.metallic);
	vec3 bsdf = vec3(0);
	vec3 brdf = vec3(0);

	// specular
	float a = data.roughness;
	float Ds = GTR2(NDotH, a);

	// Fresnel term with the microfacet normal
	float FH = SchlickFresnel(LDotH);
	vec3 Fs = mix(Cspec0, vec3(1), FH);
	float Gs = SmithGGX(NDotV, a) * SmithGGX(NDotL, a);

	// Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
	// and mix in diffuse retro-reflection based on roughness
	float FL = SchlickFresnel(NDotL), FV = SchlickFresnel(NDotV);
	float Fd90 = 0.5 + 2.0f * LDotH * LDotH * a;
	float Fd = mix(1.0f, Fd90, FL) * mix(1.0f, Fd90, FV);

	// clearcoat (ior = 1.5 -> F0 = 0.04)
	float Dr = GTR1(NDotH, mix(.1, .001, data.clearcoat_gloss));
	float Fc = mix(.04f, 1.0f, FH);
	float Gr = SmithGGX(NDotL, .25) * SmithGGX(NDotV, .25);

	brdf = INVPI * Fd * Cdlin * (1.0f - data.metallic) * (1.0f - data.subsurface) + Gs * Fs * Ds + data.clearcoat * Gr * Fc * Dr;

	return mix(brdf, bsdf, data.transmission);
}


vec3 evalLighting(ShadingData data, vec3 N, vec3 wo, vec3 wi)
{
    return BSDFEval(data, N, wo, wi);
}