///|
/// Graphics backend selection contracts.
///
/// Ebiten refs:
/// - internal/graphicsdriver/graphics.go
/// - internal/ui/ui_glfw.go (platform dependent surface binding)

///|
pub(all) enum GraphicsBackendKind {
  WgpuNative
  WebGpu
  WebGl2
  Null
} derive(Debug)

///|
pub impl Show for GraphicsBackendKind with fn output(self, logger) {
  logger.write_object(to_repr(self))
}

///|
pub(all) struct GraphicsBackendOptions {
  enable_validation : Bool
  prefer_low_power : Bool
  enable_vsync : Bool
} derive(Debug)

///|
pub impl Show for GraphicsBackendOptions with fn output(self, logger) {
  logger.write_object(to_repr(self))
}

///|
pub struct StubGraphicsDriver {
  backend : GraphicsBackendKind
  mut width : Int
  mut height : Int
  mut initialized : Bool
  mut native_active : Bool
  mut web_active : Bool
  mut next_id : Int
  mut begin_count : Int
  mut end_count : Int
  mut draw_count : Int
  mut resize_count : Int
  mut resize_suppressed_count : Int
  mut last_resize_duration_ms : Double
  mut total_resize_duration_ms : Double
} derive(Debug)

///|
pub struct GraphicsResizeStats {
  resize_count : Int
  suppressed_count : Int
  current_width : Int
  current_height : Int
  last_resize_duration_ms : Double
  total_resize_duration_ms : Double
} derive(Debug)

///|
pub impl Show for GraphicsResizeStats with fn output(self, logger) {
  logger.write_object(to_repr(self))
}

///|
pub type NativeGraphicsDriver = StubGraphicsDriver

///|
pub type WebGpuGraphicsDriver = StubGraphicsDriver

///|
pub type WebGlGraphicsDriver = StubGraphicsDriver

///|
pub type NullGraphicsDriver = StubGraphicsDriver

///|
/// A factory that constructs a backend driver for a given kind / surface
/// / options triple. Useful when a host wants to defer the choice of
/// (wgpu-native vs WebGPU vs WebGL2 vs Null) until runtime feature
/// detection.
pub(open) trait GraphicsBackendFactory {
  /// Build and install a driver for the requested `kind` against the
  /// surface. Implementations may raise if `kind` is unsupported or the
  /// surface is incompatible.
  fn create(
    Self,
    kind : GraphicsBackendKind,
    surface : SurfaceToken,
    options : GraphicsBackendOptions,
  ) -> Unit raise
}

///|
pub fn default_graphics_backend_options() -> GraphicsBackendOptions {
  { enable_validation: true, prefer_low_power: false, enable_vsync: true }
}

///|
fn default_clock_now_ms() -> Double {
  0.0
}

///|
let graphics_clock_provider : Ref[() -> Double] = Ref::new(default_clock_now_ms)

///|
pub fn set_graphics_clock_provider(clock : () -> Double) -> Unit {
  graphics_clock_provider.val = clock
}

///|
pub fn reset_graphics_clock_provider() -> Unit {
  graphics_clock_provider.val = default_clock_now_ms
}

///|
pub fn create_wgpu_native_graphics(
  surface : SurfaceToken,
  options : GraphicsBackendOptions,
) -> NativeGraphicsDriver {
  let _ = options
  {
    backend: GraphicsBackendKind::WgpuNative,
    width: surface.width,
    height: surface.height,
    initialized: false,
    native_active: false,
    web_active: false,
    next_id: 0,
    begin_count: 0,
    end_count: 0,
    draw_count: 0,
    resize_count: 0,
    resize_suppressed_count: 0,
    last_resize_duration_ms: 0.0,
    total_resize_duration_ms: 0.0,
  }
}

///|
pub fn create_webgpu_graphics(
  surface : SurfaceToken,
  options : GraphicsBackendOptions,
) -> WebGpuGraphicsDriver {
  let _ = options
  {
    backend: GraphicsBackendKind::WebGpu,
    width: surface.width,
    height: surface.height,
    initialized: false,
    native_active: false,
    web_active: false,
    next_id: 0,
    begin_count: 0,
    end_count: 0,
    draw_count: 0,
    resize_count: 0,
    resize_suppressed_count: 0,
    last_resize_duration_ms: 0.0,
    total_resize_duration_ms: 0.0,
  }
}

///|
pub fn create_webgl_graphics(
  surface : SurfaceToken,
  options : GraphicsBackendOptions,
) -> WebGlGraphicsDriver {
  let _ = options
  {
    backend: GraphicsBackendKind::WebGl2,
    width: surface.width,
    height: surface.height,
    initialized: false,
    native_active: false,
    web_active: false,
    next_id: 0,
    begin_count: 0,
    end_count: 0,
    draw_count: 0,
    resize_count: 0,
    resize_suppressed_count: 0,
    last_resize_duration_ms: 0.0,
    total_resize_duration_ms: 0.0,
  }
}

