///|
/// Core Types for Browser Primitives
///
/// These types are used by all layers:
/// - Core Primitives (direct API)
/// - CDP Domains
/// - WebDriver
/// - WASM exports

///|
/// Node identifier - opaque handle to a DOM node
pub struct NodeId(Int) derive(Eq, Hash, Debug)

///|
/// Rectangle with position and size
pub struct Rect {
  x : Double
  y : Double
  width : Double
  height : Double
} derive(Eq, Debug)

///|
/// 2D Point
pub struct Point {
  x : Double
  y : Double
} derive(Eq, Debug)

///|
/// Node type enumeration
pub(all) enum NodeType {
  Document
  DocumentType
  Element
  Text
  Comment
  DocumentFragment
  ShadowRoot
} derive(Eq, Debug)

///|
/// Convert DOM node type constant to NodeType
/// See: https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
pub fn NodeType::from_dom_constant(value : Int) -> NodeType? {
  match value {
    1 => Some(Element)
    3 => Some(Text)
    8 => Some(Comment)
    9 => Some(Document)
    10 => Some(DocumentType)
    11 => Some(DocumentFragment)
    _ => None
  }
}

///|
/// Basic node information
pub struct NodeInfo {
  node_id : NodeId
  node_type : NodeType
  node_name : String
  node_value : String
  child_count : Int
} derive(Debug)

///|
pub struct ShadowRootInit {
  mode : String
  delegates_focus : Bool
  slot_assignment : String
  clonable : Bool
  serializable : Bool
} derive(Eq, Debug)

///|
pub fn ShadowRootInit::default(mode? : String = "open") -> ShadowRootInit {
  {
    mode,
    delegates_focus: false,
    slot_assignment: "named",
    clonable: false,
    serializable: false,
  }
}

///|
pub fn ShadowRootInit::new(
  mode~ : String,
  delegates_focus? : Bool = false,
  slot_assignment? : String = "named",
  clonable? : Bool = false,
  serializable? : Bool = false,
) -> ShadowRootInit {
  { mode, delegates_focus, slot_assignment, clonable, serializable }
}

///|
/// Core error types
pub enum CoreError {
  NodeNotFound(node_id~ : NodeId)
  InvalidOperation(message~ : String)
} derive(Debug)

///|
/// Scroll state for a scrollable element
pub struct ScrollState {
  scroll_left : Double
  scroll_top : Double
  scroll_width : Double
  scroll_height : Double
  client_width : Double
  client_height : Double
} derive(Eq, Debug)

///|
/// Create a new ScrollState
pub fn ScrollState::new(
  scroll_width : Double,
  scroll_height : Double,
  client_width : Double,
  client_height : Double,
) -> ScrollState {
  {
    scroll_left: 0.0,
    scroll_top: 0.0,
    scroll_width,
    scroll_height,
    client_width,
    client_height,
  }
}

///|
/// Create a default ScrollState (no scrolling)
pub fn ScrollState::default() -> ScrollState {
  {
    scroll_left: 0.0,
    scroll_top: 0.0,
    scroll_width: 0.0,
    scroll_height: 0.0,
    client_width: 0.0,
    client_height: 0.0,
  }
}

///|
/// Clamp a value between min and max
fn clamp(value : Double, min_val : Double, max_val : Double) -> Double {
  if value < min_val {
    min_val
  } else if value > max_val {
    max_val
  } else {
    value
  }
}

///|
/// Set scroll position (clamped to valid range)
pub fn ScrollState::scroll_to(
  self : ScrollState,
  left : Double,
  top : Double,
) -> ScrollState {
  let max_left = if self.scroll_width > self.client_width {
    self.scroll_width - self.client_width
  } else {
    0.0
  }
  let max_top = if self.scroll_height > self.client_height {
    self.scroll_height - self.client_height
  } else {
    0.0
  }
  {
    ..self,
    scroll_left: clamp(left, 0.0, max_left),
    scroll_top: clamp(top, 0.0, max_top),
  }
}

