local al = advtrains.lines
local rwt = assert(advtrains.lines.rwt)

-- Get current translator
local S = advtrains.lines.translate

local MODE_NORMAL = 0 -- normální zastávka (výchozí nebo mezilehlá)
local MODE_REQUEST_STOP = 1 -- zastávka na znamení (mezilehlá)
local MODE_HIDDEN = 2 -- skrytá zastávka (výchozí nebo mezilehlá)
local MODE_DISABLED = 3 -- vypnutá zastávka (mezilehlá nebo koncová)
local MODE_FINAL = 4 -- koncová zastávka (linka zde jízdu končí)
local MODE_FINAL_HIDDEN = 5 -- koncová zastávka skrytá
local MODE_FINAL_CONTINUE = 6 -- koncová zastávka (vlak pokračuje jako jiná linka)

local simple_modes = {
    [MODE_NORMAL] = MODE_NORMAL,
    [MODE_REQUEST_STOP] = MODE_NORMAL,
    [MODE_HIDDEN] = MODE_NORMAL,
    [MODE_DISABLED] = MODE_DISABLED,
    [MODE_FINAL] = MODE_FINAL,
    [MODE_FINAL_HIDDEN] = MODE_FINAL,
    [MODE_FINAL_CONTINUE] = MODE_FINAL,
}

local current_passages = {--[[
    [train_id] = {
        [1] = rwtime,
        ...,
        [n] = rwtime (časy *odjezdu*, kromě koncových zastávek, kde jde o čas příjezdu)
        wait = int or nil (původně naplánovaná doba čekání na výchozí zastávce)
    }
]]}

local last_passages = {--[[
    [linevar] = {
        [1..10] = {[1] = rwtime, ..., wait} -- jízdy seřazeny od nejstarší (1) po nejnovější (až 10) podle odjezdu z výchozí zastávky
    }
]]}

local diakritika_na_velka = {
	["á"] = "Á", ["ä"] = "Ä", ["č"] = "Č", ["ď"] = "Ď", ["é"] = "É", ["ě"] = "Ě", ["í"] = "Í", ["ĺ"] = "Ĺ", ["ľ"] = "Ľ",
	["ň"] = "Ň", ["ó"] = "Ó", ["ô"] = "Ô", ["ŕ"] = "Ŕ", ["ř"] = "Ř", ["š"] = "Š", ["ť"] = "Ť", ["ú"] = "Ú", ["ů"] = "Ů",
	["ý"] = "Ý", ["ž"] = "Ž",
}

local debug_print_i = 0

-- LOCAL funkce:
-- =========================================================================
local function debug_print(s)
    debug_print_i = debug_print_i + 1
    -- core.chat_send_all("["..debug_print_i.."] "..tostring(s))
    return s
end

-- Je-li stn kód stanice, vrací její jméno, jinak vrací "???". stn může být i nil.
local function get_station_name(stn)
    local station = advtrains.lines.stations[stn or ""]
    if station ~= nil and station.name ~= nil then
        return station.name
    else
        return "???"
    end
end
al.get_station_name = get_station_name

local function na_velka_pismena(s)
	local l = #s
	local i = 1
	local res = ""
	local c
	while i <= l do
		c = diakritika_na_velka[s:sub(i, i + 1)]
		if c then
			res = res .. c
			i = i + 2
		else
			res = res .. s:sub(i, i)
			i = i + 1
		end
	end
	return string.upper(res)
end

--[[
    -- Vrací index následujícího výskytu 'stn' v seznamu zastávek podle linevar_def.
    -- Vrací i skryté zastávky, ale ne vypnuté.
    -- Je-li linevar_def == nil nebo stn == nil nebo stn == "", vrací nil.
    - line_status = table
    - linevar_def = table or nil
    - stn = string
    return: int or nil
]]
local function find_next_index(line_status, linevar_def, stn)
    assert(line_status)
    if linevar_def ~= nil and stn ~= nil and stn ~= "" then
        local stops = linevar_def.stops
        if line_status.linevar_index < #stops then
            for i = line_status.linevar_index + 1, #stops do
                local stop = stops[i]
                if stop.stn == stn and (stop.mode or MODE_NORMAL) ~= MODE_DISABLED then
                    return i
                end
            end
        end
    end
end

-- Vrací kódy výchozí a cílové stanice k zobrazení cestujícím: stn_first (string), stn_terminus (string)
-- Předané linevar_def musí obsahovat platný seznam 'stops', ale nemusí obsahovat nic jiného (nemusí to být platná definice).
-- V případě chyby vrací nil, nil.
local function get_first_last_stations(linevar_def)
    local a, b = linevar_def.index_vychozi, linevar_def.index_cil
    if a ~= nil and b ~= nil then
        local stops = linevar_def.stops
        return stops[a].stn, stops[b].stn
    else
        return nil, nil
    end
end

--[[
    Používá se na *nelinkový* vlak stojící na *neanonymní* zastávce.
    Zahájí jízdu na lince nalezené podle kombinace line/stn/rc, nebo vrátí false.
    chg orwell: added linevar parameter, linevar no longer derived from the line/rc/stn
]]
local function line_start(train, stn, departure_rwtime, linevar)
	if not linevar then
		return false
	end
    assert(train)
    assert(stn and stn ~= "")
    assert(departure_rwtime)
    local ls = al.get_line_status(train)
    if ls.linevar ~= nil then
        -- chg orwell: no longer error here, instead cancel previous linevar
        al.cancel_linevar(train)
    end
    local linevar_def = al.try_get_linevar_def(linevar, stn)
    if linevar == nil or linevar_def == nil or linevar_def.disabled then
        return false
    end
    ls.linevar = linevar
    ls.linevar_station = stn
    ls.linevar_index = 1
    ls.linevar_dep = departure_rwtime
    ls.linevar_last_dep = departure_rwtime
    ls.linevar_last_stn = stn
    ls.linevar_past = nil
    train.text_outside = al.get_line_description(linevar_def, {
        line_number = true,
        last_stop = true,
        last_stop_prefix = "",
        last_stop_uppercase = true,
        train_name = true,
    })
    return true
end

