-- helpers.lua — shared palette + pixel primitives for asset generation
-- Usage: local H = dofile(ROOT .. "tools/aseprite/helpers.lua")
-- All draw helpers operate on an Aseprite Image via a "pen" table bound to an origin.

local H = {}

H.ROOT = "/Users/leidianyayi/Documents/GitHub/ModelTest20260714/Kimi-K3/"

-- ----------------------------------------------------------------------------
-- Candy palette (CONTRACT §1) + white-layer grays + a few flat utility shades.
-- Stored as {r,g,b}. No gradients, no AA — every pixel is one of these.
-- ----------------------------------------------------------------------------
H.P = {
  -- grass greens
  grass    = {125, 201,  94}, -- #7DC95E
  grass_d  = { 95, 179,  74}, -- #5FB34A
  grass_dd = { 74, 148,  58},
  grass_l  = {158, 216, 128},
  -- cream
  cream    = {255, 243, 214}, -- #FFF3D6
  cream_d  = {238, 220, 178},
  cream_dd = {216, 196, 150},
  -- honey orange
  honey    = {245, 166,  35}, -- #F5A623
  honey_d  = {224, 143,  18},
  honey_dd = {196, 120,  10},
  honey_l  = {250, 193,  96},
  -- sky blue
  sky      = {143, 211, 255}, -- #8FD3FF
  sky_d    = {110, 186, 240},
  -- stone road
  stone    = {232, 220, 200}, -- #E8DCC8
  stone_d  = {206, 190, 162},
  stone_dd = {178, 160, 130},
  stone_l  = {245, 237, 224},
  -- lake water
  water    = {110, 193, 228}, -- #6EC1E4
  water_d  = { 79, 168, 216}, -- #4FA8D8
  water_dd = { 56, 140, 190},
  water_l  = {158, 216, 242},
  -- pink
  pink     = {247, 183, 203}, -- #F7B7CB
  pink_d   = {232, 150, 176},
  pink_dd  = {208, 120, 150},
  pink_l   = {252, 208, 222},
  -- brown (trunk / wood dark)
  brown    = {139,  90,  43}, -- #8B5A2B
  brown_d  = {108,  68,  30},
  brown_dd = { 84,  52,  22},
  brown_l  = {172, 122,  72},
  -- dirt road
  dirt     = {205, 172, 122},
  dirt_d   = {182, 148,  98},
  dirt_l   = {220, 190, 146},
  -- white-layer grays (CONTRACT §1)
  w_hi     = {245, 245, 245}, -- #F5F5F5
  w_mid    = {218, 218, 218}, -- #DADADA
  w_lo     = {189, 189, 189}, -- #BDBDBD
  w_dk     = {150, 150, 150},
  w_dd     = {110, 110, 110},
  -- reds
  red      = {226,  87,  77},
  red_d    = {198,  66,  56},
  red_l    = {240, 130, 120},
  -- blues
  blue     = { 91, 155, 213},
  blue_d   = { 62, 125, 190},
  blue_l   = {140, 190, 232},
  -- yellows
  yellow   = {247, 214,  92},
  yellow_d = {224, 186,  55},
  yellow_l = {252, 228, 140},
  -- foliage
  leaf     = { 86, 168,  78},
  leaf_d   = { 64, 138,  60},
  leaf_dd  = { 46, 108,  44},
  leaf_l   = {120, 192, 110},
  -- wood
  wood     = {201, 152,  96},
  wood_d   = {176, 126,  74},
  wood_dd  = {150, 104,  58},
  wood_l   = {220, 178, 124},
  -- stone wall (cool gray)
  swall    = {196, 190, 178},
  swall_d  = {164, 158, 146},
  swall_dd = {134, 128, 116},
  swall_l  = {214, 209, 200},
  -- berry
  berry    = {206,  62,  96},
  berry_d  = {172,  44,  76},
  -- misc
  skin     = {255, 224, 190},
  skin_d   = {238, 198, 156},
  hair     = {120,  84,  52},
  hair_d   = { 96,  66,  40},
  hair_l   = {152, 112,  74},
  black    = { 54,  44,  40},
  ink      = { 40,  34,  30},
  white    = {255, 255, 255},
  grey     = {150, 150, 150},
  grey_d   = {108, 108, 108},
  grey_l   = {196, 196, 196},
  screen   = { 44,  58,  54},
  screen_l = {120, 220, 160},
  metal    = {178, 184, 190},
  metal_d  = {140, 146, 152},
  flame    = {255, 214,  92},
  flame_d  = {245, 140,  40},
  leaf2    = { 60, 150,  70},
}

-- make an Aseprite color from a palette name (or {r,g,b} table) + optional alpha
function H.col(name, a)
  local t = name
  if type(name) == "string" then t = H.P[name] end
  return app.pixelColor.rgba(t[1], t[2], t[3], a or 255)
end