///|
/// Scroll by delta (relative scroll)
pub fn ScrollState::scroll_by(
  self : ScrollState,
  delta_x : Double,
  delta_y : Double,
) -> ScrollState {
  self.scroll_to(self.scroll_left + delta_x, self.scroll_top + delta_y)
}

///|
/// Check if horizontal scrolling is possible
pub fn ScrollState::can_scroll_x(self : ScrollState) -> Bool {
  self.scroll_width > self.client_width
}

///|
/// Check if vertical scrolling is possible
pub fn ScrollState::can_scroll_y(self : ScrollState) -> Bool {
  self.scroll_height > self.client_height
}

///|
/// Get maximum scroll position (horizontal)
pub fn ScrollState::max_scroll_left(self : ScrollState) -> Double {
  if self.scroll_width > self.client_width {
    self.scroll_width - self.client_width
  } else {
    0.0
  }
}

///|
/// Get maximum scroll position (vertical)
pub fn ScrollState::max_scroll_top(self : ScrollState) -> Double {
  if self.scroll_height > self.client_height {
    self.scroll_height - self.client_height
  } else {
    0.0
  }
}

///|
/// Create a new Rect
pub fn Rect::new(
  x : Double,
  y : Double,
  width : Double,
  height : Double,
) -> Rect {
  { x, y, width, height }
}

///|
/// Convert Rect to center point
pub fn Rect::center(self : Rect) -> Point {
  { x: self.x + self.width / 2.0, y: self.y + self.height / 2.0 }
}

///|
/// Create NodeId from Int
pub fn NodeId::from_int(id : Int) -> NodeId {
  NodeId(id)
}

///|
/// Get Int value from NodeId
pub fn NodeId::to_int(self : NodeId) -> Int {
  self.0
}

// =============================================================================
// SVG DOM Geometry Types
// =============================================================================

///|
/// SVGRect - represents a rectangle in SVG coordinate space
/// Used by getBBox(), getClientRect(), and other SVG methods
pub struct SVGRect {
  x : Double
  y : Double
  width : Double
  height : Double
} derive(Eq, Debug)

///|
/// Create a new SVGRect
pub fn SVGRect::new(
  x : Double,
  y : Double,
  width : Double,
  height : Double,
) -> SVGRect {
  { x, y, width, height }
}

///|
/// Create an empty SVGRect (0, 0, 0, 0)
pub fn SVGRect::empty() -> SVGRect {
  { x: 0.0, y: 0.0, width: 0.0, height: 0.0 }
}

///|
/// Convert SVGRect to Rect
pub fn SVGRect::to_rect(self : SVGRect) -> Rect {
  { x: self.x, y: self.y, width: self.width, height: self.height }
}

///|
/// Create SVGRect from Rect
pub fn SVGRect::from_rect(rect : Rect) -> SVGRect {
  { x: rect.x, y: rect.y, width: rect.width, height: rect.height }
}

///|
/// SVGPoint - represents a point in SVG coordinate space
/// Has matrixTransform method for coordinate transformations
pub struct SVGPoint {
  x : Double
  y : Double
} derive(Eq, Debug)

///|
/// Create a new SVGPoint
pub fn SVGPoint::new(x : Double, y : Double) -> SVGPoint {
  { x, y }
}

///|
/// Create an SVGPoint at origin (0, 0)
pub fn SVGPoint::origin() -> SVGPoint {
  { x: 0.0, y: 0.0 }
}

///|
/// Transform this point by a matrix
pub fn SVGPoint::matrix_transform(
  self : SVGPoint,
  matrix : SVGMatrix,
) -> SVGPoint {
  let new_x = matrix.a * self.x + matrix.c * self.y + matrix.e
  let new_y = matrix.b * self.x + matrix.d * self.y + matrix.f
  { x: new_x, y: new_y }
}

