import * as THREE from "three";

function canvasTex(size, draw, repeatX = 1, repeatY = 1) {
  const c = document.createElement("canvas");
  c.width = c.height = size;
  draw(c.getContext("2d"), size);
  const t = new THREE.CanvasTexture(c);
  t.wrapS = t.wrapT = THREE.RepeatWrapping;
  t.repeat.set(repeatX, repeatY);
  t.colorSpace = THREE.SRGBColorSpace;
  t.anisotropy = 8;
  return t;
}

function rand(seed) {
  let s = seed;
  return () => {
    s = (s * 1103515245 + 12345) & 0x7fffffff;
    return s / 0x7fffffff;
  };
}

/* asphalt shingle courses with staggered tabs */
function drawShingles(g, S, base, seed = 7, weather = 0) {
  const r = rand(seed);
  g.fillStyle = base;
  g.fillRect(0, 0, S, S);
  const course = S / 10;
  for (let row = 0; row < 10; row++) {
    const y = row * course;
    const off = (row % 2) * (course * 1.1);
    for (let x = -1; x < 8; x++) {
      const w = course * 2.2;
      const px = x * w + off;
      const shade = 0.82 + r() * 0.3 - weather * r() * 0.25;
      g.fillStyle = `rgba(0,0,0,${Math.max(0, 1 - shade) * 0.9})`;
      g.fillRect(px, y, w - 1.5, course - 1.5);
      if (r() < 0.12 * (1 + weather * 3)) {
        g.fillStyle = `rgba(255,255,255,${0.04 + r() * 0.05})`;
        g.fillRect(px, y, w - 1.5, course - 1.5);
      }
    }
    g.fillStyle = "rgba(0,0,0,.5)";
    g.fillRect(0, y + course - 1.5, S, 1.5);
  }
}

/* hail damage overlay: bruise blotches + torn patches */
function drawStormShingles(g, S) {
  drawShingles(g, S, "#4c4a45", 11, 1);
  const r = rand(41);
  for (let i = 0; i < 7; i++) {
    const x = r() * S, y = r() * S, rad = 18 + r() * 46;
    const gr = g.createRadialGradient(x, y, 2, x, y, rad);
    gr.addColorStop(0, "rgba(140,60,20,.5)");
    gr.addColorStop(0.6, "rgba(120,50,25,.22)");
    gr.addColorStop(1, "rgba(0,0,0,0)");
    g.fillStyle = gr;
    g.fillRect(x - rad, y - rad, rad * 2, rad * 2);
  }
  for (let i = 0; i < 3; i++) {
    const x = r() * S * 0.8, y = r() * S * 0.8;
    g.fillStyle = "rgba(15,14,12,.85)";
    g.fillRect(x, y, 26 + r() * 30, 12 + r() * 10);
  }
}

function drawClapboard(g, S, base, shadow) {
  const board = S / 12;
  for (let i = 0; i < 12; i++) {
    const y = i * board;
    const gr = g.createLinearGradient(0, y, 0, y + board);
    gr.addColorStop(0, base);
    gr.addColorStop(0.85, base);
    gr.addColorStop(1, shadow);
    g.fillStyle = gr;
    g.fillRect(0, y, S, board);
  }
  const r = rand(5);
  g.fillStyle = "rgba(0,0,0,.05)";
  for (let i = 0; i < 40; i++) g.fillRect(r() * S, r() * S, 1.5, 4 + r() * 8);
}

function drawBrick(g, S) {
  g.fillStyle = "#8a8078";
  g.fillRect(0, 0, S, S);
  const bh = S / 10, bw = S / 4;
  const r = rand(23);
  for (let row = 0; row < 10; row++) {
    const off = (row % 2) * bw * 0.5;
    for (let col = -1; col < 5; col++) {
      const tone = 96 + r() * 46;
      g.fillStyle = `rgb(${tone + 20},${tone * 0.62},${tone * 0.5})`;
      g.fillRect(col * bw + off + 2, row * bh + 2, bw - 4, bh - 4);
    }
  }
}

function drawConcrete(g, S, tone = 168) {
  g.fillStyle = `rgb(${tone},${tone},${tone - 4})`;
  g.fillRect(0, 0, S, S);
  const r = rand(3);
  for (let i = 0; i < 900; i++) {
    const v = r();
    g.fillStyle = v > 0.5 ? "rgba(255,255,255,.05)" : "rgba(0,0,0,.06)";
    g.fillRect(r() * S, r() * S, 1 + r() * 2, 1 + r() * 2);
  }
}

