///|
pub(all) struct ViewIdentity {
  kind : String
  key : Key?
} derive(Eq)

///|
pub(all) struct ViewLayoutContext {
  constraints : Constraints
  child_sizes : Array[Size]
  child_flex_weights : Array[Double]
  text_system : TextSystem
  environment : Environment
  style : ViewStyle
}

///|
pub(all) struct ViewLayoutResult {
  size : Size
  child_frames : Array[Rect]
} derive(Eq, Debug)

///|
/// Recursive layout tree produced by the layout pass. Unlike
/// `ViewLayoutResult` (a single node's measured size + child frames),
/// `LayoutResult` is the fully-nested tree emitted by `AppRuntime::layout`.
pub(all) struct LayoutResult {
  frame : Rect
  children : Array[LayoutResult]
} derive(Eq, Debug, ToJson)

///|
pub fn hit_test(layout : LayoutResult, point : Point) -> Bool {
  if !layout.frame.contains(point) {
    false
  } else if layout.children.length() == 0 {
    true
  } else {
    layout.children.any(child => hit_test(child, point))
  }
}

///|
pub(all) struct ViewPaintContext {
  frame : Rect
  child_frames : Array[Rect]
  focused : Bool
  state : ViewStateContext
  text_control : TextControlStateContext
  environment : Environment
  style : ViewStyle
  text_system : TextSystem
  /// Current animation clock time in milliseconds, threaded from the runtime's
  /// `FrameTick`. Controls read this to compute transition `progress` between
  /// a state-change timestamp and `now_ms`. Stays 0.0 when no clock is active
  /// (e.g. the intrinsic-size layout path), which makes progress clamp to the
  /// terminal state so non-animated paint is unchanged.
  now_ms : Double
}

///|
pub(all) struct ViewPaintPlan {
  commands : Array[DrawCommand]
  child_layers : Array[ViewPaintLayer]
  post_commands : Array[DrawCommand]
  overlay_commands : Array[DrawCommand]
  platform_views : Array[PlatformViewPlacement]
  repaint_boundary : Bool
  /// True when this paint pass is a mid-animation sample (progress < 1.0) and
  /// the runtime should keep requesting redraws until the animation settles.
  /// The runtime folds this across the painted tree to decide whether to keep
  /// ticking. Controls that hard-cut between states leave this false.
  animating : Bool
} derive(Eq, Debug)

///|
pub(all) struct ViewPaintLayer {
  frame : Rect
  cache_key : String
  content_revision : Int
  commands : Array[DrawCommand]
  overlay_commands : Array[DrawCommand]
  platform_views : Array[PlatformViewPlacement]
} derive(Eq, Debug)

///|
pub(all) struct ViewEventContext {
  frame : Rect
  child_frames : Array[Rect]
  event : AppEvent
  focused : Bool
  state : ViewStateContext
  text_control : TextControlStateContext
  text_system : TextSystem
  environment : Environment
  style : ViewStyle
  /// Current animation clock time in ms, matching `ViewPaintContext.now_ms`.
  /// Event handlers read this to timestamp state transitions (e.g. recording
  /// when a button entered hover) so the next paint can interpolate progress.
  now_ms : Double
}

///|
pub(all) enum ViewDirtyHint {
  ViewClean
  ViewPaintDirty
  ViewLayoutDirty
  ViewBuildDirty
} derive(Eq, Debug)

///|
pub(all) struct ViewEventResult[Msg] {
  changed : Bool
  activated : Bool
  focused : Bool
  captured : Bool
  state : ViewStateContext?
  text_control : TextControlStateContext?
  dirty : ViewDirtyHint
  messages : Array[Msg]
} derive(Eq, Debug)

///|
pub(all) struct ViewTextCommandResult[Msg] {
  result : TextCommandResult
  text_control : TextControlStateContext?
  messages : Array[Msg]
} derive(Eq, Debug)

///|
pub(all) struct ViewSemanticsInfo {
  role : SemanticsRole
  label : String
  value : String
  description : String
  actions : Array[SemanticsAction]
  disabled : Bool?
  selected : Bool?
  pressed : Bool?
  checked : Bool?
  expanded : Bool?
  invalid : Bool?
  required : Bool?
} derive(Eq, Debug)

