@startuml

enum TrafficLightStates {
    RED
    AMBER
    GREEN
}

enum TrafficLightEvents {
    STOP
    GO
}

interface TrafficLight {
    name: String
    amberTimeout: Long
    amber: Boolean
    red: Boolean
    green: Boolean

    changeAmberTimeout(value: Long)
    setNotifyStopped(receiver: suspend () -> Unit)
    setNotifyStateChange(receiver: suspend (newState: TrafficLightStates) -> Unit)
    stopped() <<suspend>>
    switchRed(on: Boolean) <<suspend>>
    switchAmber(on: Boolean) <<suspend>>
    switchGreen(on: Boolean) <<suspend>>
    stateChanged(toState: TrafficLightStates) <<suspend>>
}

class TrafficLightFSM {
    fun start() <<suspend>>
    fun stop() <<suspend>>
}

TrafficLightFSM .left.> TrafficLightStates: <<uses>>
TrafficLightFSM ..> TrafficLightEvents: <<uses>>
TrafficLightFSM -right-> TrafficLight: context

class TrafficLightModel {
    changeAmberTimeout(value: Long)
    setNotifyStopped(receiver: suspend () -> Unit)
    setNotifyStateChange(receiver: suspend (newState: TrafficLightStates) -> Unit)
    stopped() <<suspend>>
    switchRed(on: Boolean) <<suspend>>
    switchAmber(on: Boolean) <<suspend>>
    switchGreen(on: Boolean) <<suspend>>
    stateChanged(toState: TrafficLightStates) <<suspend>>
}

TrafficLightModel -left-|> TrafficLight
TrafficLightModel *--> TrafficLightFSM


enum IntersectionStates {
    STOPPING
    WAITING
    GOING
    STOPPED
}

enum IntersectionEvents {
    SWITCH,
    STOPPED,
    STOP,
    START
}

interface TrafficIntersection {
    cycleTime: Long
    cycleWaitTime: Long
    setNotifyStateChange(receiver: suspend (newState: IntersectionStates) -> Unit)
    setNotifyStopped(receiver: suspend () -> Unit)
    addTrafficLight(name: String, trafficLight: TrafficLight)
    currentName: String
    current: TrafficLight
    listOrder: List<String>
    get(name: String): TrafficLight
    changeCycleTime(value: Long)
    changeCycleWaitTime(value: Long)
    stateChanged(toState: IntersectionStates)
    start() <<suspend>>
    stop() <<suspend>>
    next() <<suspend>>
}

class TrafficIntersectionFSM {
    currentState: IntersectionStates
    allowedEvents(): Set<IntersectionEvents>
    startIntersection() <<suspend>>
    stopIntersection() <<suspend>>
    switchIntersection() <<suspend>>
    stopped() <<suspend>>
}

TrafficIntersectionFSM --> TrafficIntersection: context
TrafficIntersectionFSM .right.> IntersectionEvents: events <<uses>>
TrafficIntersectionFSM .up.> IntersectionStates: states <<uses>>

class TrafficIntersectionImplementation {
    amberTimeout: Long
    cycleTime: Long
    cycleWaitTime: Long
    currentState: IntersectionStates
    currentName: String
    current: TrafficLight
    listOrder: List<String>
    fun changeAmberTimeout(value: Long)
    fun get(name: String): TrafficLight
    addTrafficLight(name: String, trafficLight: TrafficLight)
    setNotifyStopped(receiver: suspend () -> Unit)
    setNotifyStateChange(receiver: suspend (newState: IntersectionStates) -> Unit)
    stateChanged(toState: IntersectionStates) <<fsm>>
    changeCycleTime(value: Long)
    changeCycleWaitTime(value: Long)

    allowedEvents(): Set<IntersectionEvents>
    startSystem() <<suspend>>
    stopSystem() <<suspend>>
    switch() <<suspend>>
    stopped() <<suspend>>

    start() <<suspend, fsm>>
    stop() <<suspend, fsm>>
    next() <<suspend, fsm>>
}

TrafficIntersectionImplementation --|> TrafficIntersection
TrafficIntersectionImplementation *-right-> TrafficIntersectionFSM: intersectionFSM <<private>>
TrafficIntersectionImplementation -left-> TrafficLightFSM: stateMachines <<creates,private>>
TrafficIntersectionImplementation -up-> TrafficLight: trafficLights <<private>>


class TrafficIntersectionModel {
    get(name: String): TrafficLightModel
}

TrafficIntersectionModel .left.> TrafficLightModel: <<creates>>
TrafficIntersectionModel -down-|> TrafficIntersectionImplementation

class TrafficIntersectionViewModel {
    val amberTimeout: Long
    val cycleWaitTime: Long
    val cycleTime: Long
    val currentName: String
    val trafficLights: List<TrafficLightViewModel>

    val intersectionState: LiveData<IntersectionStates>
    val allowStart: LiveData<Boolean>
    val allowSwitch: LiveData<Boolean>
    val allowStop: LiveData<Boolean>
    val trafficLightData: List<LiveData<TrafficLightViewModel>>

    startSystem() <<suspend>>
    stopSystem() <<suspend>>
    switch() <<suspend>>
}

TrafficIntersectionViewModel --> TrafficIntersectionModel
TrafficIntersectionViewModel -right-|> ViewModel

@enduml