function drawAsphalt(g, S) {
  g.fillStyle = "#292b2a";
  g.fillRect(0, 0, S, S);
  const r = rand(17);
  for (let i = 0; i < 1200; i++) {
    const v = r();
    g.fillStyle = v > 0.55 ? "rgba(255,255,255,.05)" : "rgba(0,0,0,.12)";
    g.fillRect(r() * S, r() * S, 1 + r() * 2, 1 + r() * 2);
  }
}

/* grass detail and the radial alpha falloff baked into ONE texture (repeat 1x1),
   because standard materials share the map's UV transform across texture slots */
function drawGrassDisc(g, S) {
  g.fillStyle = "#242d1b";
  g.fillRect(0, 0, S, S);
  const r = rand(9);
  for (let i = 0; i < 26000; i++) {
    const v = r();
    const c = v > 0.66 ? "rgba(58,74,40,.5)" : v > 0.33 ? "rgba(34,44,26,.55)" : "rgba(20,26,16,.5)";
    g.fillStyle = c;
    g.fillRect(r() * S, r() * S, 1 + r() * 2.4, 1 + r() * 2);
  }
  /* mow lines */
  g.fillStyle = "rgba(255,255,255,.016)";
  for (let i = 0; i < 14; i++) g.fillRect(0, i * (S / 14), S, S / 28);
  /* carve the radial fade into the alpha channel */
  g.globalCompositeOperation = "destination-in";
  const gr = g.createRadialGradient(S / 2, S / 2, S * 0.18, S / 2, S / 2, S * 0.5);
  gr.addColorStop(0, "rgba(0,0,0,1)");
  gr.addColorStop(0.5, "rgba(0,0,0,1)");
  gr.addColorStop(0.78, "rgba(0,0,0,.45)");
  gr.addColorStop(0.98, "rgba(0,0,0,0)");
  g.fillStyle = gr;
  g.fillRect(0, 0, S, S);
  g.globalCompositeOperation = "source-over";
}

/* street strip: asphalt with lane dashes, alpha-faded at both ends */
export function drawRoad(g, S) {
  drawAsphalt(g, S);
  g.fillStyle = "rgba(210,200,160,.5)";
  for (let i = 0; i < 9; i++) g.fillRect(i * (S / 9) + S / 36, S * 0.485, S / 18, S * 0.03);
  g.fillStyle = "rgba(220,220,220,.28)";
  g.fillRect(0, S * 0.06, S, S * 0.016);
  g.fillRect(0, S * 0.924, S, S * 0.016);
  g.globalCompositeOperation = "destination-in";
  const gr = g.createLinearGradient(0, 0, S, 0);
  gr.addColorStop(0, "rgba(0,0,0,0)");
  gr.addColorStop(0.18, "rgba(0,0,0,1)");
  gr.addColorStop(0.82, "rgba(0,0,0,1)");
  gr.addColorStop(1, "rgba(0,0,0,0)");
  g.fillStyle = gr;
  g.fillRect(0, 0, S, S);
  g.globalCompositeOperation = "source-over";
}

/* soft dark contact blob that grounds whatever stands on it */
export function drawContact(g, S) {
  const gr = g.createRadialGradient(S / 2, S / 2, S * 0.05, S / 2, S / 2, S * 0.5);
  gr.addColorStop(0, "rgba(0,0,0,.62)");
  gr.addColorStop(0.55, "rgba(0,0,0,.34)");
  gr.addColorStop(1, "rgba(0,0,0,0)");
  g.fillStyle = gr;
  g.fillRect(0, 0, S, S);
}

function drawGaragePanels(g, S) {
  g.fillStyle = "#d8d6d0";
  g.fillRect(0, 0, S, S);
  const ph = S / 4;
  for (let i = 0; i < 4; i++) {
    const y = i * ph;
    const gr = g.createLinearGradient(0, y, 0, y + ph);
    gr.addColorStop(0, "rgba(0,0,0,.22)");
    gr.addColorStop(0.14, "rgba(255,255,255,.16)");
    gr.addColorStop(0.85, "rgba(0,0,0,.05)");
    gr.addColorStop(1, "rgba(0,0,0,.34)");
    g.fillStyle = gr;
    g.fillRect(0, y, S, ph);
  }
}

