///|
fn ElementNode::dispatch_pointer(
  self : ElementNode,
  event : PointerEvent,
  render : RenderNode,
  text_system? : TextSystem = TextSystem::fallback(),
) -> ElementEventResult {
  match self.spec {
    ButtonSpec(data) =>
      handle_button_element_pointer(self, data, event, render.frame)
    CheckboxSpec(data) =>
      handle_checkbox_element_pointer(self, data, event, render.frame)
    PickerSpec(data) =>
      handle_picker_element_pointer(self, data, event, render.frame)
    DatePickerSpec(data) =>
      handle_datepicker_element_pointer(self, data, event, render.frame)
    SearchBarSpec(data) =>
      handle_searchbar_element_pointer(self, data, event, render.frame)
    TextFieldSpec(_) => {
      let focused = if event.phase == PointerPhase::Down &&
        render.frame.contains(event.position) {
        self.control.caret = text_field_text_length(self.spec)
        self.control.selection = None
        self.control.composition = None
        self.control.composition_cursor = None
        Some(self.id)
      } else {
        None
      }
      { changed: false, activated: false, focused }
    }
    RichTextEditorSpec(data) =>
      handle_rich_text_editor_pointer(self, data, event, render, text_system)
    FlexSpec(_)
    | PaddingSpec(_)
    | ContainerSpec(_)
    | FrameSpec(_)
    | StackSpec(_)
    | GridSpec(_)
    | ListSpec(_)
    | CustomLayoutSpec(_)
    | ComponentSpec(_)
    | SheetSpec(_) =>
      self.dispatch_pointer_to_children(event, render, text_system~)
    ScrollSpec(data) =>
      self.dispatch_scroll_pointer(data, event, render, text_system~)
    ModifiedSpec(data) =>
      self.dispatch_modified_pointer(data, event, render, text_system~)
    EmptySpec | TextSpec(_) | ImageSpec(_) | SpacerSpec(_) | CustomSpec(_) =>
      { changed: false, activated: false, focused: None }
  }
}

///|
fn ElementNode::dispatch_scroll_pointer(
  self : ElementNode,
  data : ScrollViewSpec,
  event : PointerEvent,
  render : RenderNode,
  text_system~ : TextSystem,
) -> ElementEventResult {
  if render.frame.contains(event.position) &&
    (event.wheel_delta.x != 0.0 || event.wheel_delta.y != 0.0) {
    match data.on_scroll {
      Some(handler) => {
        handler(event.wheel_delta)
        { changed: true, activated: true, focused: None }
      }
      None => self.dispatch_pointer_to_children(event, render, text_system~)
    }
  } else {
    self.dispatch_pointer_to_children(event, render, text_system~)
  }
}

///|
fn ElementNode::dispatch_pointer_to_children(
  self : ElementNode,
  event : PointerEvent,
  render : RenderNode,
  text_system~ : TextSystem,
) -> ElementEventResult {
  let mut changed = false
  let mut activated = false
  let mut focused : ElementId? = None
  let mut index = self.children.length() - 1
  while index >= 0 {
    if index < render.children.length() {
      let child = self.children[index]
      let child_render = render.children[index]
      if child_render.frame.contains(event.position) ||
        child.expanded_popup_contains(event.position, child_render.frame) {
        let result = child.dispatch_pointer(event, child_render, text_system~)
        changed = result.changed
        activated = result.activated
        match result.focused {
          Some(_) => focused = result.focused
          None => ()
        }
        if result.changed || result.activated || result.focused is Some(_) {
          break
        }
      }
    }
    index = index - 1
  }
  if changed {
    self.dirty.mark_paint()
  }
  { changed, activated, focused }
}

///|
fn ElementNode::expanded_popup_contains(
  self : ElementNode,
  position : Point,
  frame : Rect,
) -> Bool {
  match self.spec {
    PickerSpec(data) =>
      self.control.picker_expanded &&
      picker_popup_frame(data, frame).contains(position)
    DatePickerSpec(_) =>
      self.control.datepicker_expanded &&
      datepicker_popup_frame(frame).contains(position)
    _ => false
  }
}

