//////////////////////////////////////////////////////////////////////////////
//    ##      #####   ######   ######     ##     ####     #######  ##  ##   //
//   ####    ##   ##  # ## #    ##  ##   ####     ##       ##   #  ##  ##   //
//  ##  ##   #          ##      ##  ##  ##  ##    ##       ## #     ####    //
//  ##  ##    #####     ##      #####   ##  ##    ##       ####      ##     //
//  ######        ##    ##      ## ##   ######    ##   #   ## #     ####    //
//  ##  ##   ##   ##    ##      ##  ##  ##  ##    ##  ##   ##   #  ##  ##   //
//  ##  ##    #####    ####    #### ##  ##  ##   #######  #######  ##  ##   //
//--------------------------------------------------------------------------//
//--------------------------------------------------------------------------//
//      #####   ##   ##    ##     #####    #######  ######    #####         //
//     ##   ##  ##   ##   ####     ## ##    ##   #   ##  ##  ##   ##        //
//     #        ##   ##  ##  ##    ##  ##   ## #     ##  ##  #              //
//      #####   #######  ##  ##    ##  ##   ####     #####    #####         //
//          ##  ##   ##  ######    ##  ##   ## #     ## ##        ##        //
//          ##  ##   ##  ######    ##  ##   ## #     ## ##        ##        //
//     ##   ##  ##   ##  ##  ##    ## ##    ##   #   ##  ##  ##   ##        //
//      #####   ##   ##  ##  ##   #####    #######  #### ##   #####         //
/////(BSL Shaders Edit)//////////////////////////////////////By LexBoosT//////

#if defined OVERWORLD||defined END
#include "/lib/lighting/shadows.glsl"

vec3 DistortShadow(inout vec3 worldPos,float distortFactor){
    worldPos.xy/=distortFactor;
    worldPos.z*=.2;
    return worldPos*.5+.5;
}
#endif

#if defined WATER_CAUSTICS && defined OVERWORLD
	#include "/lib/lighting/caustics.glsl"
#endif

    vec3 toLinear(vec3 color) {
	return mix(color / 12.92, pow((color + 0.055) / 1.055, vec3(2.4)), vec3(greaterThan(color, vec3(0.04045))));
}

vec3 BlackBody(float Temperature)
{
    float t = pow(Temperature, -1.5);
    float lt = log(Temperature);

    vec3 col = vec3(0.0);
         col.x = 220000.0 * t + 0.58039215686;
         col.y = 0.39231372549 * lt - 2.44549019608;
         col.y = Temperature > 6500. ? 138039.215686 * t + 0.72156862745 : col.y;
         col.z = 0.76078431372 * lt - 5.68078431373;
         col = clamp01(col);
         col = Temperature < 1000. ? col * Temperature * 0.001 : col;

    return toLinear(col);
}

float CurveBlockLight(float blockLight)
{
    float decode=pow(blockLight,1.0/0.25);
    #ifdef LIGHT_JITTER
    const float speed=JITTER_SPEED;
    float jitter1=1.0-sin(frameTimeCounter*1.4*speed+cos(frameTimeCounter*6.9*speed)-sin(frameTimeCounter*9.5*speed))*JITTER_STRENGTH1;
    float jitter2=1.0-sin(frameTimeCounter*1.4*speed+cos(frameTimeCounter*3.0*speed)-sin(frameTimeCounter*4.5*speed))*JITTER_STRENGTH2;
    float jitter3=1.0-sin(frameTimeCounter*1.4*speed+cos(frameTimeCounter*1.5*speed)-sin(frameTimeCounter*2.5*speed))*JITTER_STRENGTH3;
    #endif
    
    #ifdef LIGHT_JITTER
    decode*=jitter1*jitter2*jitter3;
    #endif
     
    return decode;
}

