/* ============================================================
   $LAND — low-poly isometric tile generator (SVG facets)
   Exposes window.Land with:
     Land.parcelSVG(opts)   -> string  (faceted land chunk like the logo)
     Land.buildingSVG(key,opts) -> string (land chunk + iso building)
     Land.Tile (React component wrapper)
   ============================================================ */
(function () {
  // ---- palette ramps (sampled from logo.png) -------------------------
  const GRASS = ['#a8cc57', '#93bb50', '#79ad4d', '#69a255', '#54925a', '#457d53', '#3a6b4c'];
  const WATER = ['#a6d8ea', '#82c5e0', '#5cb1d8', '#44a3cf', '#3793c4', '#2f7fb0'];
  const DIRT  = ['#c2af98', '#b5a18c', '#9a8470', '#856c56', '#6f5a47', '#574636', '#46392c'];

  // ---- seeded RNG + smooth value noise -------------------------------
  function mulberry32(a) {
    return function () {
      a |= 0; a = (a + 0x6D2B79F5) | 0;
      let t = Math.imul(a ^ (a >>> 15), 1 | a);
      t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
      return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
    };
  }
  function makeNoise(seed) {
    const rng = mulberry32(seed >>> 0);
    const a = [];
    for (let i = 0; i < 5; i++) a.push({ fx: 0.5 + rng() * 1.8, fy: 0.5 + rng() * 1.8, p: rng() * 6.283, w: 0.4 + rng() });
    const wsum = a.reduce((s, o) => s + o.w, 0);
    return (x, y) => {
      let v = 0;
      for (const o of a) v += o.w * Math.sin(o.fx * x + o.p) * Math.cos(o.fy * y + o.p * 0.7);
      return v / wsum; // ~ -1..1
    };
  }
  const clampIdx = (i, n) => Math.max(0, Math.min(n - 1, i));

  // ---- isometric projection ------------------------------------------
  // grid (gx,gy) on top surface, z up. 2:1 iso.
  function projector(scale, ox, oy) {
    return (gx, gy, z) => [ox + (gx - gy) * scale, oy + (gx + gy) * scale * 0.5 - z * scale];
  }
  const ptStr = (p) => p[0].toFixed(1) + ',' + p[1].toFixed(1);
  function poly(pts, fill, extra) {
    return '<polygon points="' + pts.map(ptStr).join(' ') + '" fill="' + fill + '"' + (extra || '') + '/>';
  }

  // ============================================================
  //  PARCEL — faceted flat-top land chunk (the logo motif)
  // ============================================================
  function parcelGeometry(opts) {
    opts = opts || {};
    const N = opts.subdiv || 6;
    const seed = opts.seed == null ? 7 : opts.seed;
    const lake = opts.lake == null ? 1 : opts.lake;          // 0 = none, ~1 normal
    const noise = makeNoise(seed);
    const lakeNoise = makeNoise(seed * 7 + 3);
    const cx = N * 0.5, cy = N * 0.5;
    const lakeR = N * 0.30 * lake;

    function inLake(x, y) {
      if (lake <= 0) return false;
      const dx = x - cx, dy = y - cy;
      const ang = Math.atan2(dy, dx);
      const r = lakeR * (0.78 + 0.34 * (lakeNoise(Math.cos(ang) * 2 + 5, Math.sin(ang) * 2 + 5) * 0.5 + 0.5));
      return Math.hypot(dx, dy * 1.05) < r;
    }

    const tris = []; // {a,b,c,fill,layer:'top'|'side'}
    // top surface — each grid cell -> 2 triangles, shaded by noise
    for (let gx = 0; gx < N; gx++) {
      for (let gy = 0; gy < N; gy++) {
        const cells = [
          [[gx, gy], [gx + 1, gy], [gx + 1, gy + 1]],
          [[gx, gy], [gx + 1, gy + 1], [gx, gy + 1]],
        ];
        cells.forEach((t, k) => {
          const mx = (t[0][0] + t[1][0] + t[2][0]) / 3;
          const my = (t[0][1] + t[1][1] + t[2][1]) / 3;
          const water = inLake(mx, my);
          const ramp = water ? WATER : GRASS;
          let n = noise(mx * 0.9, my * 0.9) * 0.5 + 0.5; // 0..1
          n += (k === 0 ? 0.06 : -0.06);                 // facet split
          if (water) n = lakeNoise(mx, my) * 0.5 + 0.5;
          const idx = clampIdx(Math.round(n * (ramp.length - 1)), ramp.length);
          tris.push({ a: [t[0][0], t[0][1], 0], b: [t[1][0], t[1][1], 0], c: [t[2][0], t[2][1], 0], fill: ramp[idx], layer: 'top' });
        });
      }
    }
    return { N, tris, inLake, noise, seed };
  }

  function renderParcel(opts) {
    opts = opts || {};
    const size = opts.size || 200;
    const depth = opts.depth == null ? 1.5 : opts.depth; // side height in grid units
    const g = parcelGeometry(opts);
    const N = g.N;
    const sideNoise = makeNoise(g.seed * 13 + 1);

    // provisional projector to size; compute bbox then refit
    const scale0 = size / (N * 2);
    let project = projector(scale0, 0, 0);
    // bbox over all corners incl. depth
    const corners = [[0, 0, 0], [N, 0, 0], [N, N, 0], [0, N, 0], [0, N, -depth], [N, N, -depth], [N, 0, -depth]];
    let minX = 1e9, maxX = -1e9, minY = 1e9, maxY = -1e9;
    corners.forEach(c => { const p = project(c[0], c[1], c[2]); minX = Math.min(minX, p[0]); maxX = Math.max(maxX, p[0]); minY = Math.min(minY, p[1]); maxY = Math.max(maxY, p[1]); });
    const pad = size * 0.06;
    const bw = maxX - minX, bh = maxY - minY;
    const s = (size - pad * 2) / Math.max(bw, bh) * scale0;
    project = projector(s, -((minX + maxX) / 2) * (s / scale0) + size / 2, -((minY + maxY) / 2) * (s / scale0) + size / 2);

    let out = '';
    // ---- side faces (draw first, behind top) ----
    // front-right face: edge gx=N, gy 0..N
    for (let gy = 0; gy < N; gy++) {
      const n = sideNoise(gy * 0.8 + 2, 1) * 0.5 + 0.5;
      const idx = clampIdx(2 + Math.round(n * 2), DIRT.length);
      out += poly([project(N, gy, 0), project(N, gy + 1, 0), project(N, gy + 1, -depth), project(N, gy, -depth)], DIRT[idx]);
    }
    // front-left face: edge gy=N, gx 0..N (darker)
    for (let gx = 0; gx < N; gx++) {
      const n = sideNoise(gx * 0.8 + 7, 5) * 0.5 + 0.5;
      const idx = clampIdx(3 + Math.round(n * 2), DIRT.length);
      out += poly([project(gx, N, 0), project(gx + 1, N, 0), project(gx + 1, N, -depth), project(gx, N, -depth)], DIRT[idx]);
    }
    // bottom front edge highlight (thin)
    out += poly([project(N, N, 0), project(N, N, -depth), project(N, N, -depth)], DIRT[5]);

    // ---- top faces ----
    for (const t of g.tris) {
      out += poly([project(t.a[0], t.a[1], 0), project(t.b[0], t.b[1], 0), project(t.c[0], t.c[1], 0)], t.fill);
    }

    return { svgInner: out, project, N, depth };
  }

  function parcelSVG(opts) {
    opts = opts || {};
    const size = opts.size || 200;
    const r = renderParcel(opts);
    return wrap(size, r.svgInner, opts);
  }

  // ============================================================
  //  ISO BUILDING primitives (boxes + roofs)
  // ============================================================
  function box(project, gx, gy, gw, gd, z0, gh, cols) {
    const z1 = z0 + gh;
    const top = [project(gx, gy, z1), project(gx + gw, gy, z1), project(gx + gw, gy + gd, z1), project(gx, gy + gd, z1)];
    const left = [project(gx, gy + gd, z1), project(gx + gw, gy + gd, z1), project(gx + gw, gy + gd, z0), project(gx, gy + gd, z0)];
    const right = [project(gx + gw, gy, z1), project(gx + gw, gy + gd, z1), project(gx + gw, gy + gd, z0), project(gx + gw, gy, z0)];
    return poly(right, cols.right) + poly(left, cols.left) + poly(top, cols.top);
  }
  // gable roof running along gx (ridge at mid gy)
  function gable(project, gx, gy, gw, gd, z0, rh, cols) {
    const ridgeY = gy + gd / 2;
    const zr = z0 + rh;
    const frontR = [project(gx, gy, z0), project(gx + gw, gy, z0), project(gx + gw, ridgeY, zr), project(gx, ridgeY, zr)];
    const backR = [project(gx, gy + gd, z0), project(gx + gw, gy + gd, z0), project(gx + gw, ridgeY, zr), project(gx, ridgeY, zr)];
    const gableEnd = [project(gx + gw, gy, z0), project(gx + gw, gy + gd, z0), project(gx + gw, ridgeY, zr)];
    return poly(backR, cols.back || cols.roof) + poly(gableEnd, cols.gable) + poly(frontR, cols.roof);
  }
  function pyramid(project, gx, gy, gw, gd, z0, rh, cols) {
    const apex = project(gx + gw / 2, gy + gd / 2, z0 + rh);
    const A = project(gx, gy, z0), B = project(gx + gw, gy, z0), C = project(gx + gw, gy + gd, z0), D = project(gx, gy + gd, z0);
    // visible: front-left (D-C) and front-right (B-C)
    return poly([B, C, apex], cols.r || cols.roof) + poly([D, C, apex], cols.roof) + poly([A, B, apex], cols.back || cols.roof);
  }
  function shade(hex, f) {
    const c = hex.replace('#', '');
    let r = parseInt(c.slice(0, 2), 16), g = parseInt(c.slice(2, 4), 16), b = parseInt(c.slice(4, 6), 16);
    r = Math.round(Math.max(0, Math.min(255, r * f))); g = Math.round(Math.max(0, Math.min(255, g * f))); b = Math.round(Math.max(0, Math.min(255, b * f)));
    return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');
  }
  function wallCols(base) { return { top: shade(base, 1.12), left: shade(base, 0.74), right: shade(base, 0.93) }; }
  function roofCols(base) { return { roof: shade(base, 1.0), back: shade(base, 0.78), gable: shade(base, 0.86), r: shade(base, 0.84) }; }

  // building definitions — drawn centered on a parcel top (grid 0..N)
  const STONE = '#c9bda6', STONE2 = '#b3a489', WOOD = '#9c6b3f', WOODD = '#7c5230';
  const BUILDINGS = {
    HALL: { accent: '#d9b24a', base: STONE, name: 'Town Hall' },
    KEEP: { accent: '#8a8f98', base: STONE2, name: 'Keep' },
    HOME: { accent: '#c25b3f', base: '#d8c39c', name: 'House' },
    MART: { accent: '#cf7b3a', base: '#caa978', name: 'Market' },
    FORGE: { accent: '#d4542a', base: '#7d6f63', name: 'Forge' },
    TEMPLE: { accent: '#7a6bd0', base: '#e0d8c4', name: 'Temple' },
    PORT: { accent: '#3f86b8', base: '#b79a6e', name: 'Harbor' },
    FARM: { accent: '#c8a23a', base: '#b8472f', name: 'Farmstead' },
  };

  function drawBuilding(key, project, N) {
    const def = BUILDINGS[key] || BUILDINGS.HOME;
    const m = N / 2; // center
    const wood = wallCols(WOOD), woodd = wallCols(WOODD);
    let s = '';
    const z = 0; // building sits on top surface (z=0 plane)
    switch (key) {
      case 'HALL': {
        s += box(project, m - 1.6, m - 1.1, 3.2, 2.2, z, 1.5, wallCols(def.base));
        s += gable(project, m - 1.7, m - 1.2, 3.4, 2.4, z + 1.5, 1.0, roofCols('#b03f33'));
        // clock tower
        s += box(project, m - 0.45, m - 0.4, 0.9, 0.8, z, 2.9, wallCols(shade(def.base, 1.05)));
        s += pyramid(project, m - 0.5, m - 0.45, 1.0, 0.9, z + 2.9, 0.9, roofCols(def.accent));
        // flag
        s += line(project, m, m, z + 3.8, 0.9, def.accent);
        s += flag(project, m, m, z + 4.6, def.accent);
        break;
      }
      case 'KEEP': {
        s += box(project, m - 1.4, m - 1.4, 2.8, 2.8, z, 1.2, wallCols(def.base));
        // tall tower with crenellations
        s += box(project, m - 0.9, m - 0.9, 1.8, 1.8, z + 1.2, 2.4, wallCols(shade(def.base, 0.98)));
        // battlement blocks
        for (let i = 0; i < 3; i++) for (let j = 0; j < 3; j++) {
          if ((i + j) % 2 === 0) s += box(project, m - 0.9 + i * 0.6, m - 0.9 + j * 0.6, 0.45, 0.45, z + 3.6, 0.45, wallCols(shade(def.base, 0.92)));
        }
        s += line(project, m, m, z + 3.6, 1.2, def.accent);
        s += flag(project, m, m, z + 4.4, def.accent);
        break;
      }
      case 'HOME': {
        s += box(project, m - 1.1, m - 0.9, 2.2, 1.8, z, 1.2, wallCols(def.base));
        s += gable(project, m - 1.2, m - 1.0, 2.4, 2.0, z + 1.2, 1.1, roofCols(def.accent));
        s += box(project, m + 0.5, m - 0.2, 0.4, 0.4, z + 1.2, 1.2, wallCols('#8d7a63')); // chimney
        break;
      }
      case 'MART': {
        s += box(project, m - 1.5, m - 1.0, 3.0, 2.0, z, 0.7, wallCols(def.base));
        // striped awnings (two boxes)
        s += box(project, m - 1.7, m - 1.2, 1.5, 2.4, z + 0.7, 0.18, { top: def.accent, left: shade(def.accent, 0.8), right: shade(def.accent, 0.9) });
        s += box(project, m + 0.2, m - 1.2, 1.5, 2.4, z + 0.7, 0.18, { top: '#e8e2d0', left: '#cfc8b4', right: '#ddd6c4' });
        // crates
        s += box(project, m - 1.2, m + 1.0, 0.6, 0.6, z, 0.6, wood);
        s += box(project, m + 0.4, m + 1.0, 0.6, 0.6, z, 0.6, woodd);
        break;
      }
      case 'FORGE': {
        s += box(project, m - 1.3, m - 1.0, 2.6, 2.0, z, 1.3, wallCols(def.base));
        s += gable(project, m - 1.4, m - 1.1, 2.8, 2.2, z + 1.3, 0.7, roofCols('#4a4039'));
        // big chimney with glow
        s += box(project, m + 0.4, m - 0.6, 0.7, 0.7, z, 2.6, wallCols('#5a4a40'));
        s += box(project, m + 0.45, m - 0.55, 0.6, 0.6, z + 2.6, 0.3, { top: def.accent, left: shade(def.accent, 0.8), right: shade(def.accent, 0.9) });
        break;
      }
      case 'TEMPLE': {
        s += box(project, m - 1.4, m - 1.1, 2.8, 2.2, z, 1.6, wallCols(def.base));
        // columns hint
        s += pyramid(project, m - 1.5, m - 1.2, 3.0, 2.4, z + 1.6, 0.5, roofCols('#cfc6b0'));
        // tall spire
        s += box(project, m - 0.5, m - 0.45, 1.0, 0.9, z + 2.1, 1.4, wallCols(shade(def.base, 1.02)));
        s += pyramid(project, m - 0.6, m - 0.55, 1.2, 1.1, z + 3.5, 1.8, roofCols(def.accent));
        break;
      }
      case 'PORT': {
        // water deck
        s += box(project, m - 1.7, m - 1.7, 3.4, 3.4, z - 0.4, 0.18, { top: '#5cb1d8', left: '#3793c4', right: '#44a3cf' });
        // pier
        s += box(project, m - 1.4, m - 0.5, 2.8, 1.0, z, 0.25, wood);
        // warehouse
        s += box(project, m - 0.4, m - 1.3, 1.8, 1.4, z + 0.25, 1.2, wallCols(def.base));
        s += gable(project, m - 0.5, m - 1.4, 2.0, 1.6, z + 1.45, 0.7, roofCols(def.accent));
        // boat hull
        s += box(project, m + 0.6, m + 0.5, 1.4, 0.7, z + 0.18, 0.4, wallCols('#8a5a34'));
        s += line(project, m + 1.3, m + 0.85, z + 0.58, 1.2, '#cfc6b0');
        break;
      }
      case 'FARM': {
        // fields
        s += box(project, m - 1.8, m + 0.2, 3.6, 1.6, z, 0.06, { top: shade(def.accent, 1.05), left: shade(def.accent, 0.8), right: shade(def.accent, 0.92) });
        // barn
        s += box(project, m - 1.4, m - 1.5, 2.4, 1.8, z, 1.2, wallCols(def.base));
        s += gable(project, m - 1.5, m - 1.6, 2.6, 2.0, z + 1.2, 1.0, roofCols('#8d3a2a'));
        // silo
        s += box(project, m + 0.9, m - 1.2, 0.9, 0.9, z, 1.9, wallCols('#cdbf9e'));
        s += pyramid(project, m + 0.85, m - 1.25, 1.0, 1.0, z + 1.9, 0.5, roofCols('#9aa0a8'));
        break;
      }
      default: {
        s += box(project, m - 1, m - 1, 2, 2, z, 1.2, wallCols(def.base));
      }
    }
    return s;
  }
  function line(project, gx, gy, z0, len, col) {
    const a = project(gx, gy, z0), b = project(gx, gy, z0 + len);
    return '<line x1="' + a[0].toFixed(1) + '" y1="' + a[1].toFixed(1) + '" x2="' + b[0].toFixed(1) + '" y2="' + b[1].toFixed(1) + '" stroke="' + (col || '#6b5b46') + '" stroke-width="2.2" stroke-linecap="round"/>';
  }
  function flag(project, gx, gy, z0, col) {
    const a = project(gx, gy, z0), b = project(gx + 1.0, gy, z0 + 0.18), c = project(gx, gy, z0 - 0.5);
    return poly([a, b, c], col);
  }

  // ============================================================
  //  ASSEMBLERS
  // ============================================================
  function wrap(size, inner, opts) {
    opts = opts || {};
    const grad = opts.shadow === false ? '' :
      '<ellipse cx="' + (size / 2) + '" cy="' + (size * 0.9) + '" rx="' + (size * 0.34) + '" ry="' + (size * 0.08) + '" fill="rgba(70,57,44,0.18)"/>';
    return '<svg viewBox="0 0 ' + size + ' ' + size + '" xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" style="display:block">' +
      grad + inner + '</svg>';
  }

  function buildingSVG(key, opts) {
    opts = opts || {};
    const size = opts.size || 220;
    const base = renderParcel(Object.assign({ lake: 0, subdiv: 5, seed: (opts.seed || 1) + 99 }, opts, { size }));
    let inner = base.svgInner;
    inner += drawBuilding(key, base.project, base.N);
    return wrap(size, inner, opts);
  }

  // ---- React wrapper --------------------------------------------------
  // Real art: parcels -> assets/tiles/{rarity}-{variant}.png (the 25 NFT tiles),
  // buildings -> assets/buildings/{KEY}.png. The old generative SVG path is kept
  // below (parcelSVG/buildingSVG) but no longer used by the UI.
  const RARITIES = ['common', 'uncommon', 'rare', 'epic', 'legendary'];
  function tileSrc(props) {
    if (props.kind === 'building') {
      const key = (props.building || 'HOME').toUpperCase();
      return '/app/assets/buildings/' + key + '.png';
    }
    const rarity = RARITIES.includes(props.rarity) ? props.rarity : 'common';
    const variant = (Math.abs(((props.seed || 0) * 2654435761) >>> 0) % 5) + 1; // 1..5
    return '/app/assets/tiles/' + rarity + '-' + variant + '.png';
  }
  function Tile(props) {
    const { className, style, size } = props;
    const src = tileSrc(props);
    return React.createElement('div', {
      className,
      style: Object.assign({ lineHeight: 0, display: 'inline-block' }, style),
    }, React.createElement('img', {
      src, alt: '', draggable: false,
      style: { width: size ? size : '100%', height: 'auto', display: 'block', imageRendering: 'auto' },
    }));
  }

  window.Land = { parcelSVG, buildingSVG, Tile, GRASS, WATER, DIRT, BUILDINGS };
})();
