// image-surface.el - Native own-core raster PNG surface (the image efferent
// twin of audio). Renders a 64x64 RGB scene deterministically from a frame's
// meaning-geometry, then serialises a byte-valid PNG entirely own-core:
// 8-byte magic, IHDR, IDAT (zlib STORED/uncompressed DEFLATE + Adler32), IEND,
// with a per-chunk CRC32 computed via software xor32 (EL has no bitwise ops).
//
// The RGB palette basis is read from elp/faculty/sig/scene.basis (data, not
// literals) - the same read-from-learned discipline as the audio signatures.
// Integer-only throughout; pixels are composed functionally (painter's order)
// so no list mutation is needed.

// -- small int/parse helpers (self-contained) ----------------------------------

fn i_str_to_int(s: String) -> Int {
    let n: Int = str_len(s)
    let i: Int = 0
    let v: Int = 0
    while i < n {
        let c: Int = str_char_code(s, i)
        if c >= 48 {
            if c < 58 {
                let v: Int = v * 10 + (c - 48)
            }
        }
        let i: Int = i + 1
    }
    return v
}

fn basis_load(path: String) -> [String] {
    return str_split(fs_read(path), "\n")
}

fn basis_field(lines: [String], key: String) -> String {
    let pref: String = key + ": "
    let n: Int = native_list_len(lines)
    let plen: Int = str_len(pref)
    let i: Int = 0
    while i < n {
        let ln: String = native_list_get(lines, i)
        if str_starts_with(ln, pref) {
            return str_slice(ln, plen, str_len(ln))
        }
        let i: Int = i + 1
    }
    return ""
}

fn parse_rgb(csv: String) -> [Int] {
    let parts: [String] = str_split(csv, ",")
    let out: [Int] = native_list_empty()
    let n: Int = native_list_len(parts)
    let i: Int = 0
    while i < n {
        let v: Int = i_str_to_int(native_list_get(parts, i))
        let out: [Int] = native_list_append(out, v)
        let i: Int = i + 1
    }
    return out
}

// -- software 32-bit XOR (no bitwise ops in EL) --------------------------------

fn xor32(a: Int, b: Int) -> Int {
    let r: Int = 0
    let bit: Int = 1
    let i: Int = 0
    while i < 32 {
        let abit: Int = (a / bit) % 2
        let bbit: Int = (b / bit) % 2
        if abit != bbit {
            let add: Int = bit
            let r: Int = r + add
        }
        let bit: Int = bit * 2
        let i: Int = i + 1
    }
    return r
}

// -- CRC32 (table-driven, table built with xor32) ------------------------------

fn crc_table() -> [Int] {
    let t: [Int] = native_list_empty()
    let n: Int = 0
    while n < 256 {
        let c: Int = n
        let k: Int = 0
        while k < 8 {
            if c % 2 == 1 {
                let h: Int = c / 2
                let c: Int = xor32(h, 3988292384)
            } else {
                let c: Int = c / 2
            }
            let k: Int = k + 1
        }
        let t: [Int] = native_list_append(t, c)
        let n: Int = n + 1
    }
    return t
}

fn crc32_of(bytes: [Int], table: [Int]) -> Int {
    let crc: Int = 4294967295
    let n: Int = native_list_len(bytes)
    let i: Int = 0
    while i < n {
        let b: Int = native_list_get(bytes, i)
        let lo: Int = crc % 256
        let idx: Int = xor32(lo, b) % 256
        let tv: Int = native_list_get(table, idx)
        let hi: Int = crc / 256
        let crc: Int = xor32(hi, tv)
        let i: Int = i + 1
    }
    return xor32(crc, 4294967295)
}

// -- Adler32 (for the zlib trailer) --------------------------------------------

fn adler32_of(bytes: [Int]) -> Int {
    let a: Int = 1
    let b: Int = 0
    let n: Int = native_list_len(bytes)
    let i: Int = 0
    while i < n {
        let byte: Int = native_list_get(bytes, i)
        let a: Int = (a + byte) % 65521
        let b: Int = (b + a) % 65521
        let i: Int = i + 1
    }
    return b * 65536 + a
}

// -- byte-list append helpers --------------------------------------------------

fn app_u32be(dst: [Int], v: Int) -> [Int] {
    let dst: [Int] = native_list_append(dst, (v / 16777216) % 256)
    let dst: [Int] = native_list_append(dst, (v / 65536) % 256)
    let dst: [Int] = native_list_append(dst, (v / 256) % 256)
    let dst: [Int] = native_list_append(dst, v % 256)
    return dst
}

fn app_tag(dst: [Int], s: String) -> [Int] {
    let n: Int = str_len(s)
    let i: Int = 0
    while i < n {
        let dst: [Int] = native_list_append(dst, str_char_code(s, i))
        let i: Int = i + 1
    }
    return dst
}