///|
pub(all) struct ViewStateContext {
  focused : Bool
  hovered : Bool
  pressed : Bool
  selected : Bool
  checked : Bool
  expanded : Bool
  disabled : Bool
  scratch : String
} derive(Eq, Debug)

///|
pub(all) struct TextHistoryEntryContext {
  text : String
  caret : Int
  selection : TextRange?
} derive(Eq, Debug, ToJson)

///|
pub(all) struct TextControlStateContext {
  caret : Int
  selection : TextRange?
  text_selection_anchor : Int?
  composition : String?
  composition_cursor : TextRange?
  undo_stack : Array[TextHistoryEntryContext]
  redo_stack : Array[TextHistoryEntryContext]
} derive(Eq, Debug, ToJson)

///|
priv struct ViewNode[Msg] {
  identity_fn : () -> ViewIdentity
  revision_fn : () -> String
  // paint_revision 变化只触发 paint dirty(重算 paint + 清 render cache),
  // 不触发 layout dirty(不清 layout cache、不重算 layout)。
  // 用于滚动等"只影响 paint 不影响 layout"的场景。
  paint_revision_fn : () -> String
  children_fn : () -> Array[View[Msg]]
  build_child_fn : ((ComponentContext) -> View[Msg])?
  child_constraints_fn : (Constraints) -> Constraints
  child_environment_fn : (Environment) -> Environment
  child_style_fn : (ViewStyle) -> ViewStyle
  layout_fn : (ViewLayoutContext) -> ViewLayoutResult
  paint_fn : (ViewPaintContext) -> ViewPaintPlan
  event_fn : (ViewEventContext) -> ViewEventResult[Msg]
  text_input_state_fn : (ViewPaintContext) -> TextInputState?
  text_command_fn : (ViewEventContext, CommandIntent, String?) -> ViewTextCommandResult[
    Msg,
  ]
  semantics_fn : () -> ViewSemanticsInfo
  focusable_fn : () -> Bool
  flex_weight_fn : () -> Double
}

///|
pub fn ViewIdentity::new(kind~ : String, key? : String? = None) -> ViewIdentity {
  {
    kind,
    key: match key {
      Some(value) => Some(Key::new(value))
      None => None
    },
  }
}

///|
pub fn ViewLayoutResult::new(
  size~ : Size,
  child_frames? : Array[Rect] = [],
) -> ViewLayoutResult {
  { size, child_frames }
}

///|
pub fn ViewPaintPlan::empty() -> ViewPaintPlan {
  {
    commands: [],
    child_layers: [],
    post_commands: [],
    overlay_commands: [],
    platform_views: [],
    repaint_boundary: false,
    animating: false,
  }
}

///|
pub fn ViewPaintPlan::commands(commands : Array[DrawCommand]) -> ViewPaintPlan {
  {
    commands,
    child_layers: [],
    post_commands: [],
    overlay_commands: [],
    platform_views: [],
    repaint_boundary: false,
    animating: false,
  }
}

///|
/// Like `ViewPaintPlan::commands` but flags the paint as a mid-animation
/// sample. The runtime keeps requesting redraws while any painted plan in the
/// tree reports `animating=true`. Controls set this when their transition
/// `progress < 1.0` (and `reduced_motion` is not in effect).
pub fn ViewPaintPlan::animating_commands(
  commands : Array[DrawCommand],
) -> ViewPaintPlan {
  { ..ViewPaintPlan::commands(commands), animating: true }
}

///|
pub fn ViewPaintLayer::new(
  frame~ : Rect,
  cache_key~ : String,
  content_revision~ : Int,
  commands? : Array[DrawCommand] = [],
  overlay_commands? : Array[DrawCommand] = [],
  platform_views? : Array[PlatformViewPlacement] = [],
) -> ViewPaintLayer {
  {
    frame,
    cache_key,
    content_revision,
    commands,
    overlay_commands,
    platform_views,
  }
}

///|
pub fn[Msg] ViewEventResult::ignored() -> ViewEventResult[Msg] {
  {
    changed: false,
    activated: false,
    focused: false,
    captured: false,
    state: None,
    text_control: None,
    dirty: ViewClean,
    messages: [],
  }
}

