-- 'subway' environment --
--F.stn_union=function(line1, prev1, prev2, this, next, doors, dps, osig, ret_sw, ret_st, nohalt, waittime)
F.stationnames={
Ewb="Edenwood Beach",
Ban="Bananame",
ctr="Coulomb Street Triangle",
Cht="Churchill Street",
Bbe="Birch Bay East",
Bap="Turtle Rock",
Icm="Ice Mountain",
Eft="BHS10",
Apl="Apple Plains",
Pal="Palm Bay",
Slh="Smacker's Land of Hope and Glory",
Lks="Leekston",
Ta1="Testing Area 1",
Ta2="Testing Area 2",
Ahr="AHRAZHUL's Station",
Ahz="Large Beach",
Wim="Windy Mountains",
Dam="Szymon's Dam",
Wva="Windy Mountains Valley 1",
Wvb="Windy Mountains Valley 2",
Wvc="Windy Mountains Valley 3",
App="Apple Grove",
Dem="Desert Mountain",
Dev="Desert View (OCP)",
Lvc="Levenshtein Canyon",
Gho="Green Hope",
Snb="Snake Bend",
Adb="Adorno Boulevard",
Duf="Duff Road",
Wat="Something in the water",
Ram="Ramanujan Street",
Per="Perelman Street",
Trp="Trump Park",
Sfs="South Forest Station",
Lok="Jude Milhon Street",
Bam="Bamboo Hills",
Sfa="unnamed",
Gcl="Green Cliffs",
Dri="Dry Island",
Ged="Green Edge",
Ghb="Green Hill Beach",
Acm="Acacia Mountains",
Ghm="Greenhat Mountain",
Pna="Pence Avenue",
Dbl="Dubulti",
Sws="Schwarzschildt Street",
Mnk="Minkowsky Street",
Rgs="Robert Gardon Street",
Ehl="Ehlodex",
Lus="Lusin Street",
Lin="Lesnoi Industrial Area",
Boz="Booze Grove",
Mrh="Mirzakhani Street",
Plt="Planetarium",
Mcf="McFly Street",
Tha="Theodor Adorno Street",
Oni="Onionland",
Ora="Orange Lake",
Uaa="Eiffel Street",
Leo="Leonhard Street",
Bby="Birch Bay",
Stb="Stone Beach",
Jis="Jungle Island",
Ice="Eternal Ice",
Bnt="Pierre Berton Street",
Osa="Origin Sands (Plaza de la Republica)",
OTh2="Town Hall",
OBa="Market (Babbage Road)",
OOr="Origin",
OSc="School",
ONb="North Bridge (ARA)",
OIs="Intel ME Stairs",
OSm="SCSI Connector Mess",
OEs="Plaza de la Republica",
OIr2="Ice Rink",
ioa="Cow Bridge",
iob="Babbage Road",
Wcs="Watson-Crick Street",
Rru="Rockefeller Runway",
Ewd="Edenwood",
Chu="Marcuse Street Station",
Erd="Erdos Street",
Uni="Museum",
Mar="Felfa's Market (Bracket Road)",
Wac="Watson-Crick",
OLv="Suburbs",
Irk="Ice Rink",
Sbr="Suburb",
Unv="University",
Arc="Archangel",
Dar="Darwin Road",
Hmi="Half-Mile Island",
Zoo="Zoo",
Bea="Beach",
Yos="Yoshi Island",
Krs="Kernighan&Ritchie Street",
Rkb="Robert Koch Boulevard",
Rsi="Riverside",
Swr="Swimming Rabbit Street",
Wbb="Banana Forest",
Ori="Origin",
Snl="Snowland",
Sys="Ship Rock",
Rfo="Redwood forest",
Moj="Mom Junction",
Wfr="Wolf Rock",
Spa="Shanielle Park",
Thh="Treehouse Hotel",
Stn="Main station",
WB1="Riverside",
WB2="Banana Forest",
WB3="Eiffel Street",
WB4="Buckminster Fuller Street",
WB5="White Beaches",
Shn="Shanielle City",
Jus="Tom Lehrer Street",
Fre="Frege Street",
Min="MinerLand",
Vlc="Volcano Cliffs",
Mio="Minio",
Wpy="Water Pyramid",
Cat="Cathedral",
Dca="Desert Canyon",
Spn="Spawn",
Brn="Ministry of Transport (bernhardd)",
Kav="Knuth Avenue",
Lvf="Library",
Fms="John Horton Conway Street",
Mnt="Mountain",
Mnv="Mountain Valley",
Mnn="Mountain View",
Max="Maxwell Street",
Snp="Snowy Peak",
Scl="ScottishLion's City",
Lza="Laza's City",
Bld="BlackDog",
Hts="Hotel Shanielle",
Fmn="Euler Street",
Gpl="Market",
Jun="Jungle",
Jng="Franklin Road",
Uic="Coulomb Street",
Grs="Gram-Schmidt Street",
Lih="Lighthouse",
Rea="Reactor",
Hhs="Henderson-Hasselbalch Street",
Ack="Ackermann Avenue",
Lis="Lone Island",
Pyr="Pytagoras Road",
Nha="North Harbour",
STn="Technic Station",
SPo="Post Office",
SSw="Spawn, westbound",
SSe="Spawn, eastbound",
SPa="Papyrus Farm",
STo="Tourist Info",
SMi="Public Mine",
MR1="Euler Street",
MSt="Main Station (Spawn)",
MOr="Marcuse Street Station (Origin)",
}
--[[
Signal names:
F.stn(<previous>, <this>, <next> 
          <door side>, <Depart speed (maximum if omitted)>
Halt here and continue when signal is green.
no halt:
F.stn_nohalt=function(prev, this, next, dps, osig)
F.stn_return(<previous>, <this>, <next>, 
          <door side>, <Switch to set>, <State to set switch to (st/cr)>,
          <Depart speed (maximum if omitted)>
Halt here. Set the switch to desired state and return when signal is green. Does not free previous section!
F.stn_return_free=function(<Signal at previous station>, <Switch>, <State>)
To be called after train left the switch of a stn_return station. Sets the switch back to incoming trains and sets signal to green.
]]
--Warning: Expects line to be a string!
local linet={
["1"] = {W="Palm Bay", E="Windy Mountains"},
["2"] = {N="Szymon's Dam", S="Onionland"},
["3"] = {S="Bananame", N="Large Beach"},
["4"] = {E="Schwarzschildt Street", W="Ice Mountain"},
["5"] = {W="Lighthouse", E="Leekston"},
["7"] = {N="Birch Bay East", S="Planetarium"},
}
F.lineterm = function(line, terminal)
if linet[line] and linet[line][terminal] then return linet[line][terminal] end
return terminal
end
F.lineset = function(line, terminal)
  if event.train then
    atc_set_text_outside("Line "..line.." - "..F.lineterm(line, terminal))
    S.line[atc_id] = line
    set_line(tonumber(line))
  end
end
F.rant=function()
  return math.random(5,8)
end
F.stnname=function(cap)
return F.stationnames[string.sub(cap, 1, 3)] or "?"
end
F.stn=function(prev, this, next, doors, dps, osig)
  F.stn_union(nil, prev, nil, this, next, doors, dps, osig)
end

F.stn_return=function(prev, this, next, doors, switch, state, dps, osig, waittime)
  F.stn_union(nil, prev, nil, this, next, doors, dps, osig, switch, state, false, waittime)
end

F.stn_return_nohalt=function(prev, this, next, switch, state, dps)
  F.stn_union(nil, prev, nil, this, next, "C", dps, osig, switch, state, true)
end

F.stn_return_free=function(prev, switch, state)
  if event.train then
    setstate(prev, "green")
    setstate(switch, state)
  end
end

F.stn_nohalt=function(prev, this, next, dps, osig)
  F.stn_union(nil, prev, nil, this, next, "C", dps, osig, nil, nil, true)
end

F.union_wait=function(sect)
  S.union_waiting[sect] = not depart
end

F.stn_union=function(line1, prev1, prev2, this, next, doors, dps, osig, ret_sw, ret_st, nohalt, waittime)
  if not atc_id then
    atc_send("B0")
    error("Train has disappeared!")
  end
  if not atc_arrow then
    atc_send("B0")
    error("Train passed in wrong direction!")
  end
  depart=false
  if event.train then
    setstate(prev1, "red")
    if prev2 then  setstate(prev2, "red") end
    atc_send("B0O"..doors)

if atc_speed and atc_speed > 10 then
  local dt = os.date()
  atc_set_text_outside("BrakeFail speed="..atc_speed.." when="..dt.year.."-"..dt.month.."-"..dt.day.." "..dt.hour..":"..dt.min..":"..dt.sec)
  error("Train "..atc_id.." has passed rail at speed of "..atc_speed)
end

    if not nohalt then
       interrupt(waittime or (ret_sw and 20 or 7), "ready")
       atc_set_text_inside(F.stnname(this))
    end
  end
  if (event.int and event.message=="ready") or (event.train and nohalt) then
    if getstate(this)=="green" and (not osig or getstate(osig)=="green") then
      if ret_sw then
        atc_send("OCD1B0WRS"..(dps or "M"))
        setstate(ret_sw, ret_st)
      else
        atc_send("OCD1S"..(dps or "M"))
        setstate(prev1, "green")
        if line1 then --this call did not come from F.stn, do union stuff
            setstate(prev2, "green")
            if S.line[atc_id]==line1 then
               if S.union_waiting[prev2] then setstate(prev1, "red") end
            else
               if S.union_waiting[prev1] then setstate(prev2, "red") end
            end
        end
      end
      setstate(this, "red")
      atc_set_text_inside("Next stop: "..F.stnname(next))
      depart=true
      nodepartc=nil
    else
       interrupt(F.rant(), "ready")
       nodepartc=nodepartc and nodepartc+1 or 0
       if nodepartc>=10 then
         atc_set_text_inside(F.stnname(this).."\nLine out of order!")
       else
         if (not osig or getstate(osig)=="green") then
           atc_set_text_inside(F.stnname(this).."\nWaiting for preceding train...")
         else
           atc_set_text_inside(F.stnname(this).."\nWaiting for oncoming train...")
         end
       end
    end
  end
end
F.pre=function(signal)
   if getstate(signal) == "red" then
     atc_send("B4")
    end
end

F.uiclog = function () 
return
end
--L197

F.stat=function(line, init)
--statistics
-- init
if init then
reftrain = atc_id
a_tbt = 30
a_tbtmax = 30
a_rtt = 500
a_not = 0
c_not = 0
c_tbtmax = 0
time_lt = os.time()
time_rt=os.time()
end
if not a_tbtmax then a_tbtmax = 30 end
if not c_tbtmax then c_tbtmax = 0 end
--real code
if event.train then
local time = os.time()
c_not = c_not + 1
a_tbt = (a_tbt + (time - time_lt)) / 2
c_tbtmax = math.max(c_tbtmax, (time - time_lt))
if atc_id == reftrain then
  a_rtt = (a_rtt*0.2 + (time - time_rt)*0.8)
  a_not = c_not
  c_not = 0
  a_tbtmax = (a_tbtmax + c_tbtmax) / 2
  c_tbtmax = 0
end
  digiline_send("stats", "Stat: "..line..
    " NoT:"..a_not.."("..c_not..")"..
    " TbT:"..math.floor(a_tbt).."("..(time-time_lt)..")"..
    " Tmx:"..math.floor(a_tbtmax).."("..c_tbtmax..")"..
    " R:"..math.floor(a_rtt).."("..(time - time_rt)..")"
    )
time_lt = time
if atc_id == reftrain then
  time_rt = time
end
end
end

local function aspect_is_free(asp)
  if type(asp.main) == "table" then
    return asp.main.free
  else
    return asp.main ~= 0
  end
end

-- 21.1.19, the rise of tss
F.stn_ilk=function(prev, this, next, doors, dps)
  depart = false
  if event.train then
    atc_send("B0 W O"..doors)
    atc_set_text_inside(F.stnname(this))
    interrupt(7, "ready")
  elseif event.int then
    local asp = get_aspect(this)
    if not asp then
      atc_set_text_inside(F.stnname(this).."\nNo aspect for "..this)
    else
      if aspect_is_free(asp) then
        atc_set_text_inside("Next stop:\n"..F.stnname(next))
        atc_send("OC D1 S"..(dps or "M"))
        depart = true
        return
      else
        atc_set_text_inside(F.stnname(this).."\nSection ahead is blocked...")
      end
    end
    interrupt(7, "ready")
  end
end

F.stn_ilkentry=function(prev, this, next, doors, dps)
  F.stn_ilk(prev, this, next, doors, dps)
  if depart then setstate(prev, "green") end
end