// Каталог пиксель-арт спрайтов сада (ТЗ 3.13). Геометрия строится программно
// (эллипсы, стволы, крыши) — надёжнее, чем ASCII-матрицы переменной ширины.
// Всё рисуется в матрицах «нативного пикселя» (клетка = 16px, мир = ×2).
import { blank, flipX, stamp } from './pixelart'
import type { Matrix, Palette } from './pixelart'

/** Палитра сцены — тон в тон тёмной теме (фон ~#1a1f2e). */
export const PAL: Palette = {
  g: '#2c4a36', // трава тёмная (холодный оттенок)
  G: '#365a41', // трава
  H: '#40674b', // трава светлая / травинки
  w: '#3b3226', // дикая земля
  W: '#45392b', // дикая земля светлая
  e: '#35682c', // изгородь
  E: '#4a8a3c', // изгородь светлая
  F: '#254c1f', // изгородь тень
  o: '#8a6d4b', // дерево (постройки)
  O: '#6b5238', // дерево тёмное
  r: '#c96a7c', // крыша
  R: '#a35264', // крыша тень
  n: '#8f7a4f', // стена
  N: '#6e5c3c', // стена тень
  d: '#5b74c9', // дверь
  c: '#5fc4b4', // окно
  C: '#f5d76e', // свет (окна, фонарь)
  u: '#4a6fd4', // вода
  U: '#7aa2f7', // вода светлая
  b: '#6b4f3a', // клумба (земля)
  S: '#b3a26b', // песок берега
  s: '#4a3826', // почва тёмная
  l: '#4a8f3c', // листва
  L: '#5fae4b', // листва светлая
  k: '#356b2c', // листва тёмная
  t: '#3f7a33', // стебель
  p: '#f7768e', // цветок розовый
  P: '#bb9af7', // цветок лиловый
  y: '#e0af68', // золото (редкость)
  h: '#d6dae4', // белый
  x: '#7d8496', // камень
  q: '#f5d76e', // свечение (эпик)
  m: '#243022', // тень на земле
  i: '#c9b98a', // дорожка (гравий светлый)
  I: '#9a8a5f', // дорожка тень
  f: '#e8903a', // огонь / рябина
  Z: '#f5b04a', // огонь светлая
  v: '#7c5fd4', // звёздный/бабочий (сирень)
  V: '#a48ae8', // сирень светлая
  B: '#63b3e8', // голубой (роса)
  A: '#8a5a3c', // янтарь (ягоды)
  j: '#c9e06e', // подсолнух жёлтый
  J: '#f0d75e', // подсолнух светлая
  z: '#3e8f82', // стекло в тени (теплица)
}

/** Поставить пиксель (без выхода за границы). */
function put(m: Matrix, x: number, y: number, ch: string): void {
  const row = m[y]
  if (row && x >= 0 && x < row.length) m[y] = row.substring(0, x) + ch + row.substring(x + 1)
}

/** Заполненный эллипс. */
function ellipse(m: Matrix, cx: number, cy: number, rx: number, ry: number, ch: string): void {
  for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++) {
    for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {
      const dx = (x - cx) / rx
      const dy = (y - cy) / ry
      if (dx * dx + dy * dy <= 1) put(m, x, y, ch)
    }
  }
}

/** Вертикальная линия. */
function vline(m: Matrix, x: number, y0: number, y1: number, ch: string): void {
  for (let y = y0; y <= y1; y++) put(m, x, y, ch)
}

/** Горизонтальная полоса. */
function hline(m: Matrix, x0: number, x1: number, y: number, ch: string): void {
  for (let x = x0; x <= x1; x++) put(m, x, y, ch)
}

/** Заливка полигона сканлайнами (even-odd) — для изометрических граней. */
function fillPoly(m: Matrix, pts: [number, number][], ch: string): void {
  const ys = pts.map((p) => p[1])
  const y0 = Math.max(0, Math.ceil(Math.min(...ys)))
  const y1 = Math.min(m.length - 1, Math.floor(Math.max(...ys)))
  for (let y = y0; y <= y1; y++) {
    const xs: number[] = []
    for (let i = 0; i < pts.length; i++) {
      const [x1, y1] = pts[i]!
      const [x2, y2] = pts[(i + 1) % pts.length]!
      if (y1 === y2) continue
      if (y >= Math.min(y1, y2) && y < Math.max(y1, y2)) {
        xs.push(x1 + ((y - y1) / (y2 - y1)) * (x2 - x1))
      }
    }
    xs.sort((a, b) => a - b)
    for (let i = 0; i + 1 < xs.length; i += 2) {
      const xa = Math.ceil(xs[i]!)
      const xb = Math.floor(xs[i + 1]!)
      for (let x = xa; x <= xb; x++) put(m, x, y, ch)
    }
  }
}

/** Вертикальная грань-параллелограмм между двумя нижними точками (высота h вверх). */
function wall(m: Matrix, x1: number, y1: number, x2: number, y2: number, h: number, ch: string): void {
  fillPoly(
    m,
    [
      [x1, y1],
      [x2, y2],
      [x2, y2 - h],
      [x1, y1 - h],
    ],
    ch,
  )
}

