// End sky integration: rotating textured Earth, white stars, and a sun.
// earthtex is supplied by shaders.properties only for the deferred End pass.

const float IES_PI = 3.14159265359;
const float IES_TAU = 6.28318530718;

float iesSaturate(float value) { return clamp(value, 0.0, 1.0); }

float iesMie(float g, float cosine) {
    float gg = g * g;
    return 0.1193662 * (1.0 - gg) / (2.0 + gg) * (1.0 + cosine * cosine) * pow(1.0 + gg - 2.0 * g * cosine, -1.5);
}

mat3 iesRotationX(float angle) {
    return mat3(1.0, 0.0, 0.0, 0.0, cos(angle), -sin(angle), 0.0, sin(angle), cos(angle));
}

mat3 iesRotationY(float angle) {
    return mat3(cos(angle), 0.0, sin(angle), 0.0, 1.0, 0.0, -sin(angle), 0.0, cos(angle));
}

mat3 iesRotationZ(float angle) {
    return mat3(cos(angle), -sin(angle), 0.0, sin(angle), cos(angle), 0.0, 0.0, 0.0, 1.0);
}

vec2 iesRaySphere(vec3 origin, vec3 direction, float radius) {
    float b = dot(origin, direction);
    float c = dot(origin, origin) - radius * radius;
    float discriminant = b * b - c;
    if (discriminant < 0.0) return vec2(1e10, -1e10);
    float root = sqrt(discriminant);
    return vec2(-b - root, -b + root);
}

float iesPlanetTime() {
    #ifdef END_SKY_MANUAL_CYCLE
        return fract(END_SKY_MANUAL_ANGLE / 360.0 + 0.282) * IES_TAU;
    #else
        return fract(frameTimeCounter * ((1.0 / 60.0) / END_SKY_CYCLE) + 0.282) * IES_TAU;
    #endif
}

vec3 iesStars(vec3 worldDirection, vec3 sunDirection) {
    float rotation = frameTimeCounter * 0.001;
    mat3 rotationY = mat3(cos(rotation), 0.0, sin(rotation), 0.0, 1.0, 0.0, -sin(rotation), 0.0, cos(rotation));
    worldDirection = rotationY * worldDirection;

    float cosine = dot(sunDirection, vec3(0.0, 0.0, 1.0));
    vec3 axis = cross(sunDirection, vec3(0.0, 0.0, 1.0));
    float axisLength2 = max(dot(axis, axis), 0.0001);
    worldDirection = cosine * worldDirection + cross(axis, worldDirection) + (1.0 - cosine) / axisLength2 * dot(axis, worldDirection) * axis;

    vec3 grid = worldDirection * 384.0;
    ivec3 cell = ivec3(floor(grid));
    vec3 local = grid - vec3(cell);
    float radius = dot(local - 0.5, local - 0.5);
    vec3 hash3 = fract(vec3(cell) * vec3(443.897, 441.423, 437.195));
    hash3 += dot(hash3, hash3.yzx + 19.19);
    vec2 hash = fract((hash3.xx + hash3.yz) * hash3.zy);
    float starCoverage = iesSaturate((hash.x - 0.985) / 0.015);

    return 0.18 * iesSaturate((0.25 - radius) / 0.25) * starCoverage * starCoverage * vec3(1.0);
}

vec2 iesEarthUv(vec3 normal, float rotation) {
    float longitude = atan(normal.z, normal.x);
    float latitude = asin(clamp(normal.y, -1.0, 1.0));
    return vec2(fract(0.5 + longitude / IES_TAU + rotation), clamp(0.5 - latitude / IES_PI, 0.0, 1.0));
}

