// Copyright 2026 Ling71671
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Standalone breadcrumb navigation widget supporting fluent configuration.
pub struct Breadcrumb {
  id_salt : String
  items : Array[String]
  separator : String
  max_visible : Int
} derive(Eq, Debug)

///|
/// Create a new breadcrumb widget with item list.
pub fn Breadcrumb::new(id_salt : String, items : Array[String]) -> Breadcrumb {
  { id_salt, items, separator: "/", max_visible: 4, }
}

///|
/// Configure custom separator delimiter string.
pub fn Breadcrumb::separator(
  self : Breadcrumb,
  separator : String,
) -> Breadcrumb {
  { ..self, separator, }
}

///|
/// Configure maximum visible hierarchy items before middle folding with ellipsis.
pub fn Breadcrumb::max_visible(
  self : Breadcrumb,
  max_visible : Int,
) -> Breadcrumb {
  { ..self, max_visible, }
}

///|
/// Show the breadcrumb strip and return (clicked_index?, response).
pub fn Breadcrumb::show(self : Breadcrumb, ctx : UIContext) -> (Int?, Response) {
  breadcrumb(
    ctx,
    self.id_salt,
    self.items,
    separator=self.separator,
    max_visible=self.max_visible,
  )
}

///|
/// Render a horizontal breadcrumb navigation strip with ancestor hover highlights,
/// smart ellipsis folding for long hierarchies, and node click detection.
pub fn breadcrumb(
  ctx : UIContext,
  id_salt : String,
  items : Array[String],
  separator? : String = "/",
  max_visible? : Int = 4,
) -> (Int?, Response) {
  let count = items.length()
  if count == 0 {
    let (_id, _rect, resp) = ctx.allocate_space(@math.Vec2::zero())
    return (None, resp)
  }

  let scale = ctx.style.scale
  let font_sz = ctx.style.font_normal * scale
  let sep_sz = ctx.measure_text(separator, font_sz)
  let sep_gap = 6.0 * scale
  let pad_x = ctx.style.spacing_sm * scale

  let display_items : Array[(String, Int)] = []
  let should_fold = max_visible > 2 && count > max_visible

  if should_fold {
    display_items.push((items[0], 0))
    display_items.push(("...", -1))
    let tail_start = count - (max_visible - 2)
    for i = tail_start; i < count; i = i + 1 {
      display_items.push((items[i], i))
    }
  } else {
    for i = 0; i < count; i = i + 1 {
      display_items.push((items[i], i))
    }
  }

  let mut total_w = 0.0
  let num_display = display_items.length()
  for i = 0; i < num_display; i = i + 1 {
    let item_sz = ctx.measure_text(display_items[i].0, font_sz)
    total_w = total_w + item_sz.x + pad_x
    if i < num_display - 1 {
      total_w = total_w + sep_sz.x + sep_gap * 2.0
    }
  }

  let h = (ctx.style.control_h_sm + ctx.style.radius_xs) * scale
  ctx.push_id(id_salt)
  let (id, rect, resp) = ctx.allocate_space(@math.Vec2::new(total_w, h))
  ctx.pop_id()

  // Keyboard support: the whole breadcrumb is one Tab stop; Left / Right walk
  // the selectable ancestors and Enter follows the highlighted one. The
  // highlighted position persists in the scoped memory store.
  ctx.register_focusable(id)
  let is_focused = resp.has_focus() || ctx.has_focus(id)
  let mut clicked_idx : Int? = None
  let selectable : Array[Int] = []
  for i = 0; i < num_display; i = i + 1 {
    let (_, orig_idx) = display_items[i]
    if i != num_display - 1 && orig_idx >= 0 {
      selectable.push(i)
    }
  }
  let mut kb_pos : Int? = None
  if is_focused && selectable.length() > 0 {
    let mut cur = ctx.memory().get_int_or_default(id, 0, tag="kb")
    if cur >= selectable.length() {
      cur = selectable.length() - 1
    }
    if ctx.input.key_pressed(Key::ArrowRight) {
      cur = if cur + 1 < selectable.length() { cur + 1 } else { cur }
      ctx.input.consume_key(Key::ArrowRight)
    }
    if ctx.input.key_pressed(Key::ArrowLeft) {
      cur = if cur - 1 >= 0 { cur - 1 } else { cur }
      ctx.input.consume_key(Key::ArrowLeft)
    }
    if ctx.input.key_pressed(Key::Home) {
      cur = 0
      ctx.input.consume_key(Key::Home)
    }
    if ctx.input.key_pressed(Key::End) {
      cur = selectable.length() - 1
      ctx.input.consume_key(Key::End)
    }
    ctx.memory().set_int(id, cur, tag="kb")
    kb_pos = Some(selectable[cur])
    if ctx.input.key_pressed(Key::Enter) || ctx.input.key_pressed(Key::Space) {
      ctx.input.consume_key(Key::Enter)
      ctx.input.consume_key(Key::Space)
      clicked_idx = Some(display_items[selectable[cur]].1)
    }
  }

  let theme = ctx.theme
  let mut current_x = rect.x

  for i = 0; i < num_display; i = i + 1 {
    let (label, orig_idx) = display_items[i]
    let item_sz = ctx.measure_text(label, font_sz)
    let is_last = i == num_display - 1
    let item_w = item_sz.x + pad_x
    let item_rect = @math.Rect::new(current_x, rect.y, item_w, h)

    let is_hovered = ctx.is_hovered(item_rect)
    let is_kb = kb_pos == Some(i)
    let painter = ctx.painter()

    if !is_last && orig_idx >= 0 {
      if is_hovered || is_kb {
        if is_hovered {
          ctx.set_cursor_icon("pointer")
        }
        painter.add_rect(item_rect, theme.bg_hover, ctx.style.radius_sm * scale)
        if is_hovered && ctx.input.mouse_pressed {
          clicked_idx = Some(orig_idx)
        }
      }
    }

    let text_col = if is_last {
      theme.text_strong
    } else if (is_hovered || is_kb) && orig_idx >= 0 {
      theme.text_primary
    } else {
      theme.text_secondary
    }

    let text_pos = @math.Vec2::new(
      current_x + 4.0 * scale,
      rect.y + (h - font_sz) * 0.5,
    )
    painter.add_text(text_pos, label, font_sz, text_col)
    current_x = current_x + item_w

    if !is_last {
      let sep_x = current_x + sep_gap
      let sep_pos = @math.Vec2::new(sep_x, rect.y + (h - font_sz) * 0.5)
      painter.add_text(sep_pos, separator, font_sz, theme.text_disabled)
      current_x = sep_x + sep_sz.x + sep_gap
    }
  }

  // Focus ring so Tab users can see the trail is keyboard-navigable
  if is_focused {
    let radius = ctx.style.radius_sm * scale
    ctx
    .painter()
    .add_rect_stroke(
      rect.expand(2.0 * scale),
      theme.border_focus,
      1.5 * scale,
      radius + 2.0 * scale,
    )
  }

  (clicked_idx, resp)
}