void GetLighting(inout vec3 albedo, out vec3 shadow, vec3 viewPos, float lViewPos, vec3 worldPos,
                 vec2 lightmap, float smoothLighting, float NoL, float vanillaDiffuse,
                 float parallaxShadow, float emission, float subsurface, float leaves, float dither) {

    lightmap.x = max(lightmap.x * 1.15 - 0.15, 0.0);
    float shadowMult = 1.0;
	float shadowTime = 1.0;

    #if defined OVERWORLD || defined END
    vec3 shadowPos = ToShadow(worldPos);

    float distb = sqrt(shadowPos.x * shadowPos.x + shadowPos.y * shadowPos.y);
    float distortFactor = distb * shadowMapBias + (1.0 - shadowMapBias);
    shadowPos = DistortShadow(shadowPos, distortFactor);

    float doShadow = float(
        shadowPos.x > 0.0 && shadowPos.x < 1.0 &&
        shadowPos.y > 0.0 && shadowPos.y < 1.0
    );

    #ifdef OVERWORLD
    doShadow *= float(lightmap.y > 0.001);
    #endif
    
   if ((NoL > 0.0 || subsurface > 0.0)) {
        if (doShadow > 0.5) {
            float biasFactor = sqrt(1.0 - NoL * NoL) / NoL;
            float distortBias = distortFactor * shadowDistance / 256.0;
            distortBias *= 8.0 * distortBias;
            float distanceBias = sqrt(dot(worldPos.xyz, worldPos.xyz)) * 0.005;
            
            float bias = 0.0;
            float offset = 0.0;
            
            if(isEyeInWater == 0){
            bias = (distortBias * biasFactor + distanceBias + 0.05) / shadowMapResolution;
            offset = 1.0 / shadowMapResolution;
            }

            if(isEyeInWater == 1.0){
            bias = (distortBias * biasFactor + distanceBias + 0.05) / 2048;
            offset = 1.0 / 2048;
            }

            if (subsurface > 0.0) {
                if (leaves < 0.5) {
                    float UdotLm = clamp(dot(upVec, lightVec) * 1.01 - 0.01, 0.0, 1.0) * 0.99 + 0.01;
                    float biasFactorF = sqrt(1.0 - UdotLm * UdotLm) / UdotLm;
                    bias = (distortBias * biasFactorF + 0.05) / shadowMapResolution * 1.3;
                } else bias = 0.0002;

                bias *= mix(subsurface, 1.0, NoL);
                offset = 0.0009765625;
            }
            if (isEyeInWater == 1) offset *= 5.0;

            #ifdef SHADOW_CLOSE_DITHERING
            if (AA > 0) dither = fract(frameTimeCounter * (1.0 / 16.0) + dither);
            shadowPos.xyz -= .0025*(dither)/shadowMapResolution*40;
            
            #endif

            shadowPos.z -=bias;
            shadow=GetShadow(shadowPos,bias,offset);
            
        }else shadow=vec3(lightmap.y);
        shadow*=parallaxShadow;
        NoL = clamp(NoL * 1.01 - 0.01, 0.0, 1.0);

    }

    float scattering = 0.0;
    #ifdef SHADOW_SUBSURFACE  
    if (subsurface > 0.0){
        float VdotL2 = clamp(dot(normalize(viewPos.xyz), lightVec) * 0.5 + 0.5, 0.0, 1.0);
        scattering = pow(VdotL2, 16.0) * (1.0 - rainStrengthS) * (1.0 - sunVisibility +0.5) * (1.0 - moonVisibility +2.0) * subsurface;
        NoL = mix(NoL, 1.0, sqrt(subsurface) * 0.7);
        NoL = mix(NoL, 1.0, scattering);
    }
    #endif

    vec3 fullShadow = shadow * NoL;

    #ifdef OVERWORLD

    shadowMult = 1.0 * (1.0 - 0.95 * rainStrengthS) * shadowFade;
    shadowTime = abs(sunVisibility - 0.5) * 2.0;
    shadowTime *= shadowTime;
    shadowMult *= shadowTime * shadowTime;

    if (isEyeInWater == 1) ambientCol *= pow(lightmap.y, 2.5);
    vec3 lightingCol = pow(lightCol, vec3(1.0 + sunVisibility * 2.0 - timeBrightness));
         lightingCol *= (1.0 + 0.5 * leaves);

    vec3 shadowDecider = fullShadow * shadowMult;
    if (isEyeInWater == 1) shadowDecider *= pow(min(lightmap.y * 1.03, 1.0), 200.0);
    vec3 sceneLighting = mix(ambientCol * AMBIENT_GROUND, lightingCol * LIGHT_GROUND, shadowDecider);
    if (isEyeInWater == 0) sceneLighting *= (4.0 - 3.0 * eBS) * pow(lightmap.y, 5.0) * (1.0 + scattering * shadow);

#endif

    #ifdef END
        #if BRIGHTNESS_END == 1
        float brightnessEnd = AMBIANT_END;
        #elif BRIGHTNESS_END == 2
        float brightnessEnd = (AMBIANT_END * 100);
        #endif

        vec3 ambientEnd = endCol.rgb * 0.17 * brightnessEnd;
        vec3 lightEnd   = endCol.rgb * 0.37 * brightnessEnd;
        vec3 shadowDecider = fullShadow * 3.0;
        vec3 sceneLighting = mix(ambientEnd, lightEnd, shadowDecider);
        sceneLighting *= 0.20 * (0.7 + 0.3 * screenBrightness2);
    #endif
    
    #else
    vec3 sceneLighting = netherColSqrt.rgb * 0.1;
    #endif
    
    float torchDist=length(worldPos);
    float handTorchLightmap=0.;
    vec3 handLightCol=vec3(0.1);
    
    #ifdef DYNAMIC_HAND_LIGHT
		float handLight = min(float(heldBlockLightValue2 + heldBlockLightValue), 15.0) / 15.0;
		float handLightFactor = 1.0 - min(14.0, lViewPos) * 0.07142857;
		lightmap.x = max(handLight * handLightFactor * 0.95, lightmap.x);
	#endif

    #ifdef DYNAMIC_HAND_LIGHT
    if (heldItemId==13001||heldItemId2==13001||
        heldItemId==13002||heldItemId2==13002||
        heldItemId==13003||heldItemId2==13003||
        heldItemId==13004||heldItemId2==13004||
        heldItemId==13005||heldItemId2==13005||
        heldItemId==13006||heldItemId2==13006||
        heldItemId==13007||heldItemId2==13007){

        handTorchLightmap=clamp(1.-((torchDist-DIST_MAX_LIGHT)/(DIST_DECLINE-DIST_MAX_LIGHT)),0.,1.)*MAX_LIGHT;
        
        #ifdef COLORED_DYNAMIC_HAND_LIGHT
        
        if(heldItemId==13002||heldItemId2==13002)
        {
            handLightCol=vec3(.4863,.7686,.9882);
        }
        else if(heldItemId==13003||heldItemId2==13003)
        {
            handLightCol=vec3(0.8863, 0.0235, 0.0235);
        }
        else if(heldItemId==13004||heldItemId2==13004)
        {
            handLightCol=vec3(0.0, 0.1843, 1.0);
        }
        else if(heldItemId==13005||heldItemId2==13005)
        {
            handLightCol=vec3(0.0, 0.8549, 0.6667);
        }
        else if(heldItemId==13006||heldItemId2==13006)
        {
            handLightCol=vec3(0.9765, 0.3412, 1.0);
        }
        else if(heldItemId==13007||heldItemId2==13007)
        {
            handLightCol=vec3(0.8275, 0.8314, 0.4118);
        }
        else if(heldItemId==13001||heldItemId2==13001)
        {
            handLightCol=vec3(1.,.3686,0.);
        }
        #endif
    }
    #endif
    
    float LightPower=LIGHTING_POWER_DAY * sunVisibility + LIGHTING_POWER_NIGHT * moonVisibility;

    if(isEyeInWater==0) LightPower = mix(LIGHTING_POWER_INSIDE,LightPower,eBS);
    if(isEyeInWater==1) LightPower = mix(LIGHTING_POWER_UNDERWATER,LightPower,eBS);

    #if CONCENTRATE_BLOCK_LIGHT == 1
    float newLightmap  = pow(lightmap.x, 120.0 / LightPower) * BLOCKLIGHT_LUMA + max((lightmap.x - 0.05) * 0.925, 0.0) * (screenBrightness2*0.25 + 0.9);
    #endif
    #if CONCENTRATE_BLOCK_LIGHT == 2
    float newLightmap  = sqrt(pow(lightmap.x, 120.0 / LightPower) * BLOCKLIGHT_I + max((lightmap.x - 0.05) * 0.925, 0.0) * (screenBrightness2*0.25 + 0.9));
    #endif

    #ifndef HAND
    #ifdef BLOCKLIGHT_BY_TEMP
    blocklightCol=BlackBody(BLOCKLIGHT_TEMP) * 2.0 * BLOCKLIGHT_LUMA;
    #endif
    #endif
    
    handTorchLightmap=CurveBlockLight(handTorchLightmap);
    newLightmap=CurveBlockLight(newLightmap);
    
    #ifdef OVERWORLD
    #if SHINE_BLOCKLIGHTINGLIMITER == 0
    float newLightMap = newLightmap;
    #endif
    #if SHINE_BLOCKLIGHTINGLIMITER == 1
    float newLightMap = newLightmap * newLightmap;
    #endif


    #ifdef COLORED_LIGHTING
		float CLr = texture2D(noisetex, 0.00006 * (worldPos.xz + cameraPosition.xz)).r;
		float CLg = texture2D(noisetex, 0.00009 * (worldPos.xz + cameraPosition.xz)).r;
		float CLb = texture2D(noisetex, 0.00014 * (worldPos.xz + cameraPosition.xz)).r;
		blocklightCol = vec3(CLr, CLg, CLb);
		blocklightCol *= blocklightCol * BLOCKLIGHT_I * 2.22;
	#endif
    
    vec3 blockLighting=max(blocklightCol*(newLightMap),handLightCol * (handTorchLightmap));
    #else
    vec3 blockLighting=max(blocklightCol*(newLightmap),handLightCol * (handTorchLightmap));
    #endif

    vec3 emissiveColor = sqrt(albedo * length(albedo) + vec3(0.00001));
    vec3 emissiveLighting = albedo * vec3(emission * 6.0);
    
    float lightFlatten = clamp(1.0 - pow(1.0 - emission, 128.0), 0.0, 1.0);
    vanillaDiffuse = mix(vanillaDiffuse, 1.0, lightFlatten);
    smoothLighting = mix(smoothLighting, 1.0, lightFlatten);

    float nightVisionLighting=nightVision*.25;

    vec3 minLighting = minLightCol * (1.0 - eBS) * ((isEyeInWater == 1) ? MINLIGHT_U_I : MINLIGHT_I);

    albedo *= sceneLighting + blockLighting + emissiveLighting + nightVisionLighting + minLighting;
    albedo *=pow(vanillaDiffuse,SHADING_STRENGTH)*smoothLighting*smoothLighting;

    #if defined WATER_CAUSTICS && defined OVERWORLD
		#ifdef WATER_CAUSTICS
		if (isEyeInWater == 1) {
		#else
		if ((isEyeInWater != 0 && isEyeInWater != 2 && isEyeInWater != 3)) {
		#endif
			vec3 albedoCaustic = albedo;

			float skyLightMap = lightmap.y * lightmap.y * (3.0 - 2.0 * lightmap.y);
			float skyLightMapA = pow2(pow2((1.0 - skyLightMap)));
			float skyLightMapB = skyLightMap > 0.98 ? (1.0 - skyLightMap) * 50.0 : 1.0;
			
			float causticfactor = 1.0 - lightmap.x * 0.8;

			vec3 causticpos = worldPos.xyz + cameraPosition.xyz;
			float caustic = getCausticWaves(causticpos);
			vec3 causticcol = rawWaterColor.rgb / UNDERWATER_I;
			
			#ifdef WATER_CAUSTICS
				if (isEyeInWater == 0) {
					causticfactor *= 1.0 - skyLightMapA;
					causticfactor *= 10.0;

					causticcol = sqrt(normalize(waterColor.rgb + vec3(0.01)) * 0.32 * sqrt(UNDERWATER_I));

						albedoCaustic = albedo.rgb * 1.74;
						causticcol = sqrt(causticcol) * 0.2;

				} else {
			#endif
					causticfactor *= shadow.g * sqrt2(NoL) * (1.0 - rainStrengthS);
					causticfactor *= 0.25 - 0.15 * skyLightMapA;
					causticfactor *= skyLightMapB;

					albedoCaustic = (albedo.rgb + albedo.rgb * rawWaterColor.rgb * 16.0) * 0.225;

					albedoCaustic += albedo.rgb * rawWaterColor.rgb * caustic * sqrt1(lightmap.x) * 6.0 * skyLightMapB;
					causticcol = sqrt(causticcol) * 30.0;
			#ifdef WATER_CAUSTICS
				}
			#endif

			vec3 lightcaustic = caustic * causticfactor * causticcol * UNDERWATER_I;
			albedoCaustic *= 1.0 + lightcaustic;

			#ifdef WATER_CAUSTICS
				if (isEyeInWater == 0) albedo = mix(albedo, albedoCaustic, max(1.0 - rainStrengthS, 0.0));
				else albedo = albedoCaustic;
			#else
				albedo = albedoCaustic;
			#endif
		}
	#endif
}