///|
pub fn[Msg] ViewEventResult::message(message : Msg) -> ViewEventResult[Msg] {
  {
    changed: true,
    activated: true,
    focused: false,
    captured: true,
    state: None,
    text_control: None,
    dirty: ViewPaintDirty,
    messages: [message],
  }
}

///|
pub fn[Msg] ViewTextCommandResult::ignored() -> ViewTextCommandResult[Msg] {
  { result: TextCommandResult::ignored(), text_control: None, messages: [] }
}

///|
pub fn[Msg] ViewTextCommandResult::new(
  result~ : TextCommandResult,
  text_control? : TextControlStateContext? = None,
  messages? : Array[Msg] = [],
) -> ViewTextCommandResult[Msg] {
  { result, text_control, messages }
}

///|
pub fn ViewSemanticsInfo::new(
  role? : SemanticsRole = SemanticsRole::Group,
  label? : String = "",
  value? : String = "",
  description? : String = "",
  actions? : Array[SemanticsAction] = [],
  disabled? : Bool,
  selected? : Bool,
  pressed? : Bool,
  checked? : Bool,
  expanded? : Bool,
  invalid? : Bool,
  required? : Bool,
) -> ViewSemanticsInfo {
  {
    role,
    label,
    value,
    description,
    actions,
    disabled,
    selected,
    pressed,
    checked,
    expanded,
    invalid,
    required,
  }
}

///|
fn[Msg] ViewNode::new(
  identity~ : () -> ViewIdentity,
  revision? : () -> String = () => "",
  paint_revision? : () -> String = () => "",
  children? : () -> Array[View[Msg]] = () => [],
  build_child? : ((ComponentContext) -> View[Msg])? = None,
  child_constraints? : (Constraints) -> Constraints = constraints => {
    constraints.loosen()
  },
  child_environment? : (Environment) -> Environment = environment => environment,
  child_style? : (ViewStyle) -> ViewStyle = style => style,
  layout~ : (ViewLayoutContext) -> ViewLayoutResult,
  paint? : (ViewPaintContext) -> ViewPaintPlan = _ctx => ViewPaintPlan::empty(),
  event? : (ViewEventContext) -> ViewEventResult[Msg] = _ctx => {
    ViewEventResult::ignored()
  },
  text_input_state? : (ViewPaintContext) -> TextInputState? = _ctx => None,
  text_command? : (ViewEventContext, CommandIntent, String?) -> ViewTextCommandResult[
    Msg,
  ] = (_ctx, _intent, _paste_text) => ViewTextCommandResult::ignored(),
  semantics? : () -> ViewSemanticsInfo = () => ViewSemanticsInfo::new(),
  focusable? : () -> Bool = () => false,
  flex_weight? : () -> Double = () => 0.0,
) -> ViewNode[Msg] {
  {
    identity_fn: identity,
    revision_fn: revision,
    paint_revision_fn: paint_revision,
    children_fn: children,
    build_child_fn: build_child,
    child_constraints_fn: child_constraints,
    child_environment_fn: child_environment,
    child_style_fn: child_style,
    layout_fn: layout,
    paint_fn: paint,
    event_fn: event,
    text_input_state_fn: text_input_state,
    text_command_fn: text_command,
    semantics_fn: semantics,
    focusable_fn: focusable,
    flex_weight_fn: flex_weight,
  }
}

///|
fn[Msg] ViewNode::identity(self : ViewNode[Msg]) -> ViewIdentity {
  (self.identity_fn)()
}

///|
fn[Msg] ViewNode::revision(self : ViewNode[Msg]) -> String {
  (self.revision_fn)()
}

///|
fn[Msg] ViewNode::paint_revision(self : ViewNode[Msg]) -> String {
  (self.paint_revision_fn)()
}

///|
fn[Msg] ViewNode::children(self : ViewNode[Msg]) -> Array[View[Msg]] {
  (self.children_fn)()
}

///|
fn[Msg] ViewNode::build_child(
  self : ViewNode[Msg],
) -> ((ComponentContext) -> View[Msg])? {
  self.build_child_fn
}