--[[
    Vrací:
    - "true", pokud má vlak zastavit
    - nil, pokud zastavit nemá
    - "on_request", pokud má zastavit na znamení, ale znamení zatím nebylo dáno
]]
local function should_stop(pos, stdata, train)
    if stdata == nil or stdata.stn == nil then
        return nil -- neplatná data
    end
    local stn = assert(stdata.stn) -- zastávka stále může být anonymní
	local ls, linevar_def = al.get_line_status(train)
    local next_index
    if stn ~= "" then
        next_index = find_next_index(ls, linevar_def, stn)
    end
    -- jde o linkový vlak?
    if linevar_def ~= nil then
        if next_index == nil then
            return nil
        end
        local stop = assert(linevar_def.stops[next_index])
        if stop.pos ~= nil and stop.pos ~= string.format("%d,%d,%d", pos.x, pos.y, pos.z) then
            return nil
        end
        if stop.mode ~= nil and stop.mode == MODE_REQUEST_STOP then
            if ls.stop_request ~= nil then
                debug_print("Vlak "..train.id.." zastaví na zastávce na znamení.")
                return "true"
            else
                debug_print("Vlak "..train.id.." možná zastaví na zastávce na znamení.")
                return "on_request"
            end
        end
        return "true"
        -- TODO: zastávky na znamení
    else
		-- train not on a timetable
        local ars = stdata.ars
        -- vyhovuje vlak ARS pravidlům?
        local mspec,mdef = advtrains.interlocking.ars_check_rule_match(ars, train)
        local result = (mspec or mdef) and true
        if result then
            return "true"
        else
            return nil
        end
    end
end

local function update_value(train, key, setting)
    if setting == "-" then
        train[key] = nil
    elseif setting ~= "" then
        train[key] = setting
    end
    return train[key]
end

--[[
    Pokud vlak jede na lince, odebere ho z této linky a vrátí true; jinak vrátí false.
]]
function al.cancel_linevar(train)
	local ls = train.line_status
    if ls == nil or ls.linevar == nil then return false end
    ls.linevar = nil
    ls.linevar_station = nil
    ls.linevar_index = nil
    ls.linevar_dep = nil
    ls.linevar_last_dep = nil
    ls.linevar_last_stn = nil
    train.text_outside = ""
    return true
end

-- Vrací seznam názvů neskrytých zastávek, počínaje od uvedeného indexu.
-- Pokud by vrátila prázdný seznam, vrátí místo něj {"???"}.
function al.get_stop_names(linevar_def, start_index)
    local stations = advtrains.lines.stations
    local result = {}
    if linevar_def ~= nil then
        local stops = linevar_def.stops
        if start_index == nil then
            start_index = 1
        end
        for i = start_index, #stops do
            local stop = stops[i]
            if stop.mode == nil or (stop.mode ~= MODE_HIDDEN and stop.mode ~= MODE_FINAL_HIDDEN and stop.mode ~= MODE_DISABLED) then
                local station = stations[stop.stn]
                if station ~= nil and station.name ~= nil and station.name ~= "" then
                    table.insert(result, station.name)
                else
                    table.insert(result, "???")
                end
            end
        end
    end
    if result[1] == nil then
        result[1] = "???"
    end
    return result
end

--[[
    Vrací:
    a) index, stop_data -- pokud byla vyhovující předchozí zastávka nalezena
    b) nil, nil -- pokud nalezena nebyla
]]
function al.get_first_stop(linevar_def, allow_hidden_stops)
    if allow_hidden_stops then
        return 1, linevar_def.stops[1]
    else
        local result = linevar_def.index_vychozi
        if result ~= nil then
            return result, linevar_def.stops[result]
        else
            return nil, nil
        end
    end
end

--[[
    Vrací:
    a) index, stop_data -- pokud byla vyhovující předchozí zastávka nalezena
    b) nil, nil -- pokud nalezena nebyla
]]
function al.get_last_stop(linevar_def, allow_hidden_stops)
    if allow_hidden_stops then
        local stops = linevar_def.stops[result]
        for i = linevar_def.index_cil, #stops do
            local stop = stops[i]
            local mode = stop.mode
            if mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or mode == MODE_FINAL_HIDDEN then
                return i, stop
            end
        end
    else
        local result = linevar_def.index_cil
        if result ~= nil then
            return result, linevar_def.stops[result]
        end
    end
    return nil, nil
end

--[[
    Vrací:
    a) index, stop_data -- pokud byla vyhovující předchozí zastávka nalezena
    b) nil, nil -- pokud nalezena nebyla
]]
function al.get_prev_stop(linevar_def, current_index, allow_hidden_stops)
    local stops = assert(linevar_def.stops)
    assert(current_index)
    if current_index > 1 then
        for i = current_index - 1, 1, -1 do
            local mode = stops[i].mode
            if mode == nil or (mode ~= MODE_DISABLED and ((mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN) or allow_hidden_stops)) then
                return i, stops[i]
            end
        end
    end
    return nil, nil
end

--[[
    Vrací:
    a) index, stop_data -- pokud byla vyhovující další zastávka nalezena
    b) nil, nil -- pokud nalezena nebyla
]]
function al.get_next_stop(linevar_def, current_index, allow_hidden_stops)
    local stops = assert(linevar_def.stops)
    assert(current_index)
    if current_index < #stops then
        for i = current_index + 1, #stops do
            local mode = stops[i].mode
            if mode == nil or (mode ~= MODE_DISABLED and ((mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN) or allow_hidden_stops)) then
                return i, stops[i]
            end
        end
    end
    return nil, nil
end

--[[
    Vrací:
    a) index, stop_data -- pokud byla vyhovující koncová zastávka nalezena
    b) nil, nil -- pokud nalezena nebyla
]]
function al.get_terminus(linevar_def, current_index, allow_hidden_stops)
    if linevar_def.index_cil ~= nil then
        return linevar_def.index_cil, linevar_def.stops[linevar_def.index_cil]
    end
    local stops = assert(linevar_def.stops)
    local r_i, r_stop
    if current_index < #stops then
        for i = current_index + 1, #stops do
            local mode = stops[i].mode or MODE_NORMAL
            if mode ~= MODE_DISABLED and ((mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN) or allow_hidden_stops) then
                r_i, r_stop = i, stops[i]
            end
            if mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or mode == MODE_FINAL_HIDDEN then
                break
            end
        end
    end
    return r_i, r_stop
end