///|
/// SVGMatrix - 2D transformation matrix for SVG
/// Represents a 3x3 matrix: | a c e |
///                          | b d f |
///                          | 0 0 1 |
pub struct SVGMatrix {
  a : Double // scale x
  b : Double // skew y
  c : Double // skew x
  d : Double // scale y
  e : Double // translate x
  f : Double // translate y
} derive(Eq, Debug)

///|
pub impl Show for NodeId with fn output(self, logger) {
  logger.write_string("NodeId(\{self.0})")
}

///|
pub impl Show for Rect with fn output(self, logger) {
  logger.write_string(
    "{x: \{self.x}, y: \{self.y}, width: \{self.width}, height: \{self.height}}",
  )
}

///|
pub impl Show for Point with fn output(self, logger) {
  logger.write_string("{x: \{self.x}, y: \{self.y}}")
}

///|
pub impl Show for NodeType with fn output(self, logger) {
  let name = match self {
    Document => "Document"
    DocumentType => "DocumentType"
    Element => "Element"
    Text => "Text"
    Comment => "Comment"
    DocumentFragment => "DocumentFragment"
    ShadowRoot => "ShadowRoot"
  }
  logger.write_string(name)
}

///|
pub impl Show for NodeInfo with fn output(self, logger) {
  logger.write_string(
    "{node_id: \{self.node_id}, node_type: \{self.node_type}, ",
  )
  logger.write_string("node_name: ")
  logger.write_string("\"")
  logger.write_string(self.node_name)
  logger.write_string("\"")
  logger.write_string(", node_value: ")
  logger.write_string("\"")
  logger.write_string(self.node_value)
  logger.write_string("\"")
  logger.write_string(", child_count: \{self.child_count}}")
}

///|
pub impl Show for CoreError with fn output(self, logger) {
  match self {
    NodeNotFound(node_id~) =>
      logger.write_string("NodeNotFound(node_id=\{node_id})")
    InvalidOperation(message~) => {
      logger.write_string("InvalidOperation(message=")
      logger.write_string("\"")
      logger.write_string(message)
      logger.write_string("\"")
      logger.write_string(")")
    }
  }
}

///|
pub impl Show for ScrollState with fn output(self, logger) {
  logger.write_string("{scroll_left: \{self.scroll_left}, ")
  logger.write_string("scroll_top: \{self.scroll_top}, ")
  logger.write_string("scroll_width: \{self.scroll_width}, ")
  logger.write_string("scroll_height: \{self.scroll_height}, ")
  logger.write_string("client_width: \{self.client_width}, ")
  logger.write_string("client_height: \{self.client_height}}")
}

///|
pub impl Show for SVGRect with fn output(self, logger) {
  logger.write_string(
    "{x: \{self.x}, y: \{self.y}, width: \{self.width}, height: \{self.height}}",
  )
}

///|
pub impl Show for SVGPoint with fn output(self, logger) {
  logger.write_string("{x: \{self.x}, y: \{self.y}}")
}

///|
pub impl Show for SVGMatrix with fn output(self, logger) {
  logger.write_string(
    "{a: \{self.a}, b: \{self.b}, c: \{self.c}, d: \{self.d}, ",
  )
  logger.write_string("e: \{self.e}, f: \{self.f}}")
}

///|
/// Create an identity matrix
pub fn SVGMatrix::identity() -> SVGMatrix {
  { a: 1.0, b: 0.0, c: 0.0, d: 1.0, e: 0.0, f: 0.0 }
}

///|
/// Create a new SVGMatrix with specified values
pub fn SVGMatrix::new(
  a : Double,
  b : Double,
  c : Double,
  d : Double,
  e : Double,
  f : Double,
) -> SVGMatrix {
  { a, b, c, d, e, f }
}