-- ----------------------------------------------------------------------------
-- Pen: a table of draw methods bound to an image + origin (ox, oy).
-- All coordinates passed to pen methods are LOCAL to the origin.
-- ----------------------------------------------------------------------------
function H.at(img, ox, oy)
  local t = { img = img, ox = ox, oy = oy }
  function t.p(x, y, c) img:drawPixel(ox + x, oy + y, c) end
  function t.pn(x, y, name, a) img:drawPixel(ox + x, oy + y, H.col(name, a)) end
  function t.rect(x, y, w, h, c)
    for j = 0, h - 1 do for i = 0, w - 1 do img:drawPixel(ox + x + i, oy + y + j, c) end end
  end
  function t.rectn(x, y, w, h, name, a)
    local c = H.col(name, a)
    for j = 0, h - 1 do for i = 0, w - 1 do img:drawPixel(ox + x + i, oy + y + j, c) end end
  end
  function t.hl(x, y, len, c) for i = 0, len - 1 do img:drawPixel(ox + x + i, oy + y, c) end end
  function t.hln(x, y, len, name, a)
    local c = H.col(name, a); for i = 0, len - 1 do img:drawPixel(ox + x + i, oy + y, c) end
  end
  function t.vl(x, y, len, c) for j = 0, len - 1 do img:drawPixel(ox + x, oy + y + j, c) end end
  function t.vln(x, y, len, name, a)
    local c = H.col(name, a); for j = 0, len - 1 do img:drawPixel(ox + x, oy + y + j, c) end
  end
  -- outline rectangle
  function t.frame(x, y, w, h, c)
    t.hl(x, y, w, c); t.hl(x, y + h - 1, w, c)
    t.vl(x, y, h, c); t.vl(x + w - 1, y, h, c)
  end
  function t.framen(x, y, w, h, name, a)
    t.frame(x, y, w, h, H.col(name, a))
  end
  -- filled disc (filled circle), integer radius
  function t.disc(cx, cy, r, c)
    for yy = -r, r do for xx = -r, r do
      if xx * xx + yy * yy <= r * r then t.p(cx + xx, cy + yy, c) end
    end end
  end
  function t.discn(cx, cy, r, name, a) t.disc(cx, cy, r, H.col(name, a)) end
  -- filled ellipse
  function t.ellipse(cx, cy, rx, ry, c)
    for yy = -ry, ry do for xx = -rx, rx do
      if (xx * xx) * (ry * ry) + (yy * yy) * (rx * rx) <= (rx * rx) * (ry * ry) then
        t.p(cx + xx, cy + yy, c)
      end
    end end
  end
  function t.ellipsen(cx, cy, rx, ry, name, a) t.ellipse(cx, cy, rx, ry, H.col(name, a)) end
  return t
end

-- pen for a 16x16 tile by id (tileset is 16 columns)
function H.tile(img, id)
  local ox = (id % 16) * 16
  local oy = math.floor(id / 16) * 16
  return H.at(img, ox, oy)
end

-- ----------------------------------------------------------------------------
-- Motif helpers (drawn through a pen `t`, local coords)
-- ----------------------------------------------------------------------------

-- a small 5-petal flower centered at (cx,cy)
function H.flower(t, cx, cy, petal, center)
  local pc = H.col(petal); local cc = H.col(center or "yellow")
  t.p(cx, cy - 1, pc); t.p(cx, cy + 1, pc)
  t.p(cx - 1, cy, pc); t.p(cx + 1, cy, pc)
  t.p(cx, cy, cc)
end

-- a window with brown frame + sky panes, top-left (x,y), size w x h
function H.window(t, x, y, w, h, glass)
  glass = glass or "sky"
  t.rectn(x, y, w, h, "brown_d")
  t.rectn(x + 1, y + 1, w - 2, h - 2, glass)
  -- mullions
  local mx = x + math.floor(w / 2)
  local my = y + math.floor(h / 2)
  t.vln(mx, y + 1, h - 2, "brown_d")
  t.hln(x + 1, my, w - 2, "brown_d")
  -- glass highlight
  t.pn(x + 2, y + 2, "sky_l" == glass and "white" or "water_l")
end

-- ----------------------------------------------------------------------------
-- Mirror pen: wraps a pen `t`, flipping x within a `w`-wide frame.
-- Lets one directional drawing serve both left and right facings.
-- ----------------------------------------------------------------------------
function H.mirrorX(t, w)
  local m = {}
  function m.p(x, y, c) t.p(w - 1 - x, y, c) end
  function m.pn(x, y, name, a) t.pn(w - 1 - x, y, name, a) end
  function m.rect(x, y, ww, h, c) t.rect(w - x - ww, y, ww, h, c) end
  function m.rectn(x, y, ww, h, name, a) t.rectn(w - x - ww, y, ww, h, name, a) end
  function m.hl(x, y, len, c) t.hl(w - x - len, y, len, c) end
  function m.hln(x, y, len, name, a) t.hln(w - x - len, y, len, name, a) end
  function m.vl(x, y, len, c) t.vl(w - 1 - x, y, len, c) end
  function m.vln(x, y, len, name, a) t.vln(w - 1 - x, y, len, name, a) end
  function m.framen(x, y, ww, h, name, a) t.framen(w - x - ww, y, ww, h, name, a) end
  function m.discn(cx, cy, r, name, a) t.discn(w - 1 - cx, cy, r, name, a) end
  function m.ellipsen(cx, cy, rx, ry, name, a) t.ellipsen(w - 1 - cx, cy, rx, ry, name, a) end
  return m
end

-- create a fresh RGB sprite + return (sprite, image)
function H.new(w, h)
  local spr = Sprite(w, h, ColorMode.RGB)
  local img = app.activeImage
  return spr, img
end

return H
