endfield_logo = {}

local W, H = 240, 135
local CTAB0 = 0x8000

local img, img_w, img_h

local flr = flr
local min = min
local max = max
local clamp = function(v, lo, hi) return min(max(v, lo), hi) end

-- simple deterministic RNG for shake/glitch
local _rng = 0x12345678
local function irnd(n)
  _rng = (_rng * 1103515245 + 12345) & 0x7fffffff
  return _rng % n
end

-- bloom config
local bloom_r = 11
local bloom_taps = 20
local bloom_shift_peak = 20
local BLOOM_CUT = 140
local bmask = nil

-- hardcoded palette luminance (precomputed from endfieldlogo.pod.pal)
local lum = { [0]=
  9,21,30,39,44,53,61,71,106,131,152,176,200,222,245,255,
  24,30,38,44,49,56,64,77,86,98,114,49,72,110,138,170,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
}

-- compact order/rank: only the 32 used colors (0-31) sorted by lum
local compact_order = {
  0,1,16,2,17,18,3,4,19,20,27,5,21,6,22,7,
  28,23,24,25,8,29,26,9,30,10,31,11,12,13,14,15
}
local compact_rank = { [0]=
  0,1,3,6,7,11,13,15,20,23,25,27,28,29,30,31,
  2,4,5,8,9,12,14,17,18,19,22,10,16,21,24,26
}

-- palette: copied from endfieldlogo.pod.pal
local endfield_palette = { [0]=
  0x000000, 0x15141d, 0x1f1e21, 0x2b262a, 0x2f2b30, 0x393337, 0x423b3f, 0x4e4446,
  0x73666d, 0x957e75, 0xa8948a, 0xbfac9f, 0xd4c5b9, 0xe8dcd0, 0xfbf5ec, 0xffffff,
  0x191819, 0x241c1b, 0x2d2420, 0x352926, 0x392e2c, 0x423530, 0x4c3d36, 0x5b4940,
  0x63524e, 0x735d4f, 0x876d5a, 0x40225c, 0x712f61, 0xb44c68, 0xe95897, 0xff7eb6,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000
}

-------------------------
-- bloom helpers (ctab additive)
-------------------------

local function ctab0_row63_shift(shift)
  local base = CTAB0 + 63 * 64
  for t = 0, 31 do
    local rr = compact_rank[t] + shift
    if rr > 31 then rr = 31 end
    poke(base + t, compact_order[rr + 1])
  end
  local blk = compact_order[min(32, shift + 1)]
  for t = 32, 63 do
    poke(base + t, blk)
  end
end

local function ctab0_row63_default()
  local base = CTAB0 + 63 * 64
  for t = 0, 63 do poke(base + t, 63) end
end

local function build_bloom_mask()
  if not img then return end
  bmask = userdata("u8", img_w, img_h)
  for y = 0, img_h - 1 do
    for x = 0, img_w - 1 do
      local c = img:get(x, y) & 0x3f
      bmask:set(x, y, (c != 0 and lum[c] >= BLOOM_CUT) and 63 or 0)
    end
  end
end

