# SPDX-License-Identifier: LSL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus Sovereign Core.
# See legal/LICENSE_SOVEREIGN.md for license terms.

## Nexus Membrane: Userland Graphics Compositor

# libs/membrane/compositor.nim
# Phase 35b/d: The Sovereign Compositor + Input Router

import ../../core/ion

const SYS_TABLE_ADDR = 0x83000000'u64

const 
  GAP = 10                  # Pixels between windows
  FOCUS_COLOR = 0xFF00FFFF'u32  # Cyan border for focused window
  BG_COLOR = 0xFF101020'u32     # Dark Blue background

type
  Surface* = object
    id*: int32
    buffer*: ptr UncheckedArray[uint32]
    width*, height*: int
    x*: int                 # Logical X position on the infinite strip
    dirty*: bool
    focused*: bool

  Compositor* = object
    surfaces*: seq[Surface]
    view_x*: int            # Viewport scroll position
    focused_idx*: int       # index in seq[Surface]

var c*: Compositor

# HAL Imports
proc hal_surface_alloc*(w, h: uint32): int32 {.importc, cdecl.}
proc hal_surface_get_ptr*(id: int32): ptr UncheckedArray[uint32] {.importc, cdecl.}

proc get_sys_table(): ptr ion.SysTable =
  return cast[ptr ion.SysTable](SYS_TABLE_ADDR)

proc blit_surface(s: Surface, dest_x, dest_y: int) =
  let sys = get_sys_table()
  let fb = cast[ptr UncheckedArray[uint32]](sys.fb_addr)
  let fb_w = int(sys.fb_width)
  let fb_h = int(sys.fb_height)

  # Clipping
  let start_y = max(0, dest_y)
  let end_y = min(fb_h, dest_y + s.height)
  let start_x = max(0, dest_x)
  let end_x = min(fb_w, dest_x + s.width)

  if start_x >= end_x or start_y >= end_y: return

  for y in start_y ..< end_y:
    let src_y = y - dest_y
    let src_row = cast[pointer](addr s.buffer[src_y * s.width + (start_x - dest_x)])
    let dest_row = cast[pointer](addr fb[y * fb_w + start_x])
    let copy_len = (end_x - start_x) * 4
    copyMem(dest_row, src_row, copy_len)

proc draw_border(x, y, w, h: int, color: uint32) =
  let sys = get_sys_table()
  let fb = cast[ptr UncheckedArray[uint32]](sys.fb_addr)
  let fb_w = int(sys.fb_width)
  let fb_h = int(sys.fb_height)

  for ix in x ..< x + w:
    if ix >= 0 and ix < fb_w:
      if y >= 0 and y < fb_h: fb[y * fb_w + ix] = color
      if y + h - 1 >= 0 and y + h - 1 < fb_h: fb[(y + h - 1) * fb_w + ix] = color
  
  for iy in y ..< y + h:
    if iy >= 0 and iy < fb_h:
      if x >= 0 and x < fb_w: fb[iy * fb_w + x] = color
      if x + w - 1 >= 0 and x + w - 1 < fb_w: fb[iy * fb_w + (x + w - 1)] = color

proc layout*(c: var Compositor) =
  var current_x = 0
  for i, s in c.surfaces.mpairs:
    s.x = current_x
    s.focused = (i == c.focused_idx)
    current_x += s.width + GAP

proc process_input(c: var Compositor) =
  ## Intercept and route input (STUB - not currently used)
  discard
  # var pkt: IonPacket
  # if ion_user_input(addr pkt):
  #   ... input handling commented out until ion_user_* is replaced with kernel APIs

proc render_frame*(c: var Compositor) =
  let sys = get_sys_table()
  let fb = cast[ptr UncheckedArray[uint32]](sys.fb_addr)
  let fb_total = sys.fb_width * sys.fb_height

  for i in 0 ..< int(fb_total):
    fb[i] = BG_COLOR

  c.layout()

  for i, s in c.surfaces:
    let screen_x = s.x - c.view_x
    if screen_x + s.width < 0 or screen_x >= int(sys.fb_width):
      continue
    
    let screen_y = (int(sys.fb_height) - s.height) div 2
    blit_surface(s, screen_x, screen_y)
    
    if s.focused:
      draw_border(screen_x, screen_y, s.width, s.height, FOCUS_COLOR)

proc create_surface*(w, h: int): int32 =
  let id = hal_surface_alloc(uint32(w), uint32(h))
  if id < 0: return -1
  
  let p = hal_surface_get_ptr(id)
  if p == nil: return -1

  var s: Surface
  s.id = id
  s.buffer = p
  s.width = w
  s.height = h
  s.dirty = true
  
  c.surfaces.add(s)
  if c.surfaces.len == 1:
    c.focused_idx = 0
    
  return id

proc compositor_step*() =
  process_input(c)
  render_frame(c)