///|
/// Multiply this matrix by another matrix (this * other)
pub fn SVGMatrix::multiply(self : SVGMatrix, other : SVGMatrix) -> SVGMatrix {
  {
    a: self.a * other.a + self.c * other.b,
    b: self.b * other.a + self.d * other.b,
    c: self.a * other.c + self.c * other.d,
    d: self.b * other.c + self.d * other.d,
    e: self.a * other.e + self.c * other.f + self.e,
    f: self.b * other.e + self.d * other.f + self.f,
  }
}

///|
/// Get the inverse of this matrix
pub fn SVGMatrix::inverse(self : SVGMatrix) -> SVGMatrix? {
  let det = self.a * self.d - self.b * self.c
  if det == 0.0 {
    return None
  }
  let inv_det = 1.0 / det
  Some({
    a: self.d * inv_det,
    b: -self.b * inv_det,
    c: -self.c * inv_det,
    d: self.a * inv_det,
    e: (self.c * self.f - self.d * self.e) * inv_det,
    f: (self.b * self.e - self.a * self.f) * inv_det,
  })
}

///|
/// Create a translation matrix
pub fn SVGMatrix::translate(tx : Double, ty : Double) -> SVGMatrix {
  { a: 1.0, b: 0.0, c: 0.0, d: 1.0, e: tx, f: ty }
}

///|
/// Create a scale matrix
pub fn SVGMatrix::scale(sx : Double, sy : Double) -> SVGMatrix {
  { a: sx, b: 0.0, c: 0.0, d: sy, e: 0.0, f: 0.0 }
}

///|
/// Create a uniform scale matrix
pub fn SVGMatrix::scale_uniform(s : Double) -> SVGMatrix {
  SVGMatrix::scale(s, s)
}

///|
/// Create a rotation matrix (angle in radians)
pub fn SVGMatrix::rotate(angle : Double) -> SVGMatrix {
  let cos_a = @math.cos(angle)
  let sin_a = @math.sin(angle)
  { a: cos_a, b: sin_a, c: -sin_a, d: cos_a, e: 0.0, f: 0.0 }
}

///|
/// Create a rotation matrix (angle in degrees)
pub fn SVGMatrix::rotate_deg(angle_deg : Double) -> SVGMatrix {
  SVGMatrix::rotate(angle_deg * 3.14159265358979323846 / 180.0)
}

///|
/// Create a skew X matrix (angle in radians)
pub fn SVGMatrix::skew_x(angle : Double) -> SVGMatrix {
  { a: 1.0, b: 0.0, c: @math.tan(angle), d: 1.0, e: 0.0, f: 0.0 }
}

///|
/// Create a skew Y matrix (angle in radians)
pub fn SVGMatrix::skew_y(angle : Double) -> SVGMatrix {
  { a: 1.0, b: @math.tan(angle), c: 0.0, d: 1.0, e: 0.0, f: 0.0 }
}

///|
/// Apply translation to this matrix (returns new matrix)
pub fn SVGMatrix::translate_self(
  self : SVGMatrix,
  tx : Double,
  ty : Double,
) -> SVGMatrix {
  self.multiply(SVGMatrix::translate(tx, ty))
}

///|
/// Apply scale to this matrix (returns new matrix)
pub fn SVGMatrix::scale_self(
  self : SVGMatrix,
  sx : Double,
  sy : Double,
) -> SVGMatrix {
  self.multiply(SVGMatrix::scale(sx, sy))
}

///|
/// Apply rotation to this matrix (returns new matrix)
pub fn SVGMatrix::rotate_self(self : SVGMatrix, angle : Double) -> SVGMatrix {
  self.multiply(SVGMatrix::rotate(angle))
}

///|
/// Flip on X axis
pub fn SVGMatrix::flip_x(self : SVGMatrix) -> SVGMatrix {
  self.multiply(SVGMatrix::scale(-1.0, 1.0))
}

///|
/// Flip on Y axis
pub fn SVGMatrix::flip_y(self : SVGMatrix) -> SVGMatrix {
  self.multiply(SVGMatrix::scale(1.0, -1.0))
}