fn app_all(dst: [Int], src: [Int]) -> [Int] {
    let n: Int = native_list_len(src)
    let i: Int = 0
    while i < n {
        let dst: [Int] = native_list_append(dst, native_list_get(src, i))
        let i: Int = i + 1
    }
    return dst
}

// -- plan: frame meaning-geometry -> shape atoms -------------------------------
// shape = [type, x, y, size, r, g, b]  (type 0=rect 1=disc 2=triangle)

fn charsum(s: String) -> Int {
    let n: Int = str_len(s)
    let i: Int = 0
    let acc: Int = 0
    while i < n {
        let c: Int = str_char_code(s, i)
        let acc: Int = acc + c
        let i: Int = i + 1
    }
    return acc
}

fn micro_of(s: String) -> Int {
    let dot: Int = str_index_of(s, ".")
    if dot < 0 { return i_str_to_int(s) * 1000000 }
    let n: Int = str_len(s)
    let fp: String = str_slice(s, dot + 1, n)
    let ip: String = str_slice(s, 0, dot)
    let iv: Int = i_str_to_int(ip)
    let fv: Int = 0
    let scale: Int = 100000
    let fl: Int = str_len(fp)
    let i: Int = 0
    while i < 6 {
        let d: Int = 0
        if i < fl { let d: Int = str_char_code(fp, i) - 48 }
        let fv: Int = fv + d * scale
        let scale: Int = scale / 10
        let i: Int = i + 1
    }
    return iv * 1000000 + fv
}

fn plan_scene(frames: [[String]], warm: [Int], cool: [Int]) -> [[Int]] {
    let shapes: [[Int]] = native_list_empty()
    let nf: Int = native_list_len(frames)
    let fi: Int = 0
    while fi < nf {
        let fr: [String] = native_list_get(frames, fi)
        let relation: String = surface_get(fr, "relation")
        let polarity: String = surface_get(fr, "polarity")
        let confidence: String = surface_get(fr, "confidence")
        let importance: String = surface_get(fr, "importance")
        let salience: String = surface_get(fr, "salience")
        // relation -> shape type
        let stype: Int = charsum(relation) % 3
        // confidence -> size (8..22)
        let cmi: Int = micro_of(confidence)
        let size: Int = 8 + cmi / 71428
        // salience -> y
        let sal: Int = i_str_to_int(salience)
        let y: Int = 6 + sal * 26
        // subj_id/index -> x
        let x: Int = 4 + (fi * 10) % 48
        // polarity -> warm/cool base color
        let br: Int = native_list_get(warm, 0)
        let bg2: Int = native_list_get(warm, 1)
        let bb: Int = native_list_get(warm, 2)
        if str_eq(polarity, "neg") {
            let br: Int = native_list_get(cool, 0)
            let bg2: Int = native_list_get(cool, 1)
            let bb: Int = native_list_get(cool, 2)
        }
        // importance -> brightness (500..1000 permille)
        let imi: Int = micro_of(importance)
        let bpm: Int = 500 + imi / 2000
        let r: Int = br * bpm / 1000
        let g: Int = bg2 * bpm / 1000
        let b: Int = bb * bpm / 1000
        let sh: [Int] = native_list_empty()
        let sh: [Int] = native_list_append(sh, stype)
        let sh: [Int] = native_list_append(sh, x)
        let sh: [Int] = native_list_append(sh, y)
        let sh: [Int] = native_list_append(sh, size)
        let sh: [Int] = native_list_append(sh, r)
        let sh: [Int] = native_list_append(sh, g)
        let sh: [Int] = native_list_append(sh, b)
        let shapes: [[Int]] = native_list_append(shapes, sh)
        let fi: Int = fi + 1
    }
    return shapes
}

// covers: is (px,py) inside this shape?
fn covers(sh: [Int], px: Int, py: Int) -> Bool {
    let stype: Int = native_list_get(sh, 0)
    let sx: Int = native_list_get(sh, 1)
    let sy: Int = native_list_get(sh, 2)
    let size: Int = native_list_get(sh, 3)
    let cx: Int = sx + size / 2
    if stype == 0 {
        if px >= sx {
            if px < sx + size {
                if py >= sy {
                    if py < sy + size {
                        return true
                    }
                }
            }
        }
        return false
    }
    if stype == 1 {
        let rad: Int = size / 2
        let dx: Int = px - cx
        let dy: Int = py - (sy + rad)
        if dx * dx + dy * dy <= rad * rad {
            return true
        }
        return false
    }
    // triangle: apex at top (sy), base at sy+size
    if py >= sy {
        if py < sy + size {
            let dyv: Int = py - sy
            let halfw: Int = dyv / 2
            let dxv: Int = px - cx
            let adx: Int = dxv
            if adx < 0 { let adx: Int = 0 - dxv }
            if adx <= halfw {
                return true
            }
        }
    }
    return false
}