void drawIterationEndSun(inout vec3 color, vec3 worldDirection, vec3 sunDirection) {
    // Granular solar surface and irregular radial rays; this stays local to the End sky.
    float lightDot = dot(worldDirection, sunDirection);
    vec3 tangent = normalize(cross(abs(sunDirection.y) < 0.95 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0), sunDirection));
    vec3 bitangent = cross(sunDirection, tangent);
    vec2 solarPosition = vec2(dot(worldDirection, tangent), dot(worldDirection, bitangent));
    float solarRadius = length(solarPosition);
    float core = exp(-pow(solarRadius / 0.068, 2.0) * 1.55);
    float coronaFade = exp(-pow(solarRadius / 0.150, 2.0) * 2.0);
    float forwardFacing = smoothstep(0.0, 0.25, lightDot);
    core *= forwardFacing;
    coronaFade *= forwardFacing;
    float polarAngle = atan(solarPosition.y, solarPosition.x) / IES_TAU + 0.5;
    float granulation = texture2D(noisetex, solarPosition * 7.0 + vec2(frameTimeCounter * 0.003, -frameTimeCounter * 0.002)).r;
    float rayNoise = texture2D(noisetex, vec2(polarAngle * 4.0, 0.35)).g;
    float rays = coronaFade * pow(rayNoise, 4.0) * (1.0 - core);
    vec3 solarSurface = mix(vec3(1.0, 0.20, 0.01), vec3(1.0, 0.90, 0.16), smoothstep(0.24, 0.82, granulation));
    color += solarSurface * (2.2 + granulation * 1.8) * core;
    color += mix(vec3(1.0, 0.08, 0.0), vec3(1.0, 0.58, 0.04), rayNoise) * (0.20 + rays * 1.35) * coronaFade * (1.0 - core * 0.55);
}

// The former ringed planet is now a textured Earth with independent axial rotation.
void drawIterationEndPlanet(inout vec3 color, vec3 worldDirection, vec3 sunDirection, vec3 shadowDirection) {
    const float groundRadius = 20e6;
    const float atmosphereRadius = 20.30e6;
    const float cosmicHazeRadius = 20.75e6;
    // Slightly closer camera: the planet grows without becoming invasive.
    vec3 eye = vec3(0.0, groundRadius + 13.0e6, 0.0);
    // Fixed sky placement: only the texture rotates, never the planet's position.
    mat3 earthPlacement = iesRotationX(-1.8700) * iesRotationY(1.7719);
    vec3 rayDirection = earthPlacement * worldDirection;
    vec3 rotatedSunDirection = earthPlacement * sunDirection;
    vec2 groundIntersection = iesRaySphere(eye, rayDirection, groundRadius);
    vec2 atmosphereIntersection = iesRaySphere(eye, rayDirection, atmosphereRadius);
    vec2 cosmicHazeIntersection = iesRaySphere(eye, rayDirection, cosmicHazeRadius);
    vec3 surface = vec3(0.0);
    vec3 surfacePosition = rayDirection * groundIntersection.x;
    vec3 surfaceNormal = normalize(surfacePosition + vec3(0.0, eye.y, 0.0));

    if (groundIntersection.y > 0.0) {
        // Cheap edge-softening: blend the limb into the sky instead of cutting a hard circle.
        float planetMask = smoothstep(0.012, 0.105, iesSaturate(dot(surfaceNormal, -rayDirection)));
        color *= 1.0 - planetMask;

        // frameTimeCounter is already expressed in seconds; one full turn takes END_EARTH_ROTATION_SPEED seconds.
        float rotation = fract(frameTimeCounter / END_EARTH_ROTATION_SPEED + END_EARTH_ROTATION_OFFSET / 360.0);
        vec3 textureNormal = iesRotationZ(0.18) * iesRotationX(0.46) * surfaceNormal;
        vec3 earthAlbedo = texture2D(earthtex, iesEarthUv(textureNormal, rotation)).rgb;
        float normalLight = dot(surfaceNormal, rotatedSunDirection);
        float daylight = smoothstep(-0.025, 0.115, normalLight);
        float directLight = iesSaturate(normalLight);

        // Neutral, non-reflective Earth material with a deliberately stronger night phase.
        earthAlbedo = mix(earthAlbedo, vec3(dot(earthAlbedo, vec3(0.299, 0.587, 0.114))), 0.08);
        vec3 nightSide = earthAlbedo * vec3(0.002, 0.004, 0.010);
        vec3 daySide = earthAlbedo * (0.055 + 0.52 * pow(directLight, 0.78));
        surface = mix(nightSide, daySide, daylight) * planetMask;
    }

    if (cosmicHazeIntersection.y > 0.0) {
        float atmospherePath = max(atmosphereIntersection.y - max(atmosphereIntersection.x, 0.0), 0.0);
        float groundPath = max(groundIntersection.y - max(groundIntersection.x, 0.0), 0.0);
        float shellThickness = max(atmospherePath - groundPath, 0.0) * 1e-7;
        float cosmicPath = max(cosmicHazeIntersection.y - max(cosmicHazeIntersection.x, 0.0), 0.0);
        float cosmicThickness = max(cosmicPath - atmospherePath, 0.0) * 1e-7;
        float hitsEarth = step(0.0, groundIntersection.y);
        vec3 atmosphereNormal = normalize(eye + rayDirection * max(-dot(eye, rayDirection), 0.0));
        float lightSide = smoothstep(-0.25, 0.25, dot(atmosphereNormal, rotatedSunDirection));
        // The lit limb is thin; the dark limb keeps a much wider turquoise halo.
        float outerWidth = mix(1.70, 0.34, lightSide);
        float innerWidth = mix(0.030, 0.008, lightSide);
        float outerDensity = iesSaturate(shellThickness * outerWidth);
        float outerHalo = outerDensity * outerDensity * (3.0 - 2.0 * outerDensity) * (1.0 - hitsEarth);
        float innerAtmosphere = iesSaturate(shellThickness) * hitsEarth * innerWidth;
        // Broad low-opacity haze gives the planet depth without a post-process pass.
        float cosmicHaze = pow(iesSaturate(cosmicThickness * 0.58), 1.55) * (1.0 - hitsEarth);
        float hazeNoise = texture2D(noisetex, worldDirection.xz * 9.0 + vec2(frameTimeCounter * 0.0004)).b;
        vec3 cosmicTint = mix(vec3(0.00, 0.11, 0.16), vec3(0.11, 0.03, 0.16), hazeNoise * 0.35);
        surface += vec3(0.00, 0.58, 0.68) * (outerHalo * 0.72 + innerAtmosphere);
        surface += cosmicTint * cosmicHaze * 0.50;
    }

    color += surface;
}

