///|
pub(all) struct RouteId {
  id : Int
} derive(Eq, Debug, Compare)

///|
pub fn RouteId::new(id : Int) -> RouteId {
  { id, }
}

///|
pub(all) enum RouteState {
  RouteInactive
  RouteRequested
  SwitchesThrowing
  RouteLocked
  ApproachLocked
  TrainInRoute
  SectionalUnlocking
  RouteCancelling
  RouteFault
} derive(Eq, Debug)

///|
pub fn RouteState::to_string(self : RouteState) -> String {
  match self {
    RouteInactive => "INACTIVE"
    RouteRequested => "REQUESTED"
    SwitchesThrowing => "THROWING_SWITCHES"
    RouteLocked => "LOCKED"
    ApproachLocked => "APPROACH_LOCKED"
    TrainInRoute => "TRAIN_IN_ROUTE"
    SectionalUnlocking => "UNLOCKING"
    RouteCancelling => "CANCELLING"
    RouteFault => "FAULT"
  }
}

///|
pub(all) enum RouteType {
  ReceivingRoute // 进站列车进路
  DepartureRoute // 出站列车进路
  PassingRoute // 通过列车进路
  ShuntingRoute // 调车进路
} derive(Eq, Debug)

///|
pub fn RouteType::to_string(self : RouteType) -> String {
  match self {
    ReceivingRoute => "RECEIVING"
    DepartureRoute => "DEPARTURE"
    PassingRoute => "PASSING"
    ShuntingRoute => "SHUNTING"
  }
}

///|
pub(all) struct SwitchRequirement {
  turnout_id : @topology.TurnoutId
  required_position : @topology.TurnoutPosition
} derive(Debug)

///|
pub fn SwitchRequirement::new(
  turnout_id : @topology.TurnoutId,
  required_position : @topology.TurnoutPosition,
) -> SwitchRequirement {
  { turnout_id, required_position }
}

///|
pub(all) struct FlankProtection {
  turnout_id : @topology.TurnoutId
  required_position : @topology.TurnoutPosition
  description : String
} derive(Debug)

///|
pub fn FlankProtection::new(
  turnout_id : @topology.TurnoutId,
  required_position : @topology.TurnoutPosition,
  description : String,
) -> FlankProtection {
  { turnout_id, required_position, description }
}

///|
pub(all) enum ReleaseStage {
  StageNotStarted
  StageApproachOccupied // 接近区段占用
  StageEntrySectionOccupied // 进路入口区段占用
  StageMiddleSectionOccupied // 进路中间区段占用
  StageExitSectionOccupied // 进路出口区段占用
  StageFullyReleased // 完全解锁
} derive(Eq, Debug)

///|
pub(all) struct InterlockingRule {
  id : RouteId
  name : String
  route_type : RouteType
  entry_signal : @topology.SignalId
  exit_signal : @topology.SignalId
  switches : Array[SwitchRequirement]
  flank_switches : Array[FlankProtection]
  track_sections : Array[@topology.TrackId]
  hostile_route_ids : Array[RouteId]
  target_aspect : @topology.SignalAspect
  approach_section : @topology.TrackId?
} derive(Debug)

///|
pub fn InterlockingRule::new(
  id : RouteId,
  name : String,
  route_type : RouteType,
  entry_signal : @topology.SignalId,
  exit_signal : @topology.SignalId,
  switches : Array[SwitchRequirement],
  flank_switches : Array[FlankProtection],
  track_sections : Array[@topology.TrackId],
  hostile_route_ids : Array[RouteId],
  target_aspect : @topology.SignalAspect,
  approach_section : @topology.TrackId?,
) -> InterlockingRule {
  {
    id,
    name,
    route_type,
    entry_signal,
    exit_signal,
    switches,
    flank_switches,
    track_sections,
    hostile_route_ids,
    target_aspect,
    approach_section,
  }
}

///|
pub(all) struct ActiveRouteState {
  mut rule : InterlockingRule
  mut state : RouteState
  mut release_stage : ReleaseStage
  unlocked_sections : FixedArray[Bool]
  occupied_sections : FixedArray[Bool]
  mut highest_observed_section_index : Int
  mut occupancy_order_violation : Bool
  mut observed_train_id : String
  mut entry_time_ms : Int64
} derive(Debug)

///|
pub fn ActiveRouteState::new(rule : InterlockingRule) -> ActiveRouteState {
  {
    rule,
    state: RouteInactive,
    release_stage: StageNotStarted,
    unlocked_sections: FixedArray::make(256, false),
    occupied_sections: FixedArray::make(256, false),
    highest_observed_section_index: -1,
    occupancy_order_violation: false,
    observed_train_id: "",
    entry_time_ms: 0,
  }
}

///|
fn ActiveRouteState::reset_for_rule(
  self : ActiveRouteState,
  rule : InterlockingRule,
) -> Unit {
  self.rule = rule
  self.state = RouteInactive
  self.release_stage = StageNotStarted
  self.highest_observed_section_index = -1
  self.occupancy_order_violation = false
  self.observed_train_id = ""
  self.entry_time_ms = 0
  let mut index = 0
  while index < 256 {
    self.unlocked_sections[index] = false
    self.occupied_sections[index] = false
    index = index + 1
  }
}