export const MANDELBULB_VERTEX = /* glsl */`
  precision highp float;
  attribute vec3 position;
  attribute vec2 uv;
  varying vec2 vUV;

  void main(void) {
    gl_Position = vec4(position, 1.0);
    vUV = uv;
  }
`;

export const MANDELBULB_FRAGMENT = /* glsl */`
  precision highp float;

  uniform float time;
  uniform vec2 resolution;
  uniform vec3 cameraPosition;
  uniform vec3 targetPosition;
  uniform float power;
  uniform int fractalType; // 0 = Bulb, 1 = Box, 2 = Julia

  // --- Palettes ---
  vec3 palette( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d ) {
      return a + b*cos( 6.28318*(c*t+d) );
  }

  // Global trap for coloring
  float minTrap = 1000.0;

  // --- Shape 1: Mandelbulb ---
  float mapMandelbulb(vec3 pos, out float trap) {
      vec3 z = pos;
      float dr = 1.0;
      float r = 0.0;
      trap = 1000.0;

      for (int i = 0; i < 8; i++) {
          r = length(z);
          if (r > 100.0) break;
          trap = min(trap, r);

          float theta = acos(z.y / r);
          float phi = atan(z.z, z.x);
          dr = pow(r, power - 1.0) * power * dr + 1.0;
          float zr = pow(r, power);
          theta = theta * power;
          phi = phi * power;
          z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
          z += pos;
      }
      return 0.5 * log(r) * r / dr;
  }

  // --- Shape 2: Mandelbox ---
  float mapMandelbox(vec3 pos, out float trap) {
      vec3 z = pos;
      float dr = 1.0;
      float scale = 2.8; // Fixed scale for good look
      trap = 1000.0;

      for (int i = 0; i < 15; i++) {
          // Box fold
          z = clamp(z, -1.0, 1.0) * 2.0 - z;

          // Sphere fold
          float r2 = dot(z, z);
          trap = min(trap, r2); // Trap based on sphere fold

          if (r2 < 0.25) {
              z = z * 4.0;
              dr = dr * 4.0;
          } else if (r2 < 1.0) {
              z = z / r2;
              dr = dr / r2;
          }

          z = z * scale + pos;
          dr = dr * abs(scale) + 1.0;
      }
      return (length(z) - abs(scale - 1.0)) / dr;
  }

  // --- Shape 3: Julia Bulb ---
  float mapJulia(vec3 pos, out float trap) {
      vec3 z = pos;
      float dr = 1.0;
      float r = 0.0;
      trap = 1000.0;

      // Constant C for Julia set (animating slightly makes it alive)
      vec3 c = vec3(0.35, 0.45, -0.1) + vec3(sin(time*0.1)*0.2);

      for (int i = 0; i < 8; i++) {
          r = length(z);
          if (r > 100.0) break; // Higher escape radius for Julia
          trap = min(trap, r);

          float theta = acos(z.y / r);
          float phi = atan(z.z, z.x);
          dr = pow(r, power - 1.0) * power * dr + 1.0;
          float zr = pow(r, power);
          theta = theta * power;
          phi = phi * power;
          z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
          z += c; // Add C instead of pos
      }
      return 0.5 * log(r) * r / dr;
  }

  // --- Main Map Dispatcher ---
  float map(vec3 pos) {
      float d = 0.0;
      float currentTrap = 0.0;

      if (fractalType == 1) {
          d = mapMandelbox(pos, currentTrap);
      } else if (fractalType == 2) {
          d = mapJulia(pos, currentTrap);
      } else {
          d = mapMandelbulb(pos, currentTrap);
      }

      minTrap = currentTrap; // Update global
      return d;
  }

  // --- Raymarching ---
  bool intersectSphere(vec3 ro, vec3 rd, vec3 c, float r, out float t0, out float t1) {
    vec3 oc = ro - c;
    float b = dot(oc, rd);
    float c2 = dot(oc, oc) - r * r;
    float h = b*b - c2;
    if (h < 0.0) return false;
    h = sqrt(h);
    t0 = -b - h;
    t1 = -b + h;
    return true;
  }

  float raymarch(vec3 ro, vec3 rd) {
    // Bounding sphere around fractal center (here: origin)
    vec3 center = vec3(0.0);
    float radius = 6.0;

    float tEnter, tExit;
    if (!intersectSphere(ro, rd, center, radius, tEnter, tExit)) {
        return -1.0;
    }

    float t = max(tEnter, 0.0);
    float tMax = tExit;

    for (int i = 0; i < 128; i++) {
        vec3 pos = ro + t * rd;
        float d = map(pos);

        // distance-based epsilon is more stable for zoom-out
        float eps = max(0.001, 0.0005 * t);

        if (d < eps) return t;
        t += d * 0.8;              // safety factor against overshoot
        if (t > tMax) break;
    }
    return -1.0;
}

  vec3 getNormal(vec3 p) {
      float d = map(p);
      vec2 e = vec2(0.001, 0.0);
      return normalize(vec3(
          d - map(p - e.xyy),
          d - map(p - e.yxy),
          d - map(p - e.yyx)
      ));
  }

  void main(void) {
      vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
      vec3 ro = cameraPosition;
      vec3 ta = targetPosition;

      vec3 fwd = normalize(ta - ro);
      vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), fwd));
      vec3 up = normalize(cross(fwd, right));
      vec3 rd = normalize(fwd + uv.x * right + uv.y * up);

      vec3 color = vec3(0.1);

      float t = raymarch(ro, rd);

      if(t > 0.0) {
          vec3 pos = ro + t * rd;
          vec3 nor = getNormal(pos);

          // Different colors for different shapes
          vec3 colParamsA = vec3(0.5, 0.5, 0.5);
          vec3 colParamsB = vec3(0.5, 0.5, 0.5);
          vec3 colParamsC = vec3(1.0, 1.0, 1.0);
          vec3 colParamsD = vec3(0.80, 0.90, 0.30);

          if (fractalType == 1) { // Box: Sci-Fi Blue/Grey
             colParamsD = vec3(0.0, 0.1, 0.2);
          }
          if (fractalType == 2) { // Julia: Alien Green/Purple
             colParamsD = vec3(0.8, 0.0, 0.2);
          }

          vec3 materialColor = palette(minTrap, colParamsA, colParamsB, colParamsC, colParamsD);

          float camLight = max(0.0, dot(nor, -rd));
          float ambient = 0.4;
          vec3 lighting = vec3(1.0) * (camLight * 0.7 + ambient);
          color = materialColor * lighting;
      }

      color = pow(color, vec3(0.8));
      gl_FragColor = vec4(color, 1.0);
  }
`;
