local link = ac.connect{
    ac.StructItem.key('TunnelEffectLink'),
    gBlur = ac.StructItem.double(),          -- Get blur intensity from the main script
    flareIntensity = ac.StructItem.double(), -- Get lens flare intensity from the main script
    grainIntensity = ac.StructItem.double(), -- Grain intensity control
    lensDistortion = ac.StructItem.double(), -- Lens distortion control
    zoomFactor = ac.StructItem.double(),     -- Zoom factor control
    hitIntensity = ac.StructItem.double()    -- Add gHit control to the link
}

local shader = [[        
float4 main(PS_IN pin) {
    // Lens distortion parameters
    float2 uv = pin.Tex;  // Normalized UV coordinates (0 to 1)
    float2 center = float2(0.5, 0.5);  // Screen center
    float2 offset = uv - center;       // Offset from the center
    float dist = length(offset);       // Distance from the center

    // Get distortion factor and zoom factor from the linked values
    float distortionAmount = gLensDistortion; // Distortion amount from link
    float zoomFactor = gZoomFactor;           // Zoom factor from link

    // Apply zoom dynamically
    float2 zoomedUV = center + (uv - center) / zoomFactor;

    // Apply lens distortion, scaling based on the distance from the center
    float distortion = 1.0 + distortionAmount * dist * dist; // Radial distortion formula
    float2 distortedUV = center + (zoomedUV - center) * distortion;

    // Sample the lens flare textures with distorted UV
    float4 lensFlareTex1 = txLensFlare1.SampleLevel(samPoint, distortedUV, 0);
    float4 lensFlareTex2 = txLensFlare2.SampleLevel(samPoint, distortedUV, 0);
    float4 lensFlareTex3 = txLensFlare3.SampleLevel(samPoint, distortedUV, 0);

    // Sample the input texture and calculate brightness
    float4 screenColor = txInput.SampleLevel(samPoint, distortedUV, 0);
    float brightness = dot(screenColor.rgb, float3(0.299, 0.587, 0.114)); // Perceived brightness

    // Scale the lens flare effect based on brightness
    float flareIntensity = gFlareIntensity * saturate(brightness) * 0.1; // Lower intensity scaling

    // Combine lens flare textures with the scaled intensity
    float4 flareEffect = (lensFlareTex1 + lensFlareTex2 + lensFlareTex3) * flareIntensity;

    // Sample noise and blur as before
    float4 noise = txNoise.SampleLevel(samPoint, pin.PosH.xy / 32, 0);
    float blurIntensity = max(gBlur, gHit) * saturate(pow(length(pin.Tex * 2 - 1), 2));
    float mipLevel = 3 * blurIntensity;
    float2 blurDir = normalize(pin.Tex * 2 - 1) * 0.002 * blurIntensity;

    float4 ret = 0;
    for (int i = 0; i < 20; ++i) {
        float4 col = txInput.SampleLevel(samLinearBorder0, distortedUV - blurDir * ((float)i + noise.x * 0.5), mipLevel);
        float w = dot(col.rgb, 1) + 1;
        ret += float4(col.xyz, 1) * w;
    }

    ret /= ret.w;

    // Combine the blur output with the lens flare effect
    ret += flareEffect; // Lens flare added back as before

    // Generate film grain using a grain texture
    float2 grainUV = distortedUV * 7.0 + float2(gRandomFactor * 1, 1); // Scale and shift grain UVs

    // Sample the grain texture
    float4 grainTex = txGrain.SampleLevel(samPoint, grainUV, 0); // Sample grain texture at grainUV

    // Apply grain intensity and add it uniformly across the scene
    float grainIntensity = gGrainIntensity; // Control grain intensity
    float4 coloredGrain = grainTex * grainIntensity; // Apply grain to the final color

    // Add grain directly without brightness dependency
    ret.rgb += coloredGrain.rgb; // Add grain to the final color

    // Post-processing adjustments
    ret.rgb = lerp(ret.rgb, dot(ret.rgb, 0.33), gHit);
    ret.rgb = lerp(ret.rgb, 0.5 + 1.5 * (ret.rgb - 0.5), gHit);
    
    return ret;
}
]]

local hit = 0
local speedLast = 0
local shake = vec2()

function script.update(dt)
    local gameDt = ac.getGameDeltaT()
    local sim = ac.getSim()

    -- Keep the existing logic for camera mode and shake
    local car = ac.getCar(sim.focusedCar)
    shake = math.applyLag(shake, vec2(), 0.8, gameDt)

    if hit > 0 then
        hit = hit - gameDt * 0.25
    end

    if car.collisionDepth > 0 and gameDt > 0 then
        local nHit = math.saturateN((speedLast - car.speedKmh) / 60)
        if nHit > hit then
            hit = nHit
            if nHit > 0.2 then
                shake = vec2(math.random() - 0.5, math.random() - 0.5):normalize():scale(hit)
            end
        end
    end

    speedLast = math.applyLag(speedLast, car.speedKmh, 0.8, gameDt)

    -- Generate a random factor based on game time delta
    local randomFactor = gameDt * 10.0  -- Use gameDt to influence the randomness

    -- Retrieve values from the link
    local blur = link.gBlur or 0.0
    local flareIntensity = link.flareIntensity or 0.0
    local grainIntensity = link.grainIntensity or 0.0
    local lensDistortion = link.lensDistortion or 0.0
    local hitIntensity = link.hitIntensity or 0.0  -- Use linked gHit value

    local zoommod = 1
    if link.zoomFactor == 0 then
        zoommod = 1
    end
    if link.zoomFactor > 1 then
        zoommod = link.zoomFactor
    end

    local zoomFactor = zoommod + link.lensDistortion * 0.5
    ac.debug("link.zoomFactor", zoomFactor)

    -- Render shader with lens flare, grain, and distortion using additive blending mode
    ui.renderShader({
        p1 = vec2(),
        p2 = ui.windowSize(),
        blendMode = render.BlendMode.Additive,
        textures = {
            txInput = 'dynamic::screen',
            txNoise = 'dynamic::noise',
            txLensFlare1 = 'lens.png',
            txLensFlare2 = 'dirt.png',
            txLensFlare3 = 'lens2.png',
            txGrain = 'grain.png'
        },
        values = {
            gHit = (math.min(hit * 1.4, 1) ^ 2 )* hitIntensity,  -- Combine hit logic and linked value
            gBlur = blur,
            gShake = shake,
            gFlareIntensity = flareIntensity,
            gGrainIntensity = grainIntensity,
            gLensDistortion = lensDistortion,
            gZoomFactor = zoomFactor,
            gRandomFactor = randomFactor
        },
        shader = shader
    })
end