/* soft elliptical glow for the LiDAR sheet: fades before every plane edge */
function drawScanBand(g, S) {
  const gr = g.createRadialGradient(S / 2, S / 2, S * 0.05, S / 2, S / 2, S * 0.5);
  gr.addColorStop(0, "rgba(255,214,90,.85)");
  gr.addColorStop(0.45, "rgba(255,184,0,.34)");
  gr.addColorStop(1, "rgba(255,184,0,0)");
  g.fillStyle = gr;
  g.fillRect(0, 0, S, S);
}

export function makeMaterials() {
  const std = (o) => new THREE.MeshStandardMaterial(o);
  const shingleTex = canvasTex(256, (g, s) => drawShingles(g, s, "#2b2e32", 7), 6, 3);
  const shingleWarmTex = canvasTex(256, (g, s) => drawShingles(g, s, "#352e26", 19), 6, 3);
  const stormTex = canvasTex(512, (g, s) => drawStormShingles(g, s), 3, 1.6);
  return {
    shingle: std({ map: shingleTex, roughness: 0.92, metalness: 0.02 }),
    shingleWarm: std({ map: shingleWarmTex, roughness: 0.92, metalness: 0.02 }),
    shingleStorm: std({ map: stormTex, roughness: 0.95, metalness: 0.02 }),
    sidingWhite: std({ map: canvasTex(256, (g, s) => drawClapboard(g, s, "#c9c6bd", "#8f8d85"), 3, 3), roughness: 0.85 }),
    sidingBeige: std({ map: canvasTex(256, (g, s) => drawClapboard(g, s, "#b3a58c", "#7d7360"), 3, 3), roughness: 0.85 }),
    sidingGray: std({ map: canvasTex(256, (g, s) => drawClapboard(g, s, "#9aa0a2", "#6a6f71"), 3, 3), roughness: 0.85 }),
    trim: std({ color: 0xe8e6df, roughness: 0.6 }),
    fascia: std({ color: 0xdad7cf, roughness: 0.65 }),
    doorRed: std({ color: 0x7e2f26, roughness: 0.5 }),
    doorBlue: std({ color: 0x2c3f4d, roughness: 0.5 }),
    glass: std({ color: 0x0e1a22, roughness: 0.08, metalness: 0.85, envMapIntensity: 1.4 }),
    garage: std({ map: canvasTex(256, drawGaragePanels, 1, 1), roughness: 0.6 }),
    brick: std({ map: canvasTex(256, drawBrick, 1.4, 2.4), roughness: 0.95 }),
    concrete: std({ map: canvasTex(256, (g, s) => drawConcrete(g, s), 2, 2), roughness: 0.95 }),
    asphalt: std({ map: canvasTex(256, drawAsphalt, 2, 3), roughness: 0.98 }),
    grass: std({
      map: canvasTex(2048, drawGrassDisc),
      transparent: true,
      roughness: 1,
      depthWrite: false,
    }),
    soil: std({ color: 0x2b2822, roughness: 1 }),
    trunk: std({ color: 0x3c2f21, roughness: 1 }),
    foliage: std({ color: 0x33431f, roughness: 1, flatShading: true }),
    foliage2: std({ color: 0x415229, roughness: 1, flatShading: true }),
    contact: new THREE.MeshBasicMaterial({
      map: canvasTex(512, drawContact),
      transparent: true,
      depthWrite: false,
    }),
    road: std({ map: canvasTex(512, drawRoad), transparent: true, roughness: 0.98, depthWrite: false }),
    tarp: std({ color: 0x27506e, roughness: 0.55, side: THREE.DoubleSide }),
    porchRoof: std({ color: 0x3a3d40, roughness: 0.7, metalness: 0.25 }),
    hlAmber: new THREE.MeshBasicMaterial({ color: 0xffb800, transparent: true, opacity: 0.95 }),
    hlFace: new THREE.MeshBasicMaterial({ color: 0xffc233, transparent: true, opacity: 0.62, side: THREE.DoubleSide, depthWrite: false }),
    impact: std({ color: 0xff5533, emissive: 0xd23c14, emissiveIntensity: 0.9, roughness: 0.6 }),
    scan: new THREE.MeshBasicMaterial({
      map: canvasTex(256, drawScanBand),
      transparent: true,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
      side: THREE.DoubleSide,
    }),
  };
}

/* alpha-fade note: the grass disc uses alphaMap + depthWrite:false so the lawn
   dissolves radially into the page background instead of ending in a hard edge */