///|
pub fn create_null_graphics(width : Int, height : Int) -> NullGraphicsDriver {
  {
    backend: GraphicsBackendKind::Null,
    width,
    height,
    initialized: false,
    native_active: false,
    web_active: false,
    next_id: 0,
    begin_count: 0,
    end_count: 0,
    draw_count: 0,
    resize_count: 0,
    resize_suppressed_count: 0,
    last_resize_duration_ms: 0.0,
    total_resize_duration_ms: 0.0,
  }
}

///|
pub impl GraphicsDriver for StubGraphicsDriver with fn initialize(self) {
  if self.initialized {
    return
  }
  match self.backend {
    GraphicsBackendKind::WgpuNative =>
      self.native_active = native_try_initialize(self.width, self.height)
    GraphicsBackendKind::WebGpu | GraphicsBackendKind::WebGl2 =>
      self.web_active = web_graphics_try_initialize(
        self.backend,
        self.width,
        self.height,
      )
    _ => ()
  }
  self.initialized = true
}

///|
pub impl GraphicsDriver for StubGraphicsDriver with fn begin(self, pass) {
  let _ = self.backend
  if self.initialized {
    // Re-check web initialization if not yet active (async WebGPU device init)
    if !self.web_active {
      match self.backend {
        GraphicsBackendKind::WebGpu | GraphicsBackendKind::WebGl2 =>
          self.web_active = web_graphics_try_initialize(
            self.backend,
            self.width,
            self.height,
          )
        _ => ()
      }
    }
    self.begin_count = self.begin_count + 1
    native_on_begin(self.native_active, pass)
    web_graphics_on_begin(self.web_active, self.backend, pass)
  }
}

///|
pub impl GraphicsDriver for StubGraphicsDriver with fn end(self, present) {
  if self.initialized {
    self.end_count = self.end_count + 1
    native_on_end(self.native_active, present)
    web_graphics_on_end(self.web_active, self.backend, present)
  }
}

///|
pub impl GraphicsDriver for StubGraphicsDriver with fn resize(
  self,
  width,
  height,
) {
  let next_width = if width <= 0 { 1 } else { width }
  let next_height = if height <= 0 { 1 } else { height }
  if self.width == next_width && self.height == next_height {
    self.resize_suppressed_count = self.resize_suppressed_count + 1
    return ()
  }
  let t0 = (graphics_clock_provider.val)()
  self.resize_count = self.resize_count + 1
  self.width = next_width
  self.height = next_height
  if self.initialized {
    native_on_resize(self.native_active, next_width, next_height)
    web_graphics_on_resize(
      self.web_active,
      self.backend,
      next_width,
      next_height,
    )
  }
  let t1 = (graphics_clock_provider.val)()
  let duration = t1 - t0
  self.last_resize_duration_ms = duration
  self.total_resize_duration_ms = self.total_resize_duration_ms + duration
}

///|
/// Snapshot of resize counters and the latest resize duration. Useful
/// for diagnosing flaps where the host requests many no-op resizes.
pub fn StubGraphicsDriver::resize_stats(
  self : StubGraphicsDriver,
) -> GraphicsResizeStats {
  {
    resize_count: self.resize_count,
    suppressed_count: self.resize_suppressed_count,
    current_width: self.width,
    current_height: self.height,
    last_resize_duration_ms: self.last_resize_duration_ms,
    total_resize_duration_ms: self.total_resize_duration_ms,
  }
}

///|
pub impl GraphicsDriver for StubGraphicsDriver with fn new_image(
  self,
  width,
  height,
) {
  self.next_id = self.next_id + 1
  native_on_new_image(self.native_active, self.next_id, width, height)
  { id: self.next_id, width, height }
}

///|
pub impl GraphicsDriver for StubGraphicsDriver with fn new_shader(self, source) {
  self.next_id = self.next_id + 1
  { id: self.next_id, source }
}

///|
pub impl GraphicsDriver for StubGraphicsDriver with fn draw_triangles(
  self,
  command,
) {
  if self.initialized {
    self.draw_count = self.draw_count + 1
    native_on_draw(self.native_active, command)
    web_graphics_on_draw(self.web_active, self.backend, command)
  }
}

///|
pub impl GraphicsDriver for StubGraphicsDriver with fn read_pixels(
  self,
  x,
  y,
  width,
  height,
) {
  if !self.initialized {
    return None
  }
  let read_w = if x + width > self.width { self.width - x } else { width }
  let read_h = if y + height > self.height { self.height - y } else { height }
  if read_w <= 0 || read_h <= 0 {
    return None
  }
  match native_on_read_pixels(self.native_active, x, y, read_w, read_h) {
    Some(pixels) => Some(pixels)
    None =>
      web_graphics_on_read_pixels(
        self.web_active,
        self.backend,
        x,
        y,
        read_w,
        read_h,
      )
  }
}