local function draw_bloom(dx, dy, dw, dh, bloom)
  if bloom < 0.02 then return end

  local shift = flr(bloom_shift_peak * bloom)
  if shift < 1 then shift = 1 end
  local r = bloom_r * bloom

  local ox = { 1,-1, 0, 0,  1, 1,-1,-1,  2,-2, 0, 0,  2, 2,-2,-2,  3,-3, 0, 0 }
  local oy = { 0, 0, 1,-1,  1,-1, 1,-1,  0, 0, 2,-2,  2,-2, 2,-2,  0, 0, 3,-3 }
  local taps = min(bloom_taps, #ox)

  palt(0, true)

  -- inner glow
  ctab0_row63_shift(shift)
  for i = 1, taps do
    sspr(bmask, 0, 0, img_w, img_h, dx + ox[i] * r, dy + oy[i] * r, dw, dh)
  end

  -- outer halo
  local shift2 = shift \ 2
  if shift2 < 1 then shift2 = 1 end
  ctab0_row63_shift(shift2)
  local rr = r * 1.8
  for i = 9, taps do
    sspr(bmask, 0, 0, img_w, img_h, dx + ox[i] * rr, dy + oy[i] * rr, dw, dh)
  end

  ctab0_row63_default()
  palt(0, false)
end

-------------------------
-- rocket params:
--   [2] = t            (effect time)
--   [3] = scroll_x     (horizontal scroll, default: centered)
--   [4] = zoom         (zoom factor, default: 1.0)
--   [5] = bloom        (bloom intensity 0-1, default: 0)
--   [6] = fade         (0=black, 1=normal, 2=white, default: 1)
--   [7] = shake        (screen shake amplitude in px, 0=off)
--   [8] = glitch       (glitch intensity 0..1, 0=off)
-------------------------

endfield_logo.load = function(this)
--  this.packed_img = fetch("endfieldlogo.pod")
  img = unpod( fetch("endfieldlogo.pod") )
  img_w = img:width()
  img_h = img:height()
  build_bloom_mask()
end

endfield_logo.init = function(this)
  vid(3)

  for i = 0, 63 do pal(i, endfield_palette[i], 2) end
--  pal(0, 0x000000, 2)
  for i = 0, 63 do palt(i, false) end
  ctab0_row63_default()
end

endfield_logo.setparams = function(this, params)
  this.t        = params[2] or 0
  this.scroll_x = params[3] or flr(-(img_w - W) / 2)
  this.zoom     = params[4] or 1.0
  if params[5] == nil then this.bloom = 0 else this.bloom = params[5] end
  this.fade     = params[6] or 1.0
  this.shake    = params[7] or 0
  this.glitch   = params[8] or 0
end

endfield_logo.update = function(this)
  local fade = this.fade
  local needs_transform = fade ~= 1.0

  if needs_transform then
    for i = 0, 63 do
      local c = endfield_palette[i]
      local r = (c >> 16) & 0xff
      local g = (c >> 8) & 0xff
      local b = c & 0xff

      if fade < 1.0 then
        r = r * fade; g = g * fade; b = b * fade
      elseif fade > 1.0 then
        local ft = fade - 1.0
        r = r + (255 - r) * ft; g = g + (255 - g) * ft; b = b + (255 - b) * ft
      end

      r = clamp(flr(r), 0, 255)
      g = clamp(flr(g), 0, 255)
      b = clamp(flr(b), 0, 255)
      pal(i, (r << 16) | (g << 8) | b, 2)
    end
    this._pal_dirty = true
  elseif this._pal_dirty then
    for i = 0, 63 do pal(i, endfield_palette[i], 2) end
--    pal(0, 0x000000, 2)
    this._pal_dirty = false
  end
end

endfield_logo.draw = function(this)
  -- reseed RNG per frame so shake/glitch don't interfere with each other
  _rng = (flr(this.t * 1000) * 1103515245 + 12345) & 0x7fffffff

  cls(0)

  if not img then return end

  local zoom = this.zoom
  local scroll_x = this.scroll_x
  local bloom = this.bloom

  -- shake: offset drawing origin
  local shake_ox, shake_oy = 0, 0
  if this.shake > 0 then
    local a = this.shake
    shake_ox = flr((irnd(2000) / 1000 - 1) * a)
    shake_oy = flr((irnd(2000) / 1000 - 1) * a)
    camera(-shake_ox, -shake_oy)
  end

  local dw = img_w * zoom
  local dh = img_h * zoom
  local dy = (H - dh) / 2

  sspr(img, 0, 0, img_w, img_h, scroll_x, dy, dw, dh)

  if bloom > 0 and bmask then
    draw_bloom(scroll_x, dy, dw, dh, bloom)
  end

  -- reset camera offset
  if shake_ox ~= 0 or shake_oy ~= 0 then
    camera(0, 0)
  end

  -- glitch post-processing: horizontal slice displacement
  if this.glitch > 0 then
    local disp = get_display()
    local amp = this.glitch * 18
    local slices = 24
    local iw, ih = W, H
    local snap = userdata("u8", iw, ih)
    snap:copy(disp, true, 0, 0, iw * ih, 0, 0, 1)
    for s = 0, slices - 1 do
      local sy0 = flr(s * ih / slices)
      local sy1 = flr((s + 1) * ih / slices)
      local sh = sy1 - sy0
      if sh > 0 then
        local off = flr((irnd(2000) / 1000 - 1) * amp)
        if irnd(9) == 0 then off += (irnd(2) * 2 - 1) * flr(amp * 0.9) end
        if off != 0 then
          for y = sy0, sy1 - 1 do
            local row_off = y * iw
            if off > 0 then
              disp:copy(snap, true, row_off, row_off + off, iw - off, 0, 0, 1)
              disp:copy(0, true, 0, row_off, off, 0, 0, 1)
            else
              local a = -off
              disp:copy(snap, true, row_off + a, row_off, iw - a, 0, 0, 1)
              disp:copy(0, true, 0, row_off + iw - a, a, 0, 0, 1)
            end
          end
        end
      end
    end
  end
end