--[[
    options = {
        line_number = bool or nil, -- zahrnout do popisu číslo linky? nil => false
        first_stop = bool or nil, -- zahrnout do popisu název výchozí zastávky? nil => false
        last_stop = bool or nil, -- zahrnout do popisu název cílové zastávky? nil => true
        last_stop_prefix = string or nil, -- text před název cílové zastávky; nil => "⇒ "
        last_stop_uppercase = bool or nil, -- je-li true, název cílové zastávky se před uvedením převede na velká písmena
        train_name = bool or nil, -- zahrnout do popisu jméno vlaku, je-li k dispozici; nil => false
        train_name_prefix = string or nil, -- text před jméno vlaku; nil => "\n"
    }
]]
function al.get_line_description(linevar_def, options)
    local line, stn = al.linevar_decompose(linevar_def.name)
    local first_stn, last_stn = get_first_last_stations(linevar_def)
    if line == nil or first_stn == nil or last_stn == nil then
        return "???"
    end
    if options == nil then options = {} end
    local s1, s2, s3, s4
    if options.line_number then
        s1 = "["..line.."] "
    else
        s1 = ""
    end
    if first_stn == last_stn and options.first_stop and (options.last_stop == nil or options.last_stop) then
        s2 = get_station_name(last_stn)
        if options.last_stop_uppercase then
            s2 = na_velka_pismena(s2)
        end
        s3 = " (okružní)"
    else
        if options.first_stop then
            s2 = get_station_name(first_stn).." "
        else
            s2 = ""
        end
        if options.last_stop == nil or options.last_stop then
            s3 = get_station_name(last_stn)
            if options.last_stop_uppercase then
                s3 = na_velka_pismena(s3)
            end
            s3 = (options.last_stop_prefix or "⇒ ")..s3
        else
            s3 = ""
        end
    end
    if options.train_name and linevar_def.train_name ~= nil then
        s4 = (options.train_name_prefix or "\n")..linevar_def.train_name
    else
        s4 = ""
    end
    return s1..s2..s3..s4
end

function al.get_stop_description(stop_data, next_stop_data)
    local s1, s2 = "", ""
    if stop_data ~= nil then
        local mode = stop_data.mode
        if mode ~= MODE_DISABLED and mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN then
            s1 = get_station_name(stop_data.stn)
            --[[
            if
                mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or
                (next_stop_data ~= nil and next_stop_data.mode == MODE_FINAL_HIDDEN)
            then
                s2 = "Koncová zastávka"
            end
            ]]
        end
    end
    if next_stop_data ~= nil then
        local mode = next_stop_data.mode
        if mode ~= MODE_DISABLED and mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN then
            s2 = "Příští zastávka/stanice: "..get_station_name(next_stop_data.stn)
            if mode == MODE_REQUEST_STOP then
                s2 = s2.." (na znamení)"
            end
        end
    else
        s2 = "Koncová zastávka"
    end
    if s1 ~= "" and s2 ~= "" then
        return s1.."\n"..s2
    else
        return s1..s2
    end
end

--[[
    Není-li zpoždění k dispozici, vrací {text = ""}, jinak vrací:
    {
        has_delay = bool,
        delay = int,
        text = string,
    }
]]
function al.get_delay_description(line_status, linevar_def, rwtime)
    assert(line_status)
    if linevar_def == nil then
        return {text = ""}
    end
    local stops = linevar_def.stops
    local expected_departure = line_status.linevar_dep + assert(stops[line_status.linevar_index]).dep
    local real_departure = line_status.linevar_last_dep
    local delay = real_departure - expected_departure

    if rwtime ~= nil then
        local expected_departure_next
        for i = line_status.linevar_index + 1, #stops do
            if stops[i] == nil then
                break
            else
                local mode = stops[i].mode
                if mode == nil or mode ~= MODE_DISABLED then
                    expected_departure_next = line_status.linevar_dep + stops[i].dep
                    break
                end
            end
        end
        if expected_departure_next ~= nil then
            local delay2 = rwtime - expected_departure_next
            if delay2 > delay then
                delay = delay2
            end
        end
    end

    if delay < -1 or delay > 10 then
        return {
            has_delay = true,
            delay = delay,
            text = "late "..delay.." seconds",
        }
    else
        return {
            has_delay = false,
            delay = delay,
            text = "on time",
        }
    end
end

-- Test na módy MODE_FINAL*
function al.is_final_mode(stop_mode)
    return stop_mode ~= nil and simple_modes[stop_mode] == MODE_FINAL
end
local is_final_mode = al.is_final_mode

-- Test na skrytou zastávku. Vrací true, pokud zadaný mód neodpovídá skryté zastávce.
function al.is_visible_mode(stop_mode)
	return stop_mode == nil or (stop_mode ~= MODE_HIDDEN and stop_mode ~= MODE_FINAL_HIDDEN)
end
local is_visible_mode = al.is_visible_mode

-- Pokud zadaná varianta linky existuje, vrátí:
--      linevar_def, linevar_station
-- Jinak vrací:
--      nil, nil
-- Je-li linevar_station == nil, doplní se z linevar. Je-li linevar == nil, vrátí nil, nil.
function al.try_get_linevar_def(linevar, linevar_station)
	if linevar == nil then
        return nil, nil
    end
    if linevar_station == nil then
        local line
        line, linevar_station = al.linevar_decompose(linevar)
		if line == nil then
            return nil, nil
        end
    end
    local t = advtrains.lines.stations[linevar_station]
    if t ~= nil then
        t = t.linevars
		if t ~= nil then
            return t[linevar], linevar_station
        end
    end
	return nil, nil
end

