//
//  SWNeuroNoise.metal
//  ShipSwift
//
//  Stitchable SwiftUI colorEffect — port of Paper Shaders' neuro-noise
//  (https://shaders.paper.design/neuro-noise, MIT). A glowing web-like
//  structure of fluid lines and soft intersections, generated by
//  iteratively rotating UV + scale and accumulating sine/cosine waves.
//
//  Original algorithm: https://x.com/zozuar/status/1625182758745128981
//
//  Paired with: SWNeuroNoise.swift
//  Entry point: `swNeuroNoise` — invoked via SwiftUI `.colorEffect(...)`.
//  Requires iOS 17+ / macOS 14+.
//

#include <metal_stdlib>
#include <SwiftUI/SwiftUI_Metal.h>
using namespace metal;

// 2D rotation by 1 radian (Paper's `rotate(uv, 1.0)` baked in for speed).
static float2 swNN_rotate1(float2 p) {
    const float c = 0.5403023058681398;  // cos(1)
    const float s = 0.8414709848078965;  // sin(1)
    return float2(c * p.x - s * p.y, s * p.x + c * p.y);
}

// Paper's `neuroShape` — 15 layers of iteratively rotated, scale-doubling
// sine/cosine accumulation. The sine accumulator threads "memory" of the
// previous layer into the next, producing the organic web look.
static float swNN_neuroShape(float2 uv, float t) {
    float2 sineAcc = float2(0.0);
    float2 res = float2(0.0);
    float scale = 8.0;

    for (int j = 0; j < 15; j++) {
        uv      = swNN_rotate1(uv);
        sineAcc = swNN_rotate1(sineAcc);
        float2 layer = uv * scale + float(j) + sineAcc - t;
        sineAcc += sin(layer);
        res += (0.5 + 0.5 * cos(layer)) / scale;
        scale *= 1.2;
    }
    return res.x + res.y;
}

[[ stitchable ]] half4 swNeuroNoise(float2 position,
                                    half4  color,
                                    float4 boundingRect,
                                    float  time,
                                    float  speed,
                                    float  brightness,   // 0..1
                                    float  contrast,     // 0..1
                                    float  scale,        // 0.05..1 — small = zoomed-in, large = zoomed-out
                                    half4  colorFront,
                                    half4  colorMid,
                                    half4  colorBack) {
    float2 sz = boundingRect.zw;
    float  minDim = max(min(sz.x, sz.y), 1.0);
    // Center-anchored, aspect-preserved UV — like Paper's v_patternUV.
    float2 uv = (position - 0.5 * sz) / minDim;
    // Pattern zoom — small = features fill screen, large = zoom-out (more cycles per pixel).
    uv *= max(scale, 1e-4);

    float t = 0.5 * speed * time;
    float noise = swNN_neuroShape(uv, t);

    // Brightness / contrast curve — Paper's exact shaping.
    noise = (1.0 + saturate(brightness)) * noise * noise;
    noise = pow(noise, 0.7 + 6.0 * saturate(contrast));
    noise = min(1.4, noise);

    float blend = smoothstep(0.7, 1.4, noise);

    // Premultiply alpha for blending.
    float3 frontRGB = float3(colorFront.rgb) * float(colorFront.a);
    float3 midRGB   = float3(colorMid.rgb)   * float(colorMid.a);
    float  frontA   = float(colorFront.a);
    float  midA     = float(colorMid.a);

    float3 blendRGB = mix(midRGB,   frontRGB, blend);
    float  blendA   = mix(midA,     frontA,   blend);

    float safeNoise = max(noise, 0.0);
    float3 col = blendRGB * safeNoise;
    float  opacity = saturate(blendA * safeNoise);

    float3 backRGB = float3(colorBack.rgb) * float(colorBack.a);
    col = col + backRGB * (1.0 - opacity);

    return half4(half3(col), 1.0);
}