// pixel_color: painter's algorithm - last covering shape wins. Returns [r,g,b].
fn pixel_color(px: Int, py: Int, shapes: [[Int]], bg: [Int]) -> [Int] {
    let r: Int = native_list_get(bg, 0)
    let g: Int = native_list_get(bg, 1)
    let b: Int = native_list_get(bg, 2)
    let n: Int = native_list_len(shapes)
    let i: Int = 0
    while i < n {
        let sh: [Int] = native_list_get(shapes, i)
        if covers(sh, px, py) {
            let r: Int = native_list_get(sh, 4)
            let g: Int = native_list_get(sh, 5)
            let b: Int = native_list_get(sh, 6)
        }
        let i: Int = i + 1
    }
    let out: [Int] = native_list_empty()
    let out: [Int] = native_list_append(out, r)
    let out: [Int] = native_list_append(out, g)
    let out: [Int] = native_list_append(out, b)
    return out
}

// rasterize: build the raw (filtered) scanline byte stream, filter byte 0 / row.
fn rasterize(w: Int, h: Int, shapes: [[Int]], bg: [Int]) -> [Int] {
    let raw: [Int] = native_list_empty()
    let y: Int = 0
    while y < h {
        let raw: [Int] = native_list_append(raw, 0)
        let x: Int = 0
        while x < w {
            let col: [Int] = pixel_color(x, y, shapes, bg)
            let raw: [Int] = native_list_append(raw, native_list_get(col, 0))
            let raw: [Int] = native_list_append(raw, native_list_get(col, 1))
            let raw: [Int] = native_list_append(raw, native_list_get(col, 2))
            let x: Int = x + 1
        }
        let y: Int = y + 1
    }
    return raw
}

// zlib stream with a single STORED (uncompressed) DEFLATE block + Adler32.
fn zlib_store(raw: [Int]) -> [Int] {
    let z: [Int] = native_list_empty()
    let z: [Int] = native_list_append(z, 120)
    let z: [Int] = native_list_append(z, 1)
    let z: [Int] = native_list_append(z, 1)
    let len: Int = native_list_len(raw)
    let nlen: Int = 65535 - len
    let z: [Int] = native_list_append(z, len % 256)
    let z: [Int] = native_list_append(z, (len / 256) % 256)
    let z: [Int] = native_list_append(z, nlen % 256)
    let z: [Int] = native_list_append(z, (nlen / 256) % 256)
    let z: [Int] = app_all(z, raw)
    let ad: Int = adler32_of(raw)
    let z: [Int] = app_u32be(z, ad)
    return z
}

// append a full PNG chunk: length + (type+data) + crc32(type+data).
fn app_chunk(png: [Int], type_and_data: [Int], table: [Int]) -> [Int] {
    let total: Int = native_list_len(type_and_data)
    let dlen: Int = total - 4
    let png: [Int] = app_u32be(png, dlen)
    let png: [Int] = app_all(png, type_and_data)
    let crc: Int = crc32_of(type_and_data, table)
    let png: [Int] = app_u32be(png, crc)
    return png
}

fn png_build(w: Int, h: Int, raw: [Int], table: [Int]) -> [Int] {
    let png: [Int] = native_list_empty()
    // 8-byte signature
    let png: [Int] = native_list_append(png, 137)
    let png: [Int] = native_list_append(png, 80)
    let png: [Int] = native_list_append(png, 78)
    let png: [Int] = native_list_append(png, 71)
    let png: [Int] = native_list_append(png, 13)
    let png: [Int] = native_list_append(png, 10)
    let png: [Int] = native_list_append(png, 26)
    let png: [Int] = native_list_append(png, 10)
    // IHDR
    let ihdr: [Int] = native_list_empty()
    let ihdr: [Int] = app_tag(ihdr, "IHDR")
    let ihdr: [Int] = app_u32be(ihdr, w)
    let ihdr: [Int] = app_u32be(ihdr, h)
    let ihdr: [Int] = native_list_append(ihdr, 8)
    let ihdr: [Int] = native_list_append(ihdr, 2)
    let ihdr: [Int] = native_list_append(ihdr, 0)
    let ihdr: [Int] = native_list_append(ihdr, 0)
    let ihdr: [Int] = native_list_append(ihdr, 0)
    let png: [Int] = app_chunk(png, ihdr, table)
    // IDAT
    let z: [Int] = zlib_store(raw)
    let idat: [Int] = native_list_empty()
    let idat: [Int] = app_tag(idat, "IDAT")
    let idat: [Int] = app_all(idat, z)
    let png: [Int] = app_chunk(png, idat, table)
    // IEND
    let iend: [Int] = native_list_empty()
    let iend: [Int] = app_tag(iend, "IEND")
    let png: [Int] = app_chunk(png, iend, table)
    return png
}

fn png_write(path: String, png: [Int]) -> Int {
    let n: Int = native_list_len(png)
    let buf: String = __str_alloc(n)
    let i: Int = 0
    while i < n {
        let buf: String = __str_set_char(buf, i, native_list_get(png, i))
        let i: Int = i + 1
    }
    let ok: Int = fs_write_bytes(path, buf, n)
    return ok
}