/** Шахматная растушёвка края эллипса: переход полосы frac к цвету ch (' ' — фон). */
function feather(
  m: Matrix,
  cx: number,
  cy: number,
  rx: number,
  ry: number,
  frac: number,
  ch: string,
): void {
  for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++)
    for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {
      const d = ((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2
      if (d > 1 - frac && d <= 1 && (x + y) % 2 === 0) put(m, x, y, ch)
    }
}

// --- Тайлы (16×16, бесшовные) ---

function speckle(m: Matrix, seed: number, chs: string[]): void {
  // детерминированный «шум» внутри 2..13 — края чистые (бесшовность)
  for (let i = 0; i < 5; i++) {
    const x = 2 + ((seed * 7 + i * 29) % 12)
    const y = 2 + ((seed * 13 + i * 17) % 12)
    put(m, x, y, chs[i % chs.length]!)
  }
}

function grassTile(seed: number, base = 'g'): Matrix {
  const m = blank(16, 16).map(() => base.repeat(16)) as Matrix
  // фактура: светлые травинки и полутона (детерминированный «шум» в 2..13,
  // края чистые — бесшовность при тайлировании)
  for (let i = 0; i < 8; i++) {
    const x = 2 + ((seed * 7 + i * 29) % 12)
    const y = 2 + ((seed * 13 + i * 17) % 12)
    put(m, x, y, i % 3 === 0 ? 'H' : 'G')
  }
  // редкие тёмные пятнышки почвы
  for (let i = 0; i < 2; i++) {
    const x = 2 + ((seed * 11 + i * 23) % 12)
    const y = 2 + ((seed * 5 + i * 31) % 12)
    put(m, x, y, 'k')
  }
  return m
}

export const GRASS_A = grassTile(1)
export const GRASS_B = grassTile(2)
export const GRASS_C = grassTile(5)
export const GRASS_D = grassTile(9)

function checker2(a: Matrix, b: Matrix): Matrix {
  // 32×32: шахматка 2×2 из 16px тайлов
  const rows: Matrix = []
  for (let y = 0; y < 32; y++) {
    const left = y < 16 ? a[y]! : b[y - 16]!
    const right = y < 16 ? b[y]! : a[y - 16]!
    rows.push(left + right)
  }
  return rows
}

/** Супер-тайл 64×64: четыре разных тайла, период 4 клетки — вариативность без рваного шума. */
export const GRASS_SUPER: Matrix = (() => {
  const tl = checker2(GRASS_A, GRASS_B)
  const tr = checker2(GRASS_C, GRASS_D)
  const bl = checker2(GRASS_D, GRASS_C)
  const br = checker2(GRASS_B, GRASS_A)
  const rows: Matrix = []
  for (let y = 0; y < 64; y++) {
    rows.push((y < 32 ? tl[y]! : bl[y - 32]!) + (y < 32 ? tr[y]! : br[y - 32]!))
  }
  return rows
})()

const TUFT = ['H.H.H', 'HHHHH', '.HtH.', '..t..']

export const GRASS_TUFT: Matrix = stamp([...GRASS_A], TUFT, 5, 7)

/** Кустиик с белым цветком — второй вариант травинки для разнообразия. */
export const GRASS_TUFT_FLOWER: Matrix = stamp([...GRASS_C], ['h.h', 'hhh', '.t.'], 6, 8)

export const WILD: Matrix = (() => {
  const m = blank(16, 16).map(() => 'w'.repeat(16)) as Matrix
  speckle(m, 3, ['W', 'W', 'x'])
  return m
})()

/** Изгородь: период 4 (бугры 2×2) — бесшовна при тайлировании. */
export const HEDGE: Matrix = (() => {
  const m = blank(16, 16).map(() => 'e'.repeat(16)) as Matrix
  for (let x = 0; x < 16; x++) {
    if (x % 4 < 2) {
      put(m, x, 0, 'E')
      put(m, x, 1, 'E')
    }
    put(m, x, 15, 'F')
  }
  // лиственная фактура: светлые пятна (детерминированные)
  for (let i = 0; i < 6; i++) {
    const x = (i * 5 + 2) % 16
    const y = 2 + ((i * 7) % 12)
    put(m, x, y, y < 4 ? 'E' : 'F')
  }
  return m
})()

// --- Домик (56×46, изометрия 2:1, footprint ~3.5×2.5 клетки) ---
// Грани строятся из мировых координат: sx = wx - wy, sy = (wx + wy)/2 - wz.
// Видимые грани: нижне-левая (тёмная N) и нижне-правая (светлая n);
// скат над нижне-левой гранью — в тени (R), над верхне-правой — свет (r).

export const HOUSE: Matrix = (() => {
  const W = 62
  const H = 52
  const ox = 23
  const oy = 20
  // мировая точка → матрица
  const p = (wx: number, wy: number, wz: number): [number, number] => [
    Math.round(wx - wy + ox),
    Math.round((wx + wy) / 2 - wz + oy),
  ]
  const m = blank(W, H)

  // основание 28×20, стены высотой 12, конёк wz=20 (wy=10), свес крыши 2
  const B = p(28, 0, 0)!  // правый угол основания
  const C = p(28, 20, 0)! // передний
  const D = p(0, 20, 0)!  // левый
  const B2 = p(28, 0, 12)!
  const C2 = p(28, 20, 12)!
  const K1 = p(0, 10, 22)!  // конёк: левый конец
  const K2 = p(28, 10, 22)! // конёк: правый конец

  // стены: нижне-левая (тёмная) и нижне-правая (светлая)
  wall(m, D[0], D[1], C[0], C[1], 12, 'N')
  wall(m, C[0], C[1], B[0], B[1], 12, 'n')
  // цоколь — тёмная кромка по низу стен
  for (let i = 0; i <= 28; i++) {
    const q1 = p(i, 20, 0)
    const q2 = p(28, Math.round(20 - (i * 20) / 28), 0)
    if (q1) put(m, q1[0], q1[1], 'O')
    if (q2) put(m, q2[0], q2[1], 'O')
  }

  // скат над верхне-правой гранью AB (виден сверху, освещён)
  const M1 = p(0, -2, 12)!
  const M2 = p(28, -2, 12)!
  fillPoly(m, [K1, K2, M2, M1], 'r')
  // скат над нижне-левой гранью DC (фронтальный, в тени)
  const P1 = p(0, 22, 12)!
  const P2 = p(28, 22, 12)!
  fillPoly(m, [K1, K2, P2, P1], 'R')
  // кромки карнизов
  for (const [a, b] of [
    [M1, M2],
    [P1, P2],
  ] as const)
    for (let t = 0; t <= 1; t += 1 / 40)
      put(m, Math.round(a[0] + (b[0] - a[0]) * t), Math.round(a[1] + (b[1] - a[1]) * t), 'O')
  // конёк
  for (let t = 0; t <= 1; t += 1 / 48)
    put(m, Math.round(K1[0] + (K2[0] - K1[0]) * t), Math.round(K1[1] + (K2[1] - K1[1]) * t), 'r')

  // фронтон над правой гранью CB (щипец)
  fillPoly(m, [C2, B2, K2], 'n')

  // труба на дальнем скате (низ «утоплен» в скат)
  fillPoly(
    m,
    [
      [34, 2],
      [39, 2],
      [39, 17],
      [34, 14],
    ],
    'n',
  )
  hline(m, 34, 39, 1, 'o')
  hline(m, 34, 39, 2, 'O')

  // дверь на светлой (правой) грани: wy 7..13, высота 10
  const dr1 = p(28, 13, 0)!
  const dr2 = p(28, 7, 0)!
  fillPoly(m, [dr1, dr2, p(28, 7, 10)!, p(28, 13, 10)!], 'd')
  // окно на тёмной (левой) грани: wx 16..22, wz 5..11
  const wn1 = p(16, 20, 5)!
  const wn2 = p(22, 20, 5)!
  fillPoly(m, [wn1, wn2, p(22, 20, 11)!, p(16, 20, 11)!], 'c')
  // отблеск на окне и двери
  put(m, wn1[0] + 2, wn1[1] - 3, 'C')
  put(m, dr2[0] + 2, dr2[1] - 4, 'C')
  return m
})()

// --- Растения (6 видов × 3 стадии) ---
// Вид по item_key с бэка; стадии 0/1/2. anchor спрайта (0.5, 0.95).
// Пропорции: деревья заметно выше кустов и цветов (крона над садом).

/** Стебель с листиками (общая база цветочных видов). */
function flowerBase(w: number, h: number, topY: number): Matrix {
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  vline(m, cx, topY, h - 2, 't')
  // листья: пара эллипсов по бокам
  ellipse(m, cx - 3, h - 5, 3, 1.6, 'l')
  ellipse(m, cx + 3, h - 8, 3, 1.6, 'l')
  put(m, cx - 4, h - 6, 'k')
  put(m, cx + 4, h - 8, 'k')
  return m
}

/** Головка цветка: лепестки + сердцевина. */
function bloom(m: Matrix, cx: number, cy: number, rx: number, petal: string, core: string): void {
  ellipse(m, cx, cy, rx, Math.max(2, rx - 1), petal)
  ellipse(m, cx, cy, Math.max(1, rx - 2), Math.max(1, rx - 3), core)
  // лепестковые «выступы»
  put(m, cx, cy - Math.max(2, rx - 1) - 1, petal)
  put(m, cx, cy + Math.max(2, rx - 1), petal)
  put(m, cx - rx - 1, cy, petal)
  put(m, cx + rx + 1, cy, petal)
}

export function plantFlower(stage: number): Matrix {
  if (stage === 0) {
    const m = flowerBase(12, 12, 5)
    bloom(m, 6, 3, 2, 'p', 'y')
    return m
  }
  if (stage === 1) {
    const m = flowerBase(16, 20, 7)
    bloom(m, 8, 4, 3, 'p', 'y')
    return m
  }
  const m = flowerBase(24, 28, 10)
  bloom(m, 12, 5, 5, 'p', 'y')
  // второй бутон сбоку
  vline(m, 17, 12, 18, 't')
  bloom(m, 17, 10, 2, 'p', 'y')
  return m
}

function plantLeaf(stage: number): Matrix {
  const sizes: [number, number][] = [
    [12, 12],
    [16, 20],
    [24, 28],
  ]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  // пучок листьев: перекрывающиеся эллипсы от земли
  const ry = Math.floor(h * 0.45)
  ellipse(m, cx, h - ry - 2, Math.floor(w * 0.32), ry, 'l')
  ellipse(m, cx - Math.floor(w * 0.22), h - ry, Math.floor(w * 0.22), ry * 0.7, 'k')
  ellipse(m, cx + Math.floor(w * 0.22), h - ry, Math.floor(w * 0.22), ry * 0.7, 'k')
  ellipse(m, cx, h - ry - Math.floor(ry * 0.5), Math.floor(w * 0.18), ry * 0.55, 'L')
  // травинки сверху
  for (let i = 0; i <= stage * 2; i++)
    vline(m, cx - 2 + i * 2, h - ry * 2 + (i % 2), h - ry * 2 + 2 + (i % 3), 'H')
  return m
}

function plantEvergreen(stage: number): Matrix {
  const sizes: [number, number][] = [
    [14, 14],
    [20, 26],
    [28, 38],
  ]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  // ярусы кроны: треугольники сверху вниз
  const crownH = h - 3
  for (let tier = 0; tier <= stage; tier++) {
    const top = Math.floor((crownH / (stage + 1)) * tier)
    const bottom = Math.floor((crownH / (stage + 1)) * (tier + 1))
    const halfMax = Math.floor((w / 2 - 1) * ((tier + 1) / (stage + 1)))
    for (let y = top; y <= bottom; y++) {
      const half = Math.max(1, Math.round((halfMax * (y - top)) / (bottom - top)))
      for (let x = cx - half; x <= cx + half; x++) put(m, x, y, y === bottom ? 'k' : tier === stage ? 'l' : 'k')
      put(m, cx, y, tier === stage ? 'L' : 'l')
    }
  }
  vline(m, cx, crownH, h - 2, 'O') // ствол
  return m
}

function plantLotus(stage: number): Matrix {
  const sizes: [number, number][] = [
    [12, 12],
    [16, 20],
    [24, 28],
  ]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  // широкие листья-лежанки у земли
  ellipse(m, cx - 4, h - 3, 4, 1.6, 'l')
  ellipse(m, cx + 4, h - 3, 4, 1.6, 'l')
  ellipse(m, cx, h - 4, 3, 1.4, 'k')
  // лотос: заострённые лепестки веером
  const cy = h - 6 - stage * 3
  const size = 2 + stage * 2
  for (let i = -size; i <= size; i++) {
    const ph = Math.max(2, size - Math.abs(i)) // высота лепестка
    for (let dy = 0; dy < ph; dy++) put(m, cx + i, cy + dy, dy === 0 ? 'h' : dy < 2 ? 'h' : 'p')
  }
  ellipse(m, cx, cy + 1, Math.max(1, size - 2), 1, 'y')
  return m
}

function plantCactus(stage: number): Matrix {
  const sizes: [number, number][] = [
    [12, 12],
    [16, 22],
    [22, 30],
  ]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  const bodyH = Math.floor(h * (0.5 + stage * 0.15))
  // ствол
  ellipse(m, cx, h - bodyH / 2 - 1, Math.floor(w * 0.2), bodyH / 2, 'l')
  vline(m, cx - Math.floor(w * 0.2), h - bodyH + 2, h - 2, 'l')
  vline(m, cx + Math.floor(w * 0.2), h - bodyH + 2, h - 2, 'l')
  vline(m, cx, h - bodyH, h - 2, 'L')
  // руки
  if (stage >= 1) {
    ellipse(m, cx - Math.floor(w * 0.32), h - bodyH * 0.6, 2, 1.4, 'l')
    vline(m, cx - Math.floor(w * 0.32) + 1, h - Math.floor(bodyH * 0.75), h - Math.floor(bodyH * 0.45), 'l')
  }
  if (stage >= 2) {
    ellipse(m, cx + Math.floor(w * 0.32), h - bodyH * 0.8, 2, 1.4, 'l')
    vline(m, cx + Math.floor(w * 0.32), h - Math.floor(bodyH * 0.95), h - Math.floor(bodyH * 0.6), 'l')
  }
  // колючки
  for (let y = h - bodyH + 3; y < h - 3; y += 3) {
    put(m, cx - Math.floor(w * 0.2) - 1, y, 'H')
    put(m, cx + Math.floor(w * 0.2) + 1, y + 1, 'H')
  }
  // цветок на верхушке
  const top = h - bodyH - 2
  put(m, cx, top, 'q')
  put(m, cx - 1, top + 1, 'q')
  put(m, cx + 1, top + 1, 'q')
  if (stage >= 2) {
    put(m, cx, top - 1, 'q')
    put(m, cx, top + 1, 'y')
  }
  return m
}

function plantOak(stage: number): Matrix {
  const sizes: [number, number][] = [
    [16, 16],
    [22, 28],
    [32, 44],
  ]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  const trunkH = Math.floor(h * (0.25 + stage * 0.08))
  const crownR = Math.floor(w * (0.28 + stage * 0.08))
  // крона: три перекрывающихся эллипса
  ellipse(m, cx, h - trunkH - crownR * 0.6, crownR, crownR * 0.75, 'l')
  ellipse(m, cx - crownR * 0.5, h - trunkH - crownR * 0.2, crownR * 0.6, crownR * 0.5, 'k')
  ellipse(m, cx + crownR * 0.5, h - trunkH - crownR * 0.25, crownR * 0.6, crownR * 0.5, 'k')
  ellipse(m, cx - 1, h - trunkH - crownR * 0.8, crownR * 0.4, crownR * 0.4, 'L')
  put(m, cx + 2, h - trunkH - crownR, 'L')
  put(m, cx - 3, h - trunkH - crownR * 0.5, 'L')
  // ствол с корнями
  for (let y = h - trunkH; y < h; y++) {
    put(m, cx, y, 'o')
    put(m, cx + 1, y, 'O')
  }
  hline(m, cx - 2, cx + 3, h - 1, 'O')
  return m
}

/** Роса: тонкие стебли с голубыми цветками-каплями (ур. 7). */
function plantDew(stage: number): Matrix {
  const sizes: [number, number][] = [[12, 12], [16, 20], [24, 28]]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  // 2-4 изогнутых стебля веером
  const stems = 1 + stage * 2
  for (let i = 0; i < stems; i++) {
    const dx = (i - (stems - 1) / 2) * Math.max(2, Math.floor(w / (stems + 2)))
    const top = Math.floor(h * 0.25)
    for (let y = h - 2; y >= top + Math.abs(dx); y--) put(m, cx + dx + ((h - y) % 2), y, 't')
    // капля-цветок на конце стебля
    const fx = cx + dx + Math.max(1, Math.floor(Math.abs(dx) / 2))
    const fy = top + Math.abs(dx) - 1
    put(m, fx, fy - 1, 'B')
    put(m, fx - 1, fy, 'B')
    put(m, fx, fy, 'B')
    put(m, fx + 1, fy, 'B')
    put(m, fx, fy + 1, 'h')
  }
  ellipse(m, cx, h - 3, Math.floor(w * 0.25), 1.6, 'l')
  return m
}

/** Подсолнух: толстый стебель, крупное жёлтое соцветие (ур. 8). */
function plantSunflower(stage: number): Matrix {
  const sizes: [number, number][] = [[12, 14], [16, 22], [24, 32]]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  const headR = 2 + stage * 2
  const headY = Math.floor(h * 0.3)
  vline(m, cx, headY, h - 2, 't') // стебель
  vline(m, cx + 1, headY + 2, h - 2, 'k')
  // листья-лопасти по стеблю
  for (let i = 0; i <= stage; i++) {
    const ly = headY + Math.floor((h - headY) * (0.3 + i * 0.25))
    hline(m, cx - 3 - (i % 2), cx - 1, ly, 'l')
    hline(m, cx + 2, cx + 3 + (i % 2), ly + 2, 'l')
  }
  // соцветие: жёлтые лепестки вокруг тёмной сердцевины
  ellipse(m, cx, headY, headR + 1, headR, 'j')
  ellipse(m, cx, headY, headR, headR - 1, 'J')
  ellipse(m, cx, headY, Math.max(1, headR - 2), Math.max(1, headR - 2), 's')
  put(m, cx - headR - 1, headY, 'j')
  put(m, cx + headR + 1, headY, 'j')
  return m
}

/** Бонсай: изогнутый ствол, плоские ярусы кроны (ур. 9). */
function plantBonsai(stage: number): Matrix {
  const sizes: [number, number][] = [[14, 14], [20, 26], [28, 38]]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  const trunkH = Math.floor(h * (0.3 + stage * 0.06))
  // S-изгиб ствола
  for (let y = h - trunkH; y < h; y++) {
    const t = (y - (h - trunkH)) / trunkH
    const sway = Math.round(Math.sin(t * Math.PI) * (stage + 1) * 0.6)
    put(m, cx + sway, y, 'o')
    put(m, cx + sway + 1, y, 'O')
  }
  // крона: 2-3 плоских «подушки»
  const pads = 1 + stage
  const crownW = Math.floor(w * (0.28 + stage * 0.09))
  for (let pad = 0; pad < pads; pad++) {
    const py = h - trunkH - Math.floor((h - trunkH - 2) * (pad / pads)) - 1
    const pw = Math.floor(crownW * (1 - pad * 0.25))
    ellipse(m, cx, py, pw, 1.8, 'l')
    ellipse(m, cx - 1, py - 1, Math.max(1, pw - 2), 1.2, 'L')
    put(m, cx + pw - 1, py + 1, 'k')
  }
  return m
}

/** Бабочий куст: пучок стеблей с сиреневыми метёлками (ур. 10). */
function plantButterflyBush(stage: number): Matrix {
  const sizes: [number, number][] = [[12, 12], [16, 20], [24, 28]]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  const spikes = 3 + stage * 2
  for (let i = 0; i < spikes; i++) {
    const sx = cx + Math.round((i - (spikes - 1) / 2) * (w / (spikes + 1)))
    const top = Math.floor(h * 0.2) + (i % 2) * 2
    vline(m, sx, top + 3, h - 2, 'k')
    const spikeH = Math.max(2, h - top - 5)
    for (let dy = 0; dy < spikeH; dy++) {
      const half = Math.max(0, Math.round((1 - dy / spikeH) * 2))
      for (let dx = -half; dx <= half; dx++) put(m, sx + dx, top + dy, dy % 2 ? 'v' : 'V')
    }
    put(m, sx, top - 1, 'q')
  }
  ellipse(m, cx, h - 3, Math.floor(w * 0.3), 1.8, 'l')
  return m
}
/** Рябина: дерево с огненно-оранжевой листвой и ягодами (ур. 11, premium). */
function plantRowan(stage: number): Matrix {
  const sizes: [number, number][] = [[16, 16], [22, 28], [32, 44]]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  const trunkH = Math.floor(h * (0.25 + stage * 0.08))
  const crownR = Math.floor(w * (0.28 + stage * 0.08))
  // крона в осенних тонах
  ellipse(m, cx, h - trunkH - crownR * 0.6, crownR, crownR * 0.75, 'f')
  ellipse(m, cx - crownR * 0.5, h - trunkH - crownR * 0.2, crownR * 0.6, crownR * 0.5, 'Z')
  ellipse(m, cx + crownR * 0.5, h - trunkH - crownR * 0.25, crownR * 0.6, crownR * 0.5, 'f')
  ellipse(m, cx - 1, h - trunkH - crownR * 0.8, crownR * 0.4, crownR * 0.4, 'Z')
  // гроздья ягод
  const clusters = 2 + stage
  for (let i = 0; i < clusters; i++) {
    const bx = cx - crownR + Math.floor((i * 2 * crownR) / (clusters - 1 || 1))
    const by = h - trunkH - crownR * 0.4 + (i % 2) * 2
    put(m, bx, by, 'A')
    put(m, bx + 1, by + 1, 'A')
  }
  for (let y = h - trunkH; y < h; y++) {
    put(m, cx, y, 'o')
    put(m, cx + 1, y, 'O')
  }
  hline(m, cx - 2, cx + 3, h - 1, 'O')
  return m
}

/** Звездоцвет: цветы-звёзды необычной сиреневой палитры (ур. 12, premium). */
function plantStarflower(stage: number): Matrix {
  const sizes: [number, number][] = [[12, 12], [16, 20], [24, 28]]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  const stems = 1 + stage
  for (let i = 0; i < stems; i++) {
    const dx = (i - (stems - 1) / 2) * Math.max(3, Math.floor(w / (stems + 2)))
    const top = Math.floor(h * 0.3) + (i % 2)
    vline(m, cx + dx, top, h - 2, 't')
    // звезда: лучи из центра
    const fy = top - 2
    put(m, cx + dx, fy, 'h')
    put(m, cx + dx, fy - 1, 'V')
    put(m, cx + dx - 1, fy, 'V')
    put(m, cx + dx + 1, fy, 'V')
    put(m, cx + dx - 1, fy - 1, 'v')
    put(m, cx + dx + 1, fy - 1, 'v')
    put(m, cx + dx, fy + 1, 'v')
    put(m, cx + dx, fy, 'q')
  }
  ellipse(m, cx, h - 3, Math.floor(w * 0.22), 1.5, 'l')
  return m
}

/** Огнецвет: цветки-язычки пламени (ур. 13, premium). */
function plantFireflower(stage: number): Matrix {
  const sizes: [number, number][] = [[12, 12], [16, 20], [24, 28]]
  const [w, h] = sizes[stage]!
  const m = blank(w, h)
  const cx = Math.floor(w / 2)
  const stems = 1 + stage
  for (let i = 0; i < stems; i++) {
    const dx = (i - (stems - 1) / 2) * Math.max(3, Math.floor(w / (stems + 2)))
    const top = Math.floor(h * 0.25) + (i % 2) * 2
    vline(m, cx + dx, top, h - 2, 'k')
    // пламя: внешние языки f, внутренние Z, ядро y
    const fy = top - 2
    put(m, cx + dx - 1, fy + 1, 'f')
    put(m, cx + dx + 1, fy + 1, 'f')
    put(m, cx + dx, fy, 'f')
    put(m, cx + dx, fy - 1, 'Z')
    put(m, cx + dx - 1, fy, 'Z')
    put(m, cx + dx, fy + 2, 'y')
    if (stage === 2) put(m, cx + dx, fy - 2, 'y')
  }
  ellipse(m, cx, h - 3, Math.floor(w * 0.24), 1.6, 'l')
  return m
}

export const PLANT_BUILDERS: Record<string, (stage: number) => Matrix> = {
  'ph-flower': plantFlower,
  'ph-leaf': plantLeaf,
  'ph-tree-evergreen': plantEvergreen,
  'ph-flower-lotus': plantLotus,
  'ph-cactus': plantCactus,
  'ph-tree': plantOak,
  'ph-drop': plantDew,
  'ph-sun': plantSunflower,
  'ph-tree-structure': plantBonsai,
  'ph-butterfly': plantButterflyBush,
  'ph-campfire': plantRowan,
  'ph-sparkle': plantStarflower,
  'ph-fire': plantFireflower,
}

// --- Декорации (item_key → матрица; используются и для чипов инвентаря) ---

/** Секция забора по маске соединений: N=1, E=2, S=4, W=8.
 *  Столб по центру; прожилины тянутся к соседним секциям (вглубь — продолжение столба). */
export function fenceMatrix(mask: number): Matrix {
  const m = blank(16, 16)
  // столб: x 7-8, от верха до земли
  vline(m, 7, 2, 14, 'o')
  vline(m, 8, 2, 14, 'O')
  put(m, 7, 2, 'O')
  put(m, 8, 1, 'o')
  // горизонтальные прожилины (двойные: свет + тень)
  for (const ry of [4, 8]) {
    if (mask & 8) {
      hline(m, 0, 6, ry, 'o')
      hline(m, 0, 6, ry + 1, 'O')
    }
    if (mask & 2) {
      hline(m, 9, 15, ry, 'o')
      hline(m, 9, 15, ry + 1, 'O')
    }
  }
  // вертикальные направления: тонкая прожилина (вид с торца) между столбами
  if (mask & 1) {
    put(m, 7, 0, 'o')
    put(m, 7, 1, 'o')
  }
  if (mask & 4) put(m, 7, 15, 'o')
  return m
}

/** Обычный зелёный куст (декорация): плотная листва в два тона, без цветов. */
const BUSH: Matrix = (() => {
  const m = blank(20, 16)
  // тень-основание и крона в три слоя
  ellipse(m, 10, 12, 9, 3.2, 'k')
  ellipse(m, 10, 9, 8.5, 4.6, 'l')
  ellipse(m, 8, 7.5, 5.5, 3.2, 'L')
  // блики-листья
  for (const [x, y] of [[5, 7], [7, 5], [11, 6], [13, 9], [9, 10]] as [number, number][])
    put(m, x, y, 'L')
  return m
})()

export const DECOR: Record<string, Matrix> = {
  fence: fenceMatrix(2 | 8),
  bush: BUSH,
  flowerbed: (() => {
    const m = blank(20, 10)
    ellipse(m, 10, 7, 9, 2.5, 'b')
    ellipse(m, 10, 7, 7, 1.6, 's')
    // три цветка с лепестками и стеблями
    const flower = (cx: number, cy: number, petal: string): void => {
      vline(m, cx, cy + 2, cy + 3, 't')
      put(m, cx - 1, cy + 3, 'l')
      put(m, cx + 1, cy + 3, 'l')
      ellipse(m, cx, cy, 1.6, 1.6, petal)
      put(m, cx, cy, 'y')
    }
    flower(5, 3, 'p')
    flower(10, 2, 'P')
    flower(15, 3, 'p')
    return m
  })(),
  lantern: (() => {
    const m = blank(10, 18)
    vline(m, 4, 6, 17, 'o')
    vline(m, 5, 6, 17, 'O')
    hline(m, 2, 7, 17, 'O')
    hline(m, 2, 7, 16, 'o')
    // корпус фонаря
    for (let y = 0; y < 6; y++)
      for (let x = 2; x < 8; x++) {
        const edge = y === 0 || y === 5 || x === 2 || x === 7
        put(m, x, y, edge ? 'O' : 'C')
      }
    put(m, 4, 2, 'q')
    put(m, 5, 3, 'q')
    return m
  })(),
  bench: (() => {
    const m = blank(22, 10)
    hline(m, 0, 21, 0, 'o') // спинка
    hline(m, 0, 21, 4, 'o') // сиденье
    hline(m, 0, 21, 5, 'O')
    vline(m, 3, 6, 9, 'O')
    vline(m, 18, 6, 9, 'O')
    vline(m, 8, 1, 3, 'o')
    vline(m, 13, 1, 3, 'o')
    return m
  })(),
  birdbath: (() => {
    const m = blank(16, 12)
    ellipse(m, 8, 3, 6, 2, 'x')
    ellipse(m, 8, 3, 4, 1.2, 'u')
    put(m, 7, 3, 'U')
    vline(m, 7, 4, 9, 'x')
    vline(m, 8, 4, 9, 'x')
    ellipse(m, 8, 10, 4, 1.4, 'x')
    return m
  })(),
  flag: (() => {
    // флюгер: столб с поворотной стрелкой и вымпелом
    const m = blank(14, 20)
    vline(m, 6, 2, 19, 'o')
    vline(m, 7, 2, 19, 'O')
    ellipse(m, 7, 19, 3, 1.4, 'x')
    // вымпел вправо
    for (let dy = 0; dy < 5; dy++)
      hline(m, 8, 12 - Math.abs(dy - 2), 3 + dy, dy < 2 ? 'r' : 'R')
    // стрелка-указатель
    hline(m, 2, 11, 2, 'o')
    put(m, 2, 1, 'o')
    put(m, 1, 2, 'O')
    return m
  })(),
  birdhouse: (() => {
    // скворечник на столбике: домик с круглым летком
    const m = blank(14, 16)
    vline(m, 6, 8, 15, 'o')
    vline(m, 7, 8, 15, 'O')
    // корпус
    for (let y = 4; y < 11; y++)
      for (let x = 4; x < 10; x++) put(m, x, y, x === 4 || x === 9 ? 'O' : 'n')
    // двускатная крыша
    hline(m, 3, 10, 3, 'r')
    hline(m, 4, 9, 2, 'r')
    put(m, 6, 1, 'r')
    put(m, 7, 1, 'R')
    // леток + жёрдочка
    put(m, 6, 6, 'O')
    put(m, 7, 6, 'O')
    hline(m, 5, 8, 11, 'o')
    return m
  })(),
  gem: (() => {
    // садовый самоцвет: кристалл на каменном постаменте
    const m = blank(12, 16)
    ellipse(m, 6, 13, 5, 2, 'x')
    ellipse(m, 6, 12, 4, 1.6, 'x')
    vline(m, 6, 13, 14, 'x')
    // кристалл: гранёный ромб с бликом
    fillPoly(m, [[6, 0], [9, 5], [6, 10], [3, 5]], 'd')
    fillPoly(m, [[6, 0], [9, 5], [6, 10]], 'c')
    put(m, 5, 3, 'h')
    put(m, 4, 4, 'h')
    put(m, 6, 8, 'U')
    return m
  })(),
  pond: (() => {
    // большое озеро с переходами: камни → песок → отмель → глубина,
    // границы колец растушёваны шахматным краем (без резких кромок)
    const W = 72
    const H = 36
    const cx = 36
    const cy = 18
    const m = blank(W, H)
    ellipse(m, cx, cy, 34, 16, 'x') // каменистый берег
    ellipse(m, cx, cy, 30, 13.6, 'S') // песок
    ellipse(m, cx, cy, 26.5, 11.8, 'U') // отмель
    ellipse(m, cx, cy, 22, 9.4, 'u') // глубина
    feather(m, cx, cy, 34, 16, 0.1, ' ') // камни → трава
    feather(m, cx, cy, 30, 13.6, 0.1, 'x') // песок → камни
    feather(m, cx, cy, 26.5, 11.8, 0.1, 'S') // отмель → песок
    feather(m, cx, cy, 22, 9.4, 0.12, 'U') // глубина → отмель
    ellipse(m, 30, 15, 9, 3.5, 'U') // блик
    put(m, 44, 22, 'U')
    put(m, 26, 24, 'U')
    // кувшинки
    for (const [lx, ly] of [
      [18, 17],
      [30, 21],
      [42, 15],
      [36, 12],
      [24, 13],
    ] as const) {
      put(m, lx, ly, 'l')
      put(m, lx + 1, ly, 'k')
      put(m, lx, ly - 1, 'L')
    }
    // камыши у берега
    for (const [rx, ry] of [
      [7, 11],
      [9, 9],
      [62, 22],
      [64, 20],
      [12, 26],
      [58, 13],
    ] as const) {
      vline(m, rx, ry, ry + 4, 't')
      put(m, rx, ry - 1, 'k')
    }
    return m
  })(),
  gazebo: (() => {
    // изометрия 2:1, как у домика: настил 16×16, столбы h=14, шатёр с apex z=30,
    // свес крыши 2; видимые грани — передне-левая (тень R) и передне-правая (свет r)
    const W = 44
    const H = 50
    const ox = 22
    const oy = 30
    const p = (wx: number, wy: number, wz: number): [number, number] => [
      Math.round(wx - wy + ox),
      Math.round((wx + wy) / 2 - wz + oy),
    ]
    const m = blank(W, H)

    // настил: ромб A(0,0) B(16,0) C(16,16) D(0,16)
    const FA = p(0, 0, 0)!
    const FB = p(16, 0, 0)!
    const FC = p(16, 16, 0)!
    const FD = p(0, 16, 0)!
    fillPoly(m, [FA, FB, FC, FD], 'o')
    // доски настила: линии вдоль wy
    for (let wy = 3; wy < 16; wy += 3) {
      const q1 = p(0, wy, 0)!
      const q2 = p(16, wy, 0)!
      for (let t = 0; t <= 1; t += 1 / 32)
        put(m, Math.round(q1[0] + (q2[0] - q1[0]) * t), Math.round(q1[1] + (q2[1] - q1[1]) * t), 'O')
    }

    // перила по видимым сторонам (z=8) + вертикальные стойки столбов
    const seg = (a: [number, number], b: [number, number], ch: string): void => {
      for (let t = 0; t <= 1; t += 1 / 48)
        put(m, Math.round(a[0] + (b[0] - a[0]) * t), Math.round(a[1] + (b[1] - a[1]) * t), ch)
    }
    const R_B = p(16, 0, 8)!
    const R_C = p(16, 16, 8)!
    const R_D = p(0, 16, 8)!
    seg(R_B, R_C, 'o')
    seg(R_C, R_D, 'O')
    // столбы: B (право, свет), C (перед), D (лево, тень)
    for (const [fx, fy, lit] of [
      [16, 0, true],
      [16, 16, true],
      [0, 16, false],
    ] as const) {
      const q = p(fx, fy, 0)!
      vline(m, q[0], q[1] - 14, q[1], lit ? 'o' : 'O')
      put(m, q[0] + (lit ? 1 : -1), q[1] - 10, lit ? 'O' : 'o')
    }

    // шатровая крыша: apex над центром, свес до -2..18
    const apex = p(8, 8, 30)!
    const EB = p(18, -2, 14)!
    const EC = p(18, 18, 14)!
    const ED = p(-2, 18, 14)!
    fillPoly(m, [apex, EC, ED], 'R') // передне-левый скат (тень)
    fillPoly(m, [apex, EB, EC], 'r') // передне-правый скат (свет)
    // задний скат чуть виден над apex? нет — но добавим кромку конька
    seg(ED, EC, 'O')
    seg(EC, EB, 'O')
    put(m, apex[0], apex[1], 'O')

    return m
  })(),
  greenhouse: (() => {
    // теплица в изометрии 2:1 (как домик): стеклянные стены с деревянным
    // каркасом, двускатная стеклянная крыша
    const W = 43
    const H = 45
    const ox = 18
    const oy = 22
    const p = (wx: number, wy: number, wz: number): [number, number] => [
      Math.round(wx - wy + ox),
      Math.round((wx + wy) / 2 - wz + oy),
    ]
    const m = blank(W, H)
    const seg = (a: [number, number], b: [number, number], ch: string): void => {
      for (let t = 0; t <= 1; t += 1 / 40)
        put(m, Math.round(a[0] + (b[0] - a[0]) * t), Math.round(a[1] + (b[1] - a[1]) * t), ch)
    }

    const WX = 22 // длина по wx
    const WY = 14 // глубина по wy
    const WH = 9 // высота стен
    const RZ = 17 // конёк

    const B = p(WX, 0, 0)!
    const C = p(WX, WY, 0)!
    const D = p(0, WY, 0)!
    const B2 = p(WX, 0, WH)!
    const C2 = p(WX, WY, WH)!
    const K1 = p(0, WY / 2, RZ)!
    const K2 = p(WX, WY / 2, RZ)!

    // стеклянные стены: передне-левая (тень) и передне-правая (свет)
    wall(m, D[0], D[1], C[0], C[1], WH, 'z')
    wall(m, C[0], C[1], B[0], B[1], WH, 'c')
    // каркас стен: стойки каждые 4 ед. + горизонтальный пояс
    for (let wx = 4; wx < WX; wx += 4) {
      const q = p(wx, WY, 0)!
      vline(m, q[0], q[1] - WH, q[1], 'O')
    }
    seg(p(0, WY, 4)!, p(WX, WY, 4)!, 'O')
    for (let wy = 4; wy < WY; wy += 4) {
      const q = p(WX, wy, 0)!
      vline(m, q[0], q[1] - WH, q[1], 'O')
    }
    seg(p(WX, 0, 4)!, p(WX, WY, 4)!, 'O')

    // двускатная крыша: задний скат (свет) и передний (тень), свес 2
    const M1 = p(0, -2, WH)!
    const M2 = p(WX, -2, WH)!
    const P1 = p(0, WY + 2, WH)!
    const P2 = p(WX, WY + 2, WH)!
    fillPoly(m, [K1, K2, M2, M1], 'c')
    fillPoly(m, [K1, K2, P2, P1], 'z')
    // каркас крыши: линии по скатам каждые 4 ед. + кромки и конёк
    for (let wx = 0; wx <= WX; wx += 4) {
      seg(p(wx, -2, WH)!, p(wx, WY / 2, RZ)!, 'O')
      seg(p(wx, WY / 2, RZ)!, p(wx, WY + 2, WH)!, 'O')
    }
    seg(M1, M2, 'O')
    seg(P1, P2, 'O')
    seg(K1, K2, 'O')
    // щипец над правой гранью (стекло)
    fillPoly(m, [C2, B2, K2], 'c')
    seg(B2, K2, 'O')
    seg(C2, K2, 'O')
    // блики на стекле
    put(m, 10, 16, 'h')
    put(m, 30, 22, 'h')
    put(m, 31, 17, 'h')

    // цоколь — тёмная кромка по низу стен
    for (let i = 0; i <= WX; i++) {
      const q1 = p(i, WY, 0)
      const q2 = p(WX, Math.round(WY - (i * WY) / WX), 0)
      if (q1) put(m, q1[0], q1[1], 'O')
      if (q2) put(m, q2[0], q2[1], 'O')
    }

    // дверь на правой грани: wy 8..12, высота 8
    fillPoly(m, [p(WX, 12, 0)!, p(WX, 8, 0)!, p(WX, 8, 8)!, p(WX, 12, 8)!], 'd')

    return m
  })(),
}

// --- Вспомогательные ---

/** Кольцо редкости под растением (эллипс-кайма). */
function ring(gold: string): Matrix {
  const m = blank(18, 6)
  ellipse(m, 9, 3, 8, 2.4, gold)
  ellipse(m, 9, 3, 6, 1.6, ' ')
  return m
}

export const RING_RARE = ring('y')
export const RING_EPIC = ring('q')

/** Мягкая овальная тень под объектом (рисуется с alpha ~0.45). */
export const SHADOW: Matrix = (() => {
  const m = blank(24, 10)
  ellipse(m, 12, 5, 11, 4, 'm')
  return m
})()

export const SPARKLE: Matrix = ['.q.', 'qqq', '.q.']

/** Рамка выделенной клетки (32×32, рисуется с alpha ~0.4). */
export const SELECT_OUTLINE: Matrix = (() => {
  const m = blank(32, 32)
  hline(m, 0, 31, 0, 'h')
  hline(m, 0, 31, 31, 'h')
  vline(m, 0, 0, 31, 'h')
  vline(m, 31, 0, 31, 'h')
  return m
})()

// --- Доступ по ключам: текстуры — в textures.ts (импортирует pixi и попадает
// в async-чанк рендерера); этот файл остаётся чистым (матрицы для DOM-превью).

export { flipX }