void drawIterationEndSky(inout vec3 color, vec3 worldDirection, vec3 sunDirection) {
    color += iesStars(worldDirection, sunDirection);
    drawIterationEndSun(color, worldDirection, sunDirection);
}

void applyIterationEndFog(inout vec3 color, float distanceToSurface, vec3 worldDirection, vec3 shadowDirection) {
    #ifdef END_SKY_FOG
        float timeFactor = iesPlanetTime();
        float planetShadow = smoothstep(0.62, 0.35, timeFactor) + smoothstep(1.6, 1.87, timeFactor);
        float angleX = -1.57079633 + (0.2 * sin(timeFactor + 3.0) - 0.1) - 0.008 * sin(timeFactor + 4.7);
        mat3 rotationX = mat3(1.0, 0.0, 0.0, 0.0, cos(angleX), -sin(angleX), 0.0, sin(angleX), cos(angleX));
        mat3 rotationY = mat3(cos(timeFactor), 0.0, sin(timeFactor), 0.0, 1.0, 0.0, -sin(timeFactor), 0.0, cos(timeFactor));
        worldDirection = rotationX * rotationY * worldDirection;

        distanceToSurface = min(distanceToSurface, 512.0);
        float height = max(abs(worldDirection.z) * distanceToSurface * 0.03, 0.0001);
        float density = (1.0 - exp(-height)) / height * distanceToSurface;
        density *= iesMie(0.3, dot(shadowDirection, worldDirection)) * 10.0 * planetShadow + 0.25;
        density *= planetShadow * 0.93 + 0.07;
        color += vec3(density * 0.00001 * END_SKY_FOG_INTENSITY);
    #endif
}