///|
fn picker_popup_frame(data : PickerViewSpec, frame : Rect) -> Rect {
  let item_height = 36.0
  let count = data.items.length().to_double()
  Rect::new(
    x=frame.origin.x,
    y=frame.origin.y + frame.size.height + 4.0,
    width=frame.size.width,
    height=min_double(data.maxHeight, count * item_height),
  )
}

///|
fn datepicker_popup_frame(frame : Rect) -> Rect {
  Rect::new(
    x=frame.origin.x,
    y=frame.origin.y + frame.size.height + 4.0,
    width=max_double(frame.size.width, 280.0),
    height=350.0,
  )
}

///|
fn ElementNode::dispatch_modified_pointer(
  self : ElementNode,
  data : ModifiedViewSpec,
  event : PointerEvent,
  render : RenderNode,
  text_system~ : TextSystem,
) -> ElementEventResult {
  match data.modifier {
    DisabledModifier(true) =>
      { changed: false, activated: false, focused: None }
    OnTapModifier(handler) => {
      let child_result = self.dispatch_pointer_to_children(
        event,
        render,
        text_system~,
      )
      if child_result.activated {
        child_result
      } else {
        let inside = render.frame.contains(event.position)
        let tap = recognize_button_tap(
          self.control.button_state,
          inside,
          event.phase,
        )
        if tap.activated {
          handler()
        }
        let changed = child_result.changed ||
          tap.next_state != self.control.button_state ||
          tap.activated
        self.control.button_state = tap.next_state
        if changed {
          self.dirty.mark_paint()
        }
        {
          changed,
          activated: child_result.activated || tap.activated,
          focused: child_result.focused,
        }
      }
    }
    _ => self.dispatch_pointer_to_children(event, render, text_system~)
  }
}

///|
fn ElementNode::hit_test_path(
  self : ElementNode,
  point : Point,
  render : RenderNode,
) -> HitTestResult {
  let path : Array[HitTestEntry] = []
  if self.append_hit_test_path(point, render, path) {
    { path, }
  } else {
    { path: [] }
  }
}

///|
fn ElementNode::append_hit_test_path(
  self : ElementNode,
  point : Point,
  render : RenderNode,
  path : Array[HitTestEntry],
) -> Bool {
  if !render.frame.contains(point) {
    false
  } else {
    path.push({
      element_id: self.id,
      local_position: Point::new(
        x=point.x - render.frame.origin.x,
        y=point.y - render.frame.origin.y,
      ),
      frame: render.frame,
    })
    let mut index = self.children.length() - 1
    while index >= 0 {
      if index < render.children.length() &&
        self.children[index].append_hit_test_path(
          point,
          render.children[index],
          path,
        ) {
        return true
      }
      index = index - 1
    }
    true
  }
}

///|
fn text_field_text_length(spec : ViewSpec) -> Int {
  match spec {
    TextFieldSpec(data) => text_length(data.value.get())
    _ => 0
  }
}

///|
fn handle_rich_text_editor_pointer(
  element : ElementNode,
  data : RichTextEditorViewSpec,
  event : PointerEvent,
  render : RenderNode,
  text_system : TextSystem,
) -> ElementEventResult {
  let inside = render.frame.contains(event.position)
  match event.phase {
    Down =>
      if inside {
        match data.on_prefix_click {
          Some(handler) =>
            match
              rich_text_editor_prefix_source_offset_at_point(
                data,
                event.position,
                render.frame,
                text_system,
              ) {
              Some(offset) => handler(offset)
              None => ()
            }
          None => ()
        }
        let caret = rich_text_editor_caret_at_point(
          data,
          event.position,
          render.frame,
          text_system,
        )
        element.control.caret = caret
        element.control.selection = None
        element.control.text_selection_anchor = Some(caret)
        element.control.composition = None
        element.control.composition_cursor = None
        notify_rich_text_selection_change(data, element)
        { changed: true, activated: false, focused: Some(element.id) }
      } else {
        { changed: false, activated: false, focused: None }
      }
    Move =>
      match element.control.text_selection_anchor {
        Some(anchor) => {
          let caret = rich_text_editor_caret_at_point(
            data,
            event.position,
            render.frame,
            text_system,
          )
          let next_selection : TextRange? = if caret == anchor {
            None
          } else {
            Some(TextRange::new(start=anchor, end=caret))
          }
          let changed = element.control.caret != caret ||
            element.control.selection != next_selection
          element.control.caret = caret
          element.control.selection = next_selection
          if changed {
            notify_rich_text_selection_change(data, element)
            element.dirty.mark_paint()
          }
          { changed, activated: false, focused: None }
        }
        None => { changed: false, activated: false, focused: None }
      }
    Up | Cancel => {
      element.control.text_selection_anchor = None
      { changed: false, activated: false, focused: None }
    }
  }
}

///|
fn handle_button_element_pointer(
  element : ElementNode,
  data : ButtonViewSpec,
  event : PointerEvent,
  frame : Rect,
) -> ElementEventResult {
  let inside = frame.contains(event.position)
  let tap = recognize_button_tap(
    element.control.button_state,
    inside,
    event.phase,
  )
  if tap.activated {
    match data.on_click {
      Some(handler) => handler()
      None => ()
    }
  }
  let changed = tap.next_state != element.control.button_state || tap.activated
  element.control.button_state = tap.next_state
  if changed {
    element.dirty.mark_paint()
  }
  { changed, activated: tap.activated, focused: None }
}

///|
fn handle_checkbox_element_pointer(
  element : ElementNode,
  data : CheckboxViewSpec,
  event : PointerEvent,
  frame : Rect,
) -> ElementEventResult {
  if event.phase == PointerPhase::Up && frame.contains(event.position) {
    data.checked.set(!data.checked.get())
    element.dirty.mark_paint()
    { changed: true, activated: true, focused: None }
  } else {
    { changed: false, activated: false, focused: None }
  }
}

///|
fn handle_picker_element_pointer(
  element : ElementNode,
  data : PickerViewSpec,
  event : PointerEvent,
  frame : Rect,
) -> ElementEventResult {
  let main_button_clicked = event.phase == PointerPhase::Up &&
    frame.contains(event.position)
  if element.control.picker_expanded {
    let item_height = 36.0
    let count = data.items.length().to_double()
    let list_height = min_double(data.maxHeight, count * item_height)
    let list_frame = Rect::new(
      x=frame.origin.x,
      y=frame.origin.y + frame.size.height + 4.0,
      width=frame.size.width,
      height=list_height,
    )
    if main_button_clicked && !list_frame.contains(event.position) {
      element.control.picker_expanded = false
      element.dirty.mark_paint()
      { changed: true, activated: false, focused: None }
    } else if event.phase == PointerPhase::Up &&
      list_frame.contains(event.position) {
      let clicked_y = event.position.y - list_frame.origin.y
      let clicked_index = (clicked_y / item_height).to_int()
      if clicked_index >= 0 && clicked_index < data.items.length() {
        let clicked_item = data.items[clicked_index]
        data.selected.set(clicked_item.id)
        element.control.picker_expanded = false
        element.dirty.mark_paint()
        { changed: true, activated: true, focused: None }
      } else {
        { changed: false, activated: false, focused: None }
      }
    } else {
      { changed: false, activated: false, focused: None }
    }
  } else if main_button_clicked {
    element.control.picker_expanded = true
    element.dirty.mark_paint()
    { changed: true, activated: false, focused: None }
  } else {
    { changed: false, activated: false, focused: None }
  }
}

///|
fn handle_datepicker_element_pointer(
  element : ElementNode,
  data : DatePickerViewSpec,
  event : PointerEvent,
  frame : Rect,
) -> ElementEventResult {
  let main_button_clicked = event.phase == PointerPhase::Up &&
    frame.contains(event.position)
  if element.control.datepicker_expanded {
    let panel_width = max_double(frame.size.width, 280.0)
    let panel_height = 350.0
    let panel_frame = Rect::new(
      x=frame.origin.x,
      y=frame.origin.y + frame.size.height + 4.0,
      width=panel_width,
      height=panel_height,
    )
    if main_button_clicked && !panel_frame.contains(event.position) {
      // 点击了外部区域,收起面板
      element.control.datepicker_expanded = false
      element.dirty.mark_paint()
      { changed: true, activated: false, focused: None }
    } else if event.phase == PointerPhase::Up &&
      panel_frame.contains(event.position) {
      // 点击了面板内容,处理各种情况
      let header_height = 50.0
      let day_header_height = 36.0
      let cell_height = 36.0
      let start_y = panel_frame.origin.y + header_height + day_header_height

      let button_size = 32.0
      let left_button_frame = Rect::new(
        x=panel_frame.origin.x + 8.0,
        y=panel_frame.origin.y + (header_height - button_size) / 2.0,
        width=button_size,
        height=button_size,
      )
      let right_button_frame = Rect::new(
        x=panel_frame.origin.x + panel_frame.size.width - button_size - 8.0,
        y=panel_frame.origin.y + (header_height - button_size) / 2.0,
        width=button_size,
        height=button_size,
      )

      if left_button_frame.contains(event.position) {
        // 左导航按钮:上一个月
        let view_date = element.control.datepicker_view
        let new_view_date = if view_date.month == 1 {
          { ..view_date, year: view_date.year - 1, month: 12 }
        } else {
          { ..view_date, month: view_date.month - 1 }
        }
        element.control.datepicker_view = new_view_date
        element.dirty.mark_paint()
        { changed: true, activated: false, focused: None }
      } else if right_button_frame.contains(event.position) {
        // 右导航按钮:下一个月
        let view_date = element.control.datepicker_view
        let new_view_date = if view_date.month == 12 {
          { ..view_date, year: view_date.year + 1, month: 1 }
        } else {
          { ..view_date, month: view_date.month + 1 }
        }
        element.control.datepicker_view = new_view_date
        element.dirty.mark_paint()
        { changed: true, activated: false, focused: None }
      } else if event.position.y >= start_y {
        // 点击了日期单元格
        let year = element.control.datepicker_view.year
        let month = element.control.datepicker_view.month
        let q = 1
        let m = if month < 3 { month + 12 } else { month }
        let y = if month < 3 { year - 1 } else { year }
        let k = y % 100
        let j = y / 100
        let h = (q + 13 * (m + 1) / 5 + k + k / 4 + j / 4 + 5 * j) % 7
        let first_day_weekday = h

        let day_cell_width = panel_frame.size.width / 7.0
        let clicked_x = event.position.x - panel_frame.origin.x
        let clicked_y = event.position.y - start_y
        let col = (clicked_x / day_cell_width).to_int()
        let row = (clicked_y / cell_height).to_int()
        let total_index = row * 7 + col
        let day = total_index - first_day_weekday + 1

        let days_in_month_count = days_in_month(year, month)
        if day >= 1 && day <= days_in_month_count {
          // 有效日期,选中它
          let selected = data.selected.get()
          let new_selected = { ..selected, year, month, day }
          data.selected.set(new_selected)
          element.control.datepicker_expanded = false
          element.dirty.mark_paint()
          { changed: true, activated: true, focused: None }
        } else {
          { changed: false, activated: false, focused: None }
        }
      } else {
        { changed: false, activated: false, focused: None }
      }
    } else {
      { changed: false, activated: false, focused: None }
    }
  } else if main_button_clicked {
    // 打开日期选择器
    element.control.datepicker_expanded = true
    element.control.datepicker_view = data.selected.get()
    element.dirty.mark_paint()
    { changed: true, activated: false, focused: None }
  } else {
    { changed: false, activated: false, focused: None }
  }
}

///|
fn handle_searchbar_element_pointer(
  element : ElementNode,
  data : SearchBarViewSpec,
  event : PointerEvent,
  frame : Rect,
) -> ElementEventResult {
  // 检查是否点击了清除按钮
  let text = data.query.get()
  if text != "" {
    let clear_frame = Rect::new(
      x=frame.origin.x + frame.size.width - 26.0,
      y=frame.origin.y + (frame.size.height - 16.0) / 2.0,
      width=16.0,
      height=16.0,
    )
    if event.phase == PointerPhase::Up && clear_frame.contains(event.position) {
      data.query.set("")
      match data.on_clear {
        Some(on_clear) => on_clear()
        None => ()
      }
      element.dirty.mark_paint()
      return { changed: true, activated: true, focused: Some(element.id) }
    }
  }

  // 否则,像 TextField 一样处理
  let focused = if event.phase == PointerPhase::Down &&
    frame.contains(event.position) {
    element.control.caret = text_length(text)
    element.control.selection = None
    element.control.composition = None
    element.control.composition_cursor = None
    Some(element.id)
  } else {
    None
  }
  { changed: false, activated: false, focused }
}