--[[
    Vrací 2 hodnoty:
    - line_status = table -- tabulka train.line_status (může být prázdná)
    - linevar_def = table or nil -- jde-li o linkový vlak, vrací definici linevar, jinak nil
    Parametry:
    - train = table (train)
]]
function al.get_line_status(train)
    assert(train)
    if train.line_status == nil then
        train.line_status = {}
        return train.line_status, nil
    end
    local ls = train.line_status
    if ls.linevar == nil then
        -- nelinkový vlak
        local linevar_past = ls.linevar_past
        if linevar_past ~= nil then
            local rwtime = rwt.to_secs(rwt.get_time())
            if train.line ~= linevar_past.line or rwtime - linevar_past.arrival >= 86400 then
                -- smazat linevar_past, pokud se změnilo označení linky nebo uplynulo 24 hodin
                ls.linevar_past = nil
            end
        end
        return ls, nil
    end
    local rwtime = rwt.to_secs(rwt.get_time())
    if rwtime - ls.linevar_dep >= 86400 then
        core.log("warning", "Train "..train.id.." put out of linevar '"..ls.linevar.."', because it was riding more then 24 hours.")
        al.cancel_linevar(train)
        return ls, nil
    end
    local linevar_def = al.try_get_linevar_def(ls.linevar, ls.linevar_station)
    if linevar_def == nil then
        core.log("warning", "Train "..train.id.." was riding a non-existent (undefined) line '"..tostring(ls.linevar).."'!")
        al.cancel_linevar(train)
    elseif linevar_def.stops[ls.linevar_index] == nil then
        core.log("warning", "Train "..train.id.." put out of linevar '"..ls.linevar.."', because its index "..ls.linevar_index.." became invalid.")
        al.cancel_linevar(train)
        linevar_def = nil
    end
    return ls, linevar_def
end

local function check_selector_ars(train, stdata)
	local ars = stdata.selector_ars
	if not ars then
		return true
	end
	-- vyhovuje vlak ARS pravidlům?
	local mspec,mdef = advtrains.interlocking.ars_check_rule_match(ars, train)
	return (mspec or mdef) and true
end

function al.on_train_approach(pos, train_id, train, index)
    if train.path_cn[index] ~= 1 then return end -- špatný směr
    local pe = advtrains.encode_pos(pos)
    local stdata = advtrains.lines.stops[pe]
    if stdata == nil then
        return -- neplatná zastávka
    end
    if not check_selector_ars(train, stdata) then
		return -- does not consider stop pos here
    end
    if should_stop(pos, stdata, train) ~= nil then
        advtrains.lzb_add_checkpoint(train, index, 2, nil)
        if train.line_status.linevar == nil then
            -- nelinkový vlak:
            local stn = advtrains.lines.stations[stdata.stn]
            local stnname = stn and stn.name or S("Unknown Station")
            train.text_inside = S("Next Stop:") .. "\n"..stnname
        end
        if not stdata.arskeepen then
			advtrains.interlocking.ars_set_disable(train, true)
		end
    end
end

local function record_skipped_stops(train_id, linevar_def, linevar_index, next_index)
    assert(linevar_def)
    if next_index > linevar_index + 1 then
        local skipped_stops = {}
        for i = linevar_index + 1, next_index - 1 do
            local mode = linevar_def.stops[i].mode or MODE_NORMAL
            if mode ~= MODE_DISABLED then
                table.insert(skipped_stops, linevar_def.stops[i].stn)
            end
        end
        if #skipped_stops > 0 then
            core.log("warning", "Train "..train_id.." of line '"..linevar_def.name.."' skipped "..#skipped_stops.." stops: "..
                table.concat(skipped_stops, ", "))
        end
        return #skipped_stops
    else
        return 0
    end
end

