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

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

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

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

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

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

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

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

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

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

///|
pub(all) enum TrackDirection {
  UpTrack
  DownTrack
  BiDirectional
} derive(Eq, Debug)

///|
pub fn TrackDirection::to_string(self : TrackDirection) -> String {
  match self {
    UpTrack => "UP"
    DownTrack => "DOWN"
    BiDirectional => "BI"
  }
}

///|
pub(all) enum TurnoutPosition {
  Normal
  Reverse
  Moving
  UnknownPosition
} derive(Eq, Debug)

///|
pub fn TurnoutPosition::to_string(self : TurnoutPosition) -> String {
  match self {
    Normal => "NORMAL"
    Reverse => "REVERSE"
    Moving => "MOVING"
    UnknownPosition => "UNKNOWN"
  }
}

///|
pub(all) enum SignalAspect {
  Red
  Yellow
  Green
  DoubleYellow
  YellowGreen
  Blue
  White
  FlashingYellow
  Dark
} derive(Eq, Debug)

///|
pub fn SignalAspect::is_proceed(self : SignalAspect) -> Bool {
  match self {
    Yellow | Green | DoubleYellow | YellowGreen | White | FlashingYellow => true
    Red | Blue | Dark => false
  }
}

///|
pub fn SignalAspect::to_string(self : SignalAspect) -> String {
  match self {
    Red => "RED"
    Yellow => "YELLOW"
    Green => "GREEN"
    DoubleYellow => "DOUBLE_YELLOW"
    YellowGreen => "YELLOW_GREEN"
    Blue => "BLUE"
    White => "WHITE"
    FlashingYellow => "FLASH_YELLOW"
    Dark => "DARK"
  }
}

///|
pub(all) enum SignalType {
  HomeSignal
  ExitSignal
  ShuntingSignal
  DistantSignal
  RouteSignal
  BlockSignal
} derive(Eq, Debug)

///|
pub fn SignalType::to_string(self : SignalType) -> String {
  match self {
    HomeSignal => "HOME"
    ExitSignal => "EXIT"
    ShuntingSignal => "SHUNTING"
    DistantSignal => "DISTANT"
    RouteSignal => "ROUTE"
    BlockSignal => "BLOCK"
  }
}

///|
pub(all) enum TrackState {
  Clear
  Occupied
  Locked
  Fault
} derive(Eq, Debug)

///|
pub fn TrackState::to_string(self : TrackState) -> String {
  match self {
    Clear => "CLEAR"
    Occupied => "OCCUPIED"
    Locked => "LOCKED"
    Fault => "FAULT"
  }
}

///|
pub(all) struct Turnout {
  id : TurnoutId
  name : String
  mut position : TurnoutPosition
  mut target_position : TurnoutPosition
  mut is_locked : Bool
  mut is_in_fault : Bool
  normal_next_node : NodeId
  reverse_next_node : NodeId
  base_node : NodeId
} derive(Debug)

///|
pub fn Turnout::new(
  id : TurnoutId,
  name : String,
  base_node : NodeId,
  normal_next_node : NodeId,
  reverse_next_node : NodeId,
) -> Turnout {
  {
    id,
    name,
    position: Normal,
    target_position: Normal,
    is_locked: false,
    is_in_fault: false,
    normal_next_node,
    reverse_next_node,
    base_node,
  }
}

///|
pub(all) struct Signal {
  id : SignalId
  name : String
  signal_type : SignalType
  mut current_aspect : SignalAspect
  mut requested_aspect : SignalAspect
  mut is_locked : Bool
  bound_node : NodeId
  direction : TrackDirection
} derive(Debug)

///|
pub fn Signal::new(
  id : SignalId,
  name : String,
  signal_type : SignalType,
  bound_node : NodeId,
  direction : TrackDirection,
) -> Signal {
  let default_aspect = match signal_type {
    ShuntingSignal => Blue
    _ => Red
  }
  {
    id,
    name,
    signal_type,
    current_aspect: default_aspect,
    requested_aspect: default_aspect,
    is_locked: false,
    bound_node,
    direction,
  }
}

///|
pub(all) struct TrackSegment {
  id : TrackId
  name : String
  length_meters : Double
  speed_limit_kmh : Double
  start_node : NodeId
  end_node : NodeId
  direction : TrackDirection
  mut state : TrackState
  mut occupying_train_id : String
} derive(Debug)

///|
pub fn TrackSegment::new(
  id : TrackId,
  name : String,
  length_meters : Double,
  speed_limit_kmh : Double,
  start_node : NodeId,
  end_node : NodeId,
  direction : TrackDirection,
) -> TrackSegment {
  {
    id,
    name,
    length_meters,
    speed_limit_kmh,
    start_node,
    end_node,
    direction,
    state: Clear,
    occupying_train_id: "",
  }
}

///|
pub(all) enum NodeType {
  TrackNode
  TurnoutBaseNode
  TurnoutNormalNode
  TurnoutReverseNode
  SignalNode
  BoundaryNode
} derive(Eq, Debug)

///|
pub(all) struct TopologyNode {
  id : NodeId
  name : String
  node_type : NodeType
  mut associated_turnout : TurnoutId?
  mut associated_signal : SignalId?
  mut associated_track : TrackId?
} derive(Debug)

///|
pub fn TopologyNode::new(
  id : NodeId,
  name : String,
  node_type : NodeType,
) -> TopologyNode {
  {
    id,
    name,
    node_type,
    associated_turnout: None,
    associated_signal: None,
    associated_track: None,
  }
}

///|
pub(all) struct TopologyEdge {
  id : EdgeId
  from_node : NodeId
  to_node : NodeId
  track_id : TrackId
  weight : Double
  direction : TrackDirection
} derive(Debug)

///|
pub fn TopologyEdge::new(
  id : EdgeId,
  from_node : NodeId,
  to_node : NodeId,
  track_id : TrackId,
  weight : Double,
  direction : TrackDirection,
) -> TopologyEdge {
  { id, from_node, to_node, track_id, weight, direction }
}