///|
fn[Msg] ViewNode::child_constraints(
  self : ViewNode[Msg],
  constraints : Constraints,
) -> Constraints {
  (self.child_constraints_fn)(constraints)
}

///|
fn[Msg] ViewNode::child_environment(
  self : ViewNode[Msg],
  environment : Environment,
) -> Environment {
  (self.child_environment_fn)(environment)
}

///|
fn[Msg] ViewNode::child_style(
  self : ViewNode[Msg],
  style : ViewStyle,
) -> ViewStyle {
  (self.child_style_fn)(style)
}

///|
fn[Msg] ViewNode::layout(
  self : ViewNode[Msg],
  context : ViewLayoutContext,
) -> ViewLayoutResult {
  (self.layout_fn)(context)
}

///|
fn[Msg] ViewNode::paint(
  self : ViewNode[Msg],
  context : ViewPaintContext,
) -> ViewPaintPlan {
  (self.paint_fn)(context)
}

///|
fn[Msg] ViewNode::event(
  self : ViewNode[Msg],
  context : ViewEventContext,
) -> ViewEventResult[Msg] {
  (self.event_fn)(context)
}

///|
fn[Msg] ViewNode::text_input_state(
  self : ViewNode[Msg],
  context : ViewPaintContext,
) -> TextInputState? {
  (self.text_input_state_fn)(context)
}

///|
fn[Msg] ViewNode::text_command(
  self : ViewNode[Msg],
  context : ViewEventContext,
  intent : CommandIntent,
  paste_text? : String? = None,
) -> TextCommandResult {
  (self.text_command_fn)(context, intent, paste_text).result
}

///|
fn[Msg] ViewNode::text_command_result(
  self : ViewNode[Msg],
  context : ViewEventContext,
  intent : CommandIntent,
  paste_text? : String? = None,
) -> ViewTextCommandResult[Msg] {
  (self.text_command_fn)(context, intent, paste_text)
}

///|
fn[Msg] ViewNode::semantics(self : ViewNode[Msg]) -> ViewSemanticsInfo {
  (self.semantics_fn)()
}

///|
fn[Msg] ViewNode::focusable(self : ViewNode[Msg]) -> Bool {
  (self.focusable_fn)()
}

///|
fn[Msg] ViewNode::flex_weight(self : ViewNode[Msg]) -> Double {
  (self.flex_weight_fn)()
}

///|
fn[Msg, NextMsg] ViewNode::map(
  self : ViewNode[Msg],
  map : (Msg) -> NextMsg,
) -> ViewNode[NextMsg] {
  ViewNode::new(
    identity=() => self.identity(),
    revision=() => self.revision(),
    paint_revision=() => self.paint_revision(),
    children=() => self.children().map(child => child.map(map)),
    build_child=match self.build_child() {
      Some(build) => Some(ctx => build(ctx).map(map))
      None => None
    },
    child_constraints=constraints => self.child_constraints(constraints),
    child_environment=environment => self.child_environment(environment),
    child_style=style => self.child_style(style),
    layout=context => self.layout(context),
    paint=context => self.paint(context),
    event=context => {
      let result = self.event(context)
      {
        changed: result.changed,
        activated: result.activated,
        focused: result.focused,
        captured: result.captured,
        state: result.state,
        text_control: result.text_control,
        dirty: result.dirty,
        messages: result.messages.map(message => map(message)),
      }
    },
    text_input_state=context => self.text_input_state(context),
    text_command=(context, intent, paste_text) => {
      let result = self.text_command_result(context, intent, paste_text~)
      {
        result: result.result,
        text_control: result.text_control,
        messages: result.messages.map(message => map(message)),
      }
    },
    semantics=() => self.semantics(),
    focusable=() => self.focusable(),
    flex_weight=() => self.flex_weight(),
  )
}

///|
pub struct View[Msg] {
  priv node : ViewNode[Msg]
}

///|
pub fn[Msg] View::node(
  identity~ : () -> ViewIdentity,
  revision? : () -> String = () => "",
  paint_revision? : () -> String = () => "",
  children? : () -> Array[View[Msg]] = () => [],
  build_child? : ((ComponentContext) -> View[Msg])? = None,
  child_constraints? : (Constraints) -> Constraints = constraints => {
    constraints.loosen()
  },
  child_environment? : (Environment) -> Environment = environment => environment,
  child_style? : (ViewStyle) -> ViewStyle = style => style,
  layout~ : (ViewLayoutContext) -> ViewLayoutResult,
  paint? : (ViewPaintContext) -> ViewPaintPlan = _ctx => ViewPaintPlan::empty(),
  event? : (ViewEventContext) -> ViewEventResult[Msg] = _ctx => {
    ViewEventResult::ignored()
  },
  text_input_state? : (ViewPaintContext) -> TextInputState? = _ctx => None,
  text_command? : (ViewEventContext, CommandIntent, String?) -> ViewTextCommandResult[
    Msg,
  ] = (_ctx, _intent, _paste_text) => ViewTextCommandResult::ignored(),
  semantics? : () -> ViewSemanticsInfo = () => ViewSemanticsInfo::new(),
  focusable? : () -> Bool = () => false,
  flex_weight? : () -> Double = () => 0.0,
) -> View[Msg] {
  {
    node: ViewNode::new(
      identity~,
      revision~,
      paint_revision~,
      children~,
      build_child~,
      child_constraints~,
      child_environment~,
      child_style~,
      layout~,
      paint~,
      event~,
      text_input_state~,
      text_command~,
      semantics~,
      focusable~,
      flex_weight~,
    ),
  }
}

///|
pub fn[Msg] View::identity(self : View[Msg]) -> ViewIdentity {
  self.node.identity()
}

///|
pub fn[Msg] View::revision(self : View[Msg]) -> String {
  self.node.revision()
}

///|
pub fn[Msg] View::paint_revision(self : View[Msg]) -> String {
  self.node.paint_revision()
}

///|
pub fn[Msg] View::children(self : View[Msg]) -> Array[View[Msg]] {
  self.node.children()
}

///|
pub fn[Msg] View::build_child(
  self : View[Msg],
) -> ((ComponentContext) -> View[Msg])? {
  self.node.build_child()
}

///|
pub fn[Msg] View::child_constraints(
  self : View[Msg],
  constraints : Constraints,
) -> Constraints {
  self.node.child_constraints(constraints)
}

///|
pub fn[Msg] View::child_environment(
  self : View[Msg],
  environment : Environment,
) -> Environment {
  self.node.child_environment(environment)
}

///|
pub fn[Msg] View::child_style(self : View[Msg], style : ViewStyle) -> ViewStyle {
  self.node.child_style(style)
}

///|
pub fn[Msg] View::layout(
  self : View[Msg],
  context : ViewLayoutContext,
) -> ViewLayoutResult {
  self.node.layout(context)
}

///|
pub fn[Msg] View::paint(
  self : View[Msg],
  context : ViewPaintContext,
) -> ViewPaintPlan {
  self.node.paint(context)
}

///|
pub fn[Msg] View::event(
  self : View[Msg],
  context : ViewEventContext,
) -> ViewEventResult[Msg] {
  self.node.event(context)
}

///|
pub fn[Msg] View::text_input_state(
  self : View[Msg],
  context : ViewPaintContext,
) -> TextInputState? {
  self.node.text_input_state(context)
}

///|
pub fn[Msg] View::text_command(
  self : View[Msg],
  context : ViewEventContext,
  intent : CommandIntent,
  paste_text? : String? = None,
) -> TextCommandResult {
  self.node.text_command(context, intent, paste_text~)
}

///|
pub fn[Msg] View::text_command_result(
  self : View[Msg],
  context : ViewEventContext,
  intent : CommandIntent,
  paste_text? : String? = None,
) -> ViewTextCommandResult[Msg] {
  self.node.text_command_result(context, intent, paste_text~)
}

///|
pub fn[Msg] View::semantics(self : View[Msg]) -> ViewSemanticsInfo {
  self.node.semantics()
}

///|
pub fn[Msg] View::accepts_focus(self : View[Msg]) -> Bool {
  self.node.focusable()
}

///|
pub fn[Msg] View::flex_weight(self : View[Msg]) -> Double {
  self.node.flex_weight()
}