function al.on_train_enter(pos, train_id, train, index)
    if train.path_cn[index] ~= 1 then return end -- špatný směr
    local pe = advtrains.encode_pos(pos)
    local stdata = advtrains.lines.stops[pe]
    if stdata == nil then
        return -- neplatná zastávka
    end
    if not check_selector_ars(train, stdata) then
		return -- does not consider stop pos here
    end
    
    local stn = stdata.stn or ""
    local rwtime = assert(rwt.to_secs(rwt.get_time()))
    local ls, linevar_def = al.get_line_status(train)
    local next_index
    if stn ~= "" then
        if linevar_def ~= nil then
            next_index = find_next_index(ls, linevar_def, stn)
        end
        ls.last_enter = {stn = stn, encpos = pe, rwtime = rwtime}
        debug_print("Vlak "..train_id.." zaznamenán: "..stn.." "..core.pos_to_string(pos).." @ "..rwtime)
    end
    local should_stop_result = should_stop(pos, stdata, train)
    if should_stop_result == nil then
        debug_print("Vlak "..train_id.." projel zastávkou "..stn)
        return
    elseif should_stop_result == "on_request" then
        -- projetí zastávky na znamení
        debug_print("Vlak "..train_id.." projel zastávkou na znamení "..stn)
        if linevar_def ~= nil and next_index ~= nil then
            local stop_def = assert(linevar_def.stops[next_index])
            if stop_def.mode == nil or stop_def.mode ~= MODE_REQUEST_STOP then
                error("Internal error: mode "..MODE_REQUEST_STOP.." expected, but the real mode is "..tostring(stop_def.mode).."!")
            end
            assert(stn ~= "")
            record_skipped_stops(train_id, linevar_def, ls.linevar_index, next_index)
            ls.linevar_index = next_index
            ls.linevar_last_dep = rwtime -- u zastávky na znamení se průjezd počítá jako odjezd
            ls.linevar_last_stn = stn
            local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, next_index)
            train.text_inside = al.get_stop_description(nil, linevar_def.stops[next_stop_index or 0])
            -- ATC command:
            local atc_command = "A1 S" ..(stdata.speed or "M")
            advtrains.atc.train_set_command(train, atc_command, true)
        end
        return
    end

    -- naplánovat čas odjezdu
    local wait = tonumber(stdata.wait) or 0
    local interval = nil
    if stdata.dep_mode=="interval" or stdata.dep_mode=="ttbegin" then -- TODO: in ttbegin case, the interval should come from tt not from rail
		interval = stdata.interval
	end
    local last_dep = stdata.last_dep -- posl. odjezd z této zastávkové koleje

    if interval ~= nil and last_dep ~= nil then
        if last_dep > rwtime then
            last_dep = rwtime
        end
        local ioffset = stdata.ioffset or 0
        local normal_dep = rwtime + wait
        local next_dep = last_dep + (interval - (last_dep + (interval - ioffset)) % interval)
        if normal_dep < next_dep then
            -- core.log("action", "[INFO] The train "..train_id.." will wait for "..(next_dep - normal_dep).." additional seconds due to interval at "..core.pos_to_string(pos)..".")
            wait = wait + (next_dep - normal_dep)
        -- else -- will wait normal time
        end
    end
    local planned_departure = rwtime + wait
    debug_print("Vlak "..train_id.." zastavil na "..stn.." a odjede za "..wait.." sekund ("..planned_departure..").")
    stdata.last_dep = planned_departure -- naplánovaný čas odjezdu
    stdata.last_wait = wait -- naplánovaná doba čekání
    ls.standing_at = pe
    if linevar_def == nil or next_index == nil or (linevar_def.stops[next_index].mode or MODE_NORMAL) ~= MODE_HIDDEN then
        -- zrušit stop_request, pokud jsme nezastavili na skryté zastávce:
        ls.stop_request = nil
    end

    local can_start_line
    local had_linevar = linevar_def ~= nil
    if linevar_def == nil then
        -- nelinkový vlak
        can_start_line = true
    elseif next_index ~= nil then
        -- linkový vlak zastavil na své řádné zastávce
        assert(stn ~= "") -- dopravna musí mít kód
        local stop_def = assert(linevar_def.stops[next_index])
        debug_print("Vlak "..train_id.." je linkový vlak ("..ls.linevar..") a zastavil na své pravidelné zastávce "..stn.." (index = "..next_index..")")
        record_skipped_stops(train_id, linevar_def, ls.linevar_index, next_index)
        local stop_smode = simple_modes[stop_def.mode or 0]
        if stop_smode == MODE_NORMAL then
            -- mezilehlá zastávka
            can_start_line = false
            ls.linevar_index = next_index
            ls.linevar_last_dep = planned_departure
            ls.linevar_last_stn = stn
            debug_print("Jde o mezilehlou zastávku.")
            local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, next_index)
            train.text_inside = al.get_stop_description(linevar_def.stops[next_index], linevar_def.stops[next_stop_index or 0])
        else
            -- koncová zastávka
            can_start_line = stop_def.mode == MODE_FINAL_CONTINUE
            core.log("action", "Train "..train_id.." arrived at the final station "..stop_def.stn.." of linevar "..ls.linevar.." after "..(rwtime - ls.linevar_dep).." seconds.")
            debug_print("Vlak "..train_id.." skončil jízdu na lince "..ls.linevar..", může pokračovat na jinou linku: "..(can_start_line and "ihned" or "na příští zastávce"))
            train.text_inside = al.get_stop_description(linevar_def.stops[next_index])
            local current_passage = current_passages[train_id]
            if current_passage ~= nil then
                current_passage[next_index] = rwtime
                current_passages[train_id] = nil
            end
            al.cancel_linevar(train)
            -- vyplnit linevar_past:
            if train.line ~= nil and train.line ~= "" then
                local past_linevar = assert(linevar_def.name)
                local past_line = al.linevar_decompose(past_linevar)
                ls.linevar_past = {
                    line = assert(past_line),
                    linevar = past_linevar,
                    station = stn,
                    arrival = rwtime,
                }
            end
        end
    else
        -- linkový vlak zastavil na neznámé zastávce (nemělo by nastat)
        core.log("warning", "Train "..train_id.." of linevar '"..ls.linevar.."' stopped at unknown station '"..stn.."' at "..core.pos_to_string(pos).."!")
        debug_print("Vlak "..train_id.." je linkový vlak ("..ls.linevar.."), ale zastavil na sobě neznámé zastávce "..stn..", což by se nemělo stát.")
        can_start_line = false
    end

    -- ATC příkaz
    local trn_door_delay = math.ceil(train.door_operation_time or 1) -- must round to whole number
    local atc_command = "B0 W O"..stdata.doors -- stop and open doors
		..(stdata.kick and "K" or "") -- potentially kick passengers out
		..(stdata.reverse and "R" or "") -- reverse if requested
		.." D"..wait.." " -- wait for the requested time (TODO: replace this by a RWT scheduler at some point!)
		.." A1 "..(stdata.waitsig and "G " or " ") -- enable ARS again and potentially wait for signal
		..(stdata.keepopen and "" or "OC") -- close doors, unless keepopen was set
		.." D"..trn_door_delay -- wait for the door delay
		.." S"..(stdata.speed or "M") -- depart with specified speed
							
    advtrains.atc.train_set_command(train, atc_command, true)

    -- začít novou linku?
    if can_start_line and stdata.dep_mode == "ttbegin" then
		-- if train may start a new line and departure mode is set to ttbegin, set the new linevar
		if line_start(train, stn, planned_departure, stdata.tt_begin_linevar) then
			debug_print("Vlak "..train_id.." zahájil jízdu na nové lince ("..ls.linevar..") ze stanice "..stn..".")
			core.log("action", "Train "..train_id.." started a route with linevar '"..ls.linevar.."' at station '"..stn.."'.")
			train.text_inside = get_station_name(stn)
			assert(ls.linevar)
			linevar_def = assert(al.try_get_linevar_def(ls.linevar))
			local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, 1)
			if next_stop_index ~= nil then
				train.text_inside = train.text_inside.."\nPříští zastávka/stanice: "..get_station_name(next_stop_data.stn)
				if next_stop_data.mode ~= nil and next_stop_data.mode == MODE_REQUEST_STOP then
					train.text_inside = train.text_inside.." (na znamení)"
				end
			end
		end
    elseif not had_linevar and ls.linevar == nil then
        -- vlak, který nebyl a stále není linkový:
        train.text_inside = get_station_name(stn)
        -- core.after(wait, function() train.text_inside = "" end)
    end
end

function al.on_train_leave(pos, train_id, train, index)
    local pe = advtrains.encode_pos(pos)
    local stdata = advtrains.lines.stops[pe]
    if stdata == nil then
        return -- neplatná zastávka
    end
    local stn = stdata.stn or ""
    local ls, linevar_def = al.get_line_status(train)
    local rwtime = assert(rwt.to_secs(rwt.get_time()))

    if stn ~= "" then
        ls.last_leave = {stn = stn, encpos = pe, rwtime = rwtime}
        debug_print("Vlak "..train_id.." zaznamenán na odjezdu: "..stn.." "..core.pos_to_string(pos).." @ "..rwtime)
    end

    if ls.standing_at == pe then
        -- vlak stál v této dopravně
        ls.standing_at = nil
        if
            linevar_def == nil or ls.linevar_index == nil or
            linevar_def.stops[ls.linevar_index] == nil or
            (linevar_def.stops[ls.linevar_index].mode or MODE_NORMAL) ~= MODE_HIDDEN
        then
            ls.stop_request = nil -- zrušit stop_request při odjezdu ze zastávky, pokud není nekoncová skrytá
        end
        if stn ~= "" then
            debug_print("Vlak "..train_id.." odjel ze zastávky "..stn)
            if ls.linevar_last_dep ~= nil and ls.linevar_last_dep > rwtime then
                debug_print("Vlak "..train_id.." předčasně odjel z dopravny "..stn.." (zbývalo "..(ls.linevar_last_dep - rwtime).." sekund)")
                ls.linevar_last_dep = rwtime -- předčasný odjezd
                if ls.linevar_index == 1 then
                    ls.linevar_dep = rwtime
                end
            end
        else
            debug_print("Vlak "..train_id.." odjel z anonymní zastávky "..core.pos_to_string(pos)..".")
        end
        train.text_inside = ""
        if linevar_def ~= nil then
            -- linkový vlak:
            debug_print("Linkový vlak "..train_id.." odjel ze zastávky s indexem "..ls.linevar_index)
            if ls.linevar_index == 1 then
                -- odjezd z výchozí zastávky:
                local new_passage = {rwtime}
                if stdata.last_wait ~= nil then
                    new_passage.wait = stdata.last_wait
                end
                current_passages[train_id] = new_passage
                local passages_by_linevar = last_passages[ls.linevar]
                if passages_by_linevar == nil then
                    passages_by_linevar = {new_passage}
                    last_passages[ls.linevar] = passages_by_linevar
                else
                    while #passages_by_linevar >= 10 do
                        table.remove(passages_by_linevar, 1)
                    end
                    table.insert(passages_by_linevar, new_passage)
                end
            else
                -- odjezd z nácestné zastávky
                local current_passage = current_passages[train_id]
                if current_passage ~= nil then
                    current_passage[ls.linevar_index] = rwtime
                end
            end
            local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, ls.linevar_index)
            if next_stop_index ~= nil then
                train.text_inside = al.get_stop_description(nil, next_stop_data)
            end
        end
    -- else
        --[[ průjezd?
        local stn = (stdata and stdata.stn) or ""
        if stn ~= "" then
            debug_print("Vlak "..train_id.." projel (odjezd) zastávkou "..stn..".")
        else
            debug_print("Vlak "..train_id.." projel (odjezd) anonymní zastávkou "..core.pos_to_string(pos)..".")
        end
        ]]
    end
end

function al.linevar_decompose(linevar)
    if linevar == nil then
        return nil, nil, nil
    end
    local parts = string.split(linevar, "/", true, 3)
    return parts[1], parts[2] or "", parts[3] or ""
end

--[[
    Vrací:
    a) pokud linevar existuje a má průjezdy:
        passages, stops:
        {{[1] = rwtime, ..., wait = int or nil}...}, {"kód", "název"}...}
    b) jinak:
        nil, nil
]]
function al.get_last_passages(linevar_def)
    local lp = last_passages[linevar_def.name]
    if linevar_def ~= nil and lp ~= nil and lp[1] ~= nil then
        local passages, stops = {}, {}
        for i, stop in ipairs(linevar_def.stops) do
            stops[i] = {stop.stn, get_station_name(stop.stn)}
        end
        for i = 1, #lp do
            passages[i] = table.copy(lp[i])
        end
        return passages, stops
    end
end

local function get_last_pos(line_status)
    assert(line_status)
    local last_enter, last_leave, standing_at = line_status.last_enter, line_status.last_leave, line_status.standing_at
    local last_pos, last_pos_type
    if last_enter ~= nil then
        last_pos, last_pos_type = last_enter, "enter"
        if standing_at ~= nil and standing_at == last_enter.encpos then
            last_pos_type = "standing"
        elseif last_leave ~= nil and last_leave.rwtime > last_enter.rwtime then
            last_pos, last_pos_type = last_leave, "leave"
        end
    elseif last_leave ~= nil then
        last_pos, last_pos_type = last_leave, "leave"
    else
        result.type = "none"
        return {type = "none"}
    end
    local result = {type = last_pos_type, last_pos = last_pos}
    if last_enter ~= nil then
        result.last_enter = last_enter
    end
    if last_leave ~= nil then
        result.last_leave = last_leave
    end
    return result
end

function al.get_last_pos_station_name(line_status)
    local result = get_last_pos(line_status)
    if result.type ~= "none" then
        return get_station_name(result.last_pos.stn)
    else
        return nil
    end
end

local function get_train_position(line_status, linevar_def, rwtime)
    if line_status ~= nil then
        local last_pos_info = get_last_pos(line_status)
        local last_pos = last_pos_info.last_pos
        if last_pos ~= nil then
            local result = "„"..get_station_name(last_pos.stn).."“"
            local delay_info = al.get_delay_description(line_status, linevar_def, rwtime)
            if last_pos_info.type ~= "standing" then
                result = result.." ("..(rwtime - last_pos.rwtime).." seconds ago)"
            end
            if delay_info.has_delay ~= nil then
                result = result.." ("..delay_info.text..")"
            end
            return result
        end
    end
    return "???"
end

--[[
local function prepare_prediction_for_dump(result)
    local x = {}
    for i, record in ipairs(result) do
        local y = table.copy(record)
        y.stdata = record.stdata.name
        if y.arr_linevar_def ~= nil then
            y.arr_linevar_def = y.arr_linevar_def.name
        end
        if y.dep_linevar_def ~= nil then
            y.dep_linevar_def = y.dep_linevar_def.name
        end
        x[i] = y
    end
    return x
end
]]

local function predict_train_continue(line, stn, rc, departure, result)
    if line == nil or line == "" then
        return
    end
    local linevar_def = al.try_get_linevar_def(line.."/"..stn.."/"..rc, stn)
    if linevar_def == nil then
        return
    end
    if #result == 0 then
        return
    end
    local stops = assert(linevar_def.stops)
    local last_record = result[#result]
    last_record.dep = departure
    last_record.dep_linevar_def = linevar_def
    last_record.dep_index = 1
    local index = 2
    while stops[index] ~= nil do
        local stop = stops[index]
        local stdata = advtrains.lines.stations[stop.stn]
        if stop.mode == MODE_FINAL or stop.mode == MODE_FINAL_CONTINUE or stop.mode == MODE_FINAL_HIDDEN then
            -- koncová zastávka
            local arr = departure + stop.dep
            local record = {
                stn = assert(stop.stn),
                track = stop.track or "",
                stdata = stdata,
                arr = arr,
                arr_linevar_def = linevar_def,
                arr_index = index,
                delay = 0,
                hidden = stop.mode == MODE_FINAL_HIDDEN,
            }
            table.insert(result, record)
            return
        elseif stop.mode ~= MODE_DISABLED then
            -- mezilehlá zastávka
            local dep = departure + stop.dep
            table.insert(result, {
                stn = assert(stop.stn),
                track = stop.track or "",
                stdata = stdata,
                arr = dep - (stop.wait or 10),
                arr_linevar_def = linevar_def,
                arr_index = index,
                dep = dep,
                dep_linevar_def = linevar_def,
                dep_index = index,
                delay = 0,
                hidden = stop.mode == MODE_HIDDEN,
            })
        end
        index = index + 1
    end
end

--[[
    Zadaný vlak musí být linkový.
    Parametry:
        line_status = table,
        linevar_def = table,
        rwtime = int,
        allow_continue = bool,
    Vrací *pole* následujících záznamů:
        {
            stn = string,
            track = string,
            delay = int,
            stdata = table or nil,
            dep = int or nil, dep_linevar_def = table or nil, dep_index = int or nil,
            arr = int or nil, arr_linevar_def = table or nil, arr_index = int or nil,
        }
]]
function al.predict_train(line_status, linevar_def, rwtime, allow_continue)
    assert(line_status)
    assert(linevar_def)
    local stops = linevar_def.stops
    local result = {}
    local index = assert(line_status.linevar_index)
    if rwtime == nil then
        rwtime = rwt.to_secs(rwt.get_time())
    end
    local delay_desc = al.get_delay_description(line_status, linevar_def, rwtime)
    local delay
    if delay_desc.has_delay then
        delay = delay_desc.delay
    else
        delay = 0
    end
    local departure = line_status.linevar_dep
    if line_status.standing_at ~= nil then
        -- vlak stojí na zastávce
        local stop = assert(stops[index])
        local stdata = advtrains.lines.stations[stop.stn]
        table.insert(result, {
            stn = line_status.linevar_last_stn,
            track = stop.track or "",
            stdata = stdata,
            dep = assert(line_status.linevar_last_dep),
            dep_linevar_def = assert(linevar_def),
            dep_index = index,
            -- arr = nil, arr_index = nil, arr_linevar_def = nil,
            delay = delay,
            hidden = stop.mode == MODE_HIDDEN
        })
    end
    index = index + 1
    while stops[index] ~= nil do
        local stop = stops[index]
        local stdata = advtrains.lines.stations[stop.stn]
        if stop.mode == MODE_FINAL or stop.mode == MODE_FINAL_CONTINUE or stop.mode == MODE_FINAL_HIDDEN then
            -- koncová zastávka
            local arr = departure + stop.dep + delay
            local record = {
                stn = assert(stop.stn),
                track = stop.track or "",
                stdata = stdata,
                arr = arr,
                arr_linevar_def = linevar_def,
                arr_index = index,
                delay = delay,
                hidden = stop.mode == MODE_FINAL_HIDDEN,
                final = true,
            }
            table.insert(result, record)
            if allow_continue and (linevar_def.continue_line or "") ~= "" and stop.mode == MODE_FINAL_CONTINUE then
                predict_train_continue(linevar_def.continue_line, stop.stn, linevar_def.continue_rc, arr + (stop.wait or 10), result)
            end
            break
        elseif stop.mode ~= MODE_DISABLED then
            -- mezilehlá zastávka
            local dep = departure + stop.dep + delay
            table.insert(result, {
                stn = assert(stop.stn),
                track = stop.track or "",
                stdata = stdata,
                arr = dep - (stop.wait or 10),
                arr_linevar_def = linevar_def,
                arr_index = index,
                dep = dep,
                dep_linevar_def = assert(linevar_def),
                dep_index = index,
                delay = delay,
                hidden = stop.mode == MODE_HIDDEN,
            })
        end
        index = index + 1
    end
    -- print("DEBUG: "..dump2({prediction = prepare_prediction_for_dump(result)}))
    return result
end

--[[
    Parametry:
        linevar_def = table, -- definice linevar, z něhož se má analýza provádět
        linevar_index = int,
        rwtime = int or nil, -- (aktuální žel. čas; nepovinné)
        trains = {train_table...} or nil, -- je-li zadáno, bude zkoumat pouze vlaky v tomto seznamu
    Vrací *pole* záznamů (stejných jako al.predict_train) vztahujících se k odjezdu
    z požadované zastávky, seřazené od nejbližšího odjezdu po nejvzdálenější.
]]
function al.predict_station_departures(linevar_def, linevar_index, rwtime, trains)
    assert(linevar_def)
    assert(linevar_index)
    local linevar = linevar_def.name
    local stop = assert(linevar_def.stops[linevar_index])
    if trains == nil then
        trains = al.get_trains_by_linevar()[linevar] or {}
    end
    if rwtime == nil then
        rwtime = rwt.to_secs(rwt.get_time())
    end
    local result = {}
    for _, train in ipairs(trains) do
        local ls, lvdef = al.get_line_status(train)
        if ls.linevar == linevar and ls.linevar_index <= linevar_index then
            local prediction = al.predict_train(ls, linevar_def, rwtime, true)
            for _, pr in ipairs(prediction) do
                if
                    pr.dep ~= nil and pr.dep_linevar_def ~= nil and pr.dep_index ~= nil and
                    pr.dep_linevar_def.name == linevar and
                    pr.dep_index == linevar_index and
                    pr.dep > rwtime
                then
                    table.insert(result, pr)
                    break
                end
            end
        end
    end
    table.sort(result, function(a, b) return a.dep < b.dep end)
    return result
end

--[[
    => {{
        linevar = string,
        indices = {int,...},
        linevar_def = linevar_def,
    }...}
]]
function al.get_linevars_by_station(stn, track, options)
    if options == nil then
        options = {}
    end
    local include_hidden_stops = options.include_hidden_stops
    local ignore_first_stop = options.ignore_first_stop
    local ignore_last_stop = options.ignore_last_stop
    local result = {}
    assert(stn)
    for lvstn, stdata in pairs(advtrains.lines.stations) do
        if stdata.linevars ~= nil then
            for linevar, linevar_def in pairs(stdata.linevars) do
                local first_stop_index = al.get_first_stop(linevar_def, include_hidden_stops)
                local last_stop_index = al.get_last_stop(linevar_def, include_hidden_stops)
                if not (ignore_first_stop or ignore_last_stop) or (first_stop_index ~= nil and last_stop_index ~= nil) then
                    local ld
                    for i, stop in ipairs(linevar_def.stops) do
                        if
                            stop.stn == stn and
                            (track == nil or tostring(stop.track) == track) and
                            (include_hidden_stops or is_visible_mode(stop.mode)) and
                            ((not ignore_first_stop) or i ~= 1) and
                            ((not ignore_last_stop) or (not is_final_mode(stop.mode)))
                        then
                            if ld == nil then
                                ld = {linevar = linevar, linevar_def = linevar_def, indices = {i}}
                                table.insert(result, ld)
                            else
                                table.insert(ld.indices, i)
                            end
                        end
                    end
                end
            end
        end
    end
    if #result > 1 then
        table.sort(result, function(a, b) return a.linevar < b.linevar end)
    end
    return result
end

--[[
    => {
        [linevar] = {train...}, -- generuje jen neprázdné seznamy
    }
]]
function al.get_trains_by_linevar()
    local result = {}
    for train_id, train in pairs(advtrains.trains) do
        local ls, linevar_def = al.get_line_status(train)
        if linevar_def ~= nil then
            local linevar = linevar_def.name
            local list = result[linevar]
            if list ~= nil then
                table.insert(list, train)
            else
                list = {train}
                result[linevar] = list
            end
        end
    end
    for linevar, list in pairs(result) do
        if list[2] ~= nil then
            table.sort(list, function(a, b) return a.id < b.id end)
        end
    end
    return result
end

local function vlaky(param, past_trains_too)
    local result = {}
    if param:match("/") then
        return result -- parametr nesmí obsahovat '/'
    end
    local train_line_prefix
    if param ~= "" then
        train_line_prefix = param.."/"
    end
    local rwtime = rwt.to_secs(rwt.get_time())
    local players_per_train = {}
    local results = {}
    for player_name, train_id in pairs(advtrains.player_to_train_mapping) do
        results[train_id] = (results[train_id] or 0) + 1
    end
    for train_id, train in pairs(advtrains.trains) do
        local ls, linevar_def = al.get_line_status(train)
        if linevar_def ~= nil then
            if train_line_prefix == nil or train_line_prefix == ls.linevar:sub(1, #train_line_prefix) then
                local direction_index, direction_stop = al.get_terminus(linevar_def, ls.linevar_index, false)
                local direction = "???"
                if direction_index ~= nil then
                    direction = get_station_name(direction_stop.stn)
                end
                local line = al.linevar_decompose(linevar_def.name)
                local s = "("..train_id..") ["..line.."] direction „"..direction.."“, location: "..
                    get_train_position(ls, linevar_def, rwtime)
                if results[train_id] ~= nil then
                    s = s.." ["..results[train_id].." passengers]"
                end
                table.insert(results, {key = linevar_def.name.."/"..ls.linevar_index, value = s})
            end
        elseif past_trains_too and ls.linevar_past ~= nil and (train_line_prefix == nil or ls.linevar_past.line == param) then
            local age = rwtime - ls.linevar_past.arrival
            local s = "("..train_id..") ["..ls.linevar_past.line.."] finished, location: "..
                get_station_name(ls.linevar_past.station).." ("..age.." seconds ago)"
            if results[train_id] ~= nil then
                s = s.." ["..results[train_id].." passengers]"
            end
            table.insert(results, {
                key = string.format("%s/~/%s/%05d", ls.linevar_past.line, ls.linevar_past.station, age),
                value = s,
            })
        end
    end
    table.sort(results, function(a, b) return a.key < b.key end)
    for i, v in ipairs(results) do
        result[i] = v.value
    end
    return result
end

-- příkaz /vlaky
local def = {
    params = "[line]",
    description = "List all trains on the given line (or all lines)",
    privs = {},
    func = function(player_name, param)
        local result = vlaky(param, false)
        if #result == 0 then
            return false, "No matching train found."
        end
        return true, "Listing "..#result.." trains:\n- "..table.concat(result, "\n- ")
    end,
}
core.register_chatcommand("list_trains", def)
def = {
    params = "[line]",
    description = "List all trains on the given line (or all lines) + trains that have recently completed the line",
    privs = {ch_registered_player = true},
    func = function(player_name, param)
        local result = vlaky(param, true)
        if #result == 0 then
            return false, "No matching train found."
        end
        return true, "Listing "..#result.." trains:\n- "..table.concat(result, "\n- ")
    end,
}
core.register_chatcommand("list_trains+", def)

def = {
    -- params = "",
    description = "(pro ladění)",
    privs = {server = true},
    func = function(player_name, param)
        local train = advtrains.trains[param]
        if train == nil then
            return false, "Vlak "..param.." nenalezen!"
        end
        local line_status, linevar_def = al.get_line_status(train)
        if linevar_def == nil then
            return false, "Vlak "..param.." není linkový!"
        end
        -- function al.predict_train(line_status, linevar_def, rwtime, allow_continue)
        local rwtime = rwt.to_secs(rwt.get_time())
        local predictions = al.predict_train(line_status, linevar_def, rwtime, true)
        local result = {"----"}
        local s
        for i, record in ipairs(predictions) do
            local arr, dep
            if record.arr ~= nil then
                arr = "("..(record.arr - rwtime)..", lv="..record.arr_linevar_def.name..", i="..record.arr_index..")"
            else
                arr = "nil"
            end
            if record.dep ~= nil then
                dep  = "("..(record.dep - rwtime)..", lv="..record.dep_linevar_def.name..", i="..record.dep_index..")"
            else
                dep = "nil"
            end
            result[i + 1] = "- "..record.stn.." ["..record.track.."] arr="..arr.." dep="..dep.." delay="..record.delay
        end
        table.insert(result, "----")
        s = table.concat(result, "\n")
        print(s)
        core.chat_send_player(player_name, s)
        return true
    end,
}
core.register_chatcommand("jřád", def)
core.register_chatcommand("jrad", def)
