///|
pub(all) enum FocusTraversalDirection {
  Next
  Previous
} derive(Eq, Debug, ToJson)

///|
struct FocusScopeItem {
  id : String
  enabled : Bool
  focusable : Bool
} derive(Eq)

///|
struct FocusScope {
  items : Array[FocusScopeItem]
  current : String?
  wrap : Bool
  default_action : String
  dismiss_action : String
} derive(Eq)

///|
pub fn FocusScopeItem::new(
  id~ : String,
  enabled? : Bool = true,
  focusable? : Bool = true,
) -> FocusScopeItem {
  { id, enabled, focusable }
}

///|
pub fn FocusScope::new(
  items~ : Array[FocusScopeItem],
  current? : String? = None,
  wrap? : Bool = true,
  default_action? : String = "",
  dismiss_action? : String = "",
) -> FocusScope {
  { items, current, wrap, default_action, dismiss_action }
}

///|
pub fn FocusScope::first_focusable(self : FocusScope) -> String? {
  for item in self.items {
    if item.enabled && item.focusable {
      return Some(item.id)
    }
  }
  None
}

///|
pub fn FocusScope::last_focusable(self : FocusScope) -> String? {
  let ids = self.focusable_ids()
  if ids.length() == 0 {
    None
  } else {
    Some(ids[ids.length() - 1])
  }
}

///|
pub fn FocusScope::next(self : FocusScope) -> String? {
  self.traverse(FocusTraversalDirection::Next)
}

///|
pub fn FocusScope::previous(self : FocusScope) -> String? {
  self.traverse(FocusTraversalDirection::Previous)
}

///|
pub fn FocusScope::traverse(
  self : FocusScope,
  direction : FocusTraversalDirection,
) -> String? {
  let ids = self.focusable_ids()
  if ids.length() == 0 {
    return None
  }
  let mut index = -1
  match self.current {
    Some(current) =>
      for candidate_index, id in ids {
        if id == current {
          index = candidate_index
        }
      }
    None => ()
  }
  match direction {
    Next => {
      let next = if index < 0 {
        0
      } else if index + 1 < ids.length() {
        index + 1
      } else if self.wrap {
        0
      } else {
        index
      }
      Some(ids[next])
    }
    Previous => {
      let previous = if index < 0 {
        ids.length() - 1
      } else if index > 0 {
        index - 1
      } else if self.wrap {
        ids.length() - 1
      } else {
        index
      }
      Some(ids[previous])
    }
  }
}

///|
pub fn FocusScope::focus_target_or_first(
  self : FocusScope,
  target : String?,
) -> String? {
  match target {
    Some(id) =>
      if self.contains_focusable(id) {
        Some(id)
      } else {
        self.first_focusable()
      }
    None => self.current
  }
}

///|
pub fn FocusScope::command_target(
  self : FocusScope,
  intent : CommandIntent,
) -> String? {
  match intent {
    Submit | Activate =>
      if self.default_action == "" {
        None
      } else {
        Some(self.default_action)
      }
    Cancel =>
      if self.dismiss_action == "" {
        None
      } else {
        Some(self.dismiss_action)
      }
    _ => None
  }
}

///|
pub fn focus_scope_command_intent(event : KeyboardEvent) -> CommandIntent? {
  if !event.pressed {
    None
  } else if event.key == "Enter" {
    Some(CommandIntent::Submit)
  } else if event.key == "Escape" {
    Some(CommandIntent::Cancel)
  } else {
    None
  }
}

///|
fn FocusScope::focusable_ids(self : FocusScope) -> Array[String] {
  let ids : Array[String] = []
  for item in self.items {
    if item.enabled && item.focusable {
      ids.push(item.id)
    }
  }
  ids
}

///|
fn FocusScope::contains_focusable(self : FocusScope, id : String) -> Bool {
  for item in self.items {
    if item.id == id && item.enabled && item.focusable {
      return true
    }
  }
  false
}