require "Debug_1";
require "Commodity_1";

Train = {
	_VERSION = "1.2";
}

local function getShortName(name)
	name = name or trainName or "";
	local best, quality = nil, 0.0;
	local function checkCandidate(candidate, value)
		if value > quality or not best then
			best = candidate;
			quality = value;
		end;
	end;
	local lastPart = "";
	-- for part in string.gmatch(name, "[%w][%w%.]*") do
	for part in string.gmatch(name, "[^%p^%s%][^%p^%s%.]*") do
		-- print("best so far: "..tostring(best)..", quality="..tostring(quality)..", new part: "..part);
		if part == "Nah" then -- komplett ignorieren, auch nicht in lastPart speichern
			part = lastPart;
		elseif part == "DB" or part == "DR" or part == "DRG" or part == "DBAG" or part == "SBB" or part == "FS" then -- Bahnverwaltung
			checkCandidate(part, 0.2);
			part = "DB"; -- zur Übertragung in lastPart
		elseif part == "BR" or part == "D" or part == "Zug" or string.match(part, ".*wagen$") then
			-- überspringe "Baureihe" und "...wagen"
		elseif lastPart == "BR" then
			checkCandidate(part, 0.9);
		elseif lastPart == "DB" then
			checkCandidate(part, 0.8);
		elseif string.match(part, "^[vV]%d%d?$") or string.match(part, "^EEP%d%d?$") then -- Version
		elseif string.match(part, "^[A-Z][A-Z][0-9]$") then -- Konstrukteur
			checkCandidate(part, 0.1);
		elseif string.match(part, "^E[pP][0-9IVX]*[abAB]?$") then -- Eisenbahnperiode
			checkCandidate(part, 0.1);
		elseif string.match(part, "^%d.*") then -- beginnt mit einer Ziffer
			if string.find(part, "%a") then -- enthält einen Buchstaben
				checkCandidate(part, 0.4);
			else
				checkCandidate(part, 0.3);
			end;
		else
			if string.find(part, "%d") then -- enthält eine Ziffer
				checkCandidate(part, 0.6);
			else
				checkCandidate(part, 0.5);
			end;
		end;
		lastPart = part;
	end;
	return best or name;
end;

local function iterateRS(train, i)
	i = i + 1;
	if i < train.n then
		local rsName = EEPGetRollingstockItemName(train.name, i);
		if not rsName or rsName == "" then
			print(string.format("Train.iterateRS({\"%s\", %s}, %s): no such rolling stock", tostring(train.name), tostring(train.n), tostring(i)));
		else
			return i, rsName;
		end;
	end;
end;

local function rollingstocks(name)
	local n = EEPGetRollingstockItemsCount(name);
	if n > 0 then
		Debug.printf("Train", "Train.rollingstocks(\"%s\"): %d rollingstock(s)", name, n);
	else
		print(string.format("Train.rollingstocks(\"%s\"): EEPGetRollingstockItemsCount() returned %s, wrong train name?", tostring(name), tostring(n)));
	end;
	return iterateRS, { name = name, n = n }, -1;
end;

local function getTrainComposition(train, maxChars)
	if type(train) ~= "string" then
		maxChars = train;
		train = trainName;
	end;
	assertParam(1, false, "string");
	assertParam(2, true, "number");
	maxChars = maxChars or 0;
	Debug.printf("Train", "Train.getTrainComposition(\"%s\", %d)", train, maxChars);
	local longList = "";
	local shortList = "";
	local summary = "";
	local counts = {};
	for _, rsName in rollingstocks(train) do
		local ok, rsType, motor = EEPRollingstockGetModelType(rsName);
		if rsType ~= 3 then
			ok, motor = EEPRollingstockGetMotor(rsName);
			rsName = getShortName(rsName);
			local shortName = string.gsub(rsName, "^(%u+.*[^%d])%d*$", "%1");
			if longList == "" then
				longList = rsName;
				shortList = shortName;
			else
				longList = longList .. ", " .. rsName;
				shortList = shortList .. ", " .. shortName;
			end;
			if motor > 0 then
				if summary == "" then
					summary = rsName;
				else
					summary = summary .. ", " .. rsName;
				end;
			else
				if counts[shortName] then
					counts[shortName] = counts[shortName] + 1;
				else
					counts[shortName] = 1;
				end;
			end;
		end;
	end;
	if string.len(longList) <= maxChars then
		return longList;
	elseif string.len(shortList) <= maxChars then
		return shortList;
	end;
	for shortName, count in pairs(counts) do
		local s = string.format("%d*%s", count, shortName);
		if summary == "" then
			summary = s;
		else
			summary = summary .. ", " .. s;
		end;
	end;
	return summary;
end;

local function setName(oldName, newName)
	if EEPSetTrainName(oldName, newName) then
		Debug.printf("Train", "Train.setName(\"%s\", \"%s\"): name changed", oldName, newName);
		return true;
	else
		print(string.format("Train.setName(\"%s\", \"%s\"): failed to change name", tostring(oldName), tostring(newName)));
		if Debug.isSet("Train") then print(debug.traceback); end;
		return false;
	end;
end;

local function switchNames(name1, name2)
	local success = EEPGetTrainSpeed(name1);
	if not success then
		print(string.format("Train.switchNames(\"%s\", \"%s\"): train1 does not exist", tostring(name1), tostring(name2)));
		if Debug.isSet("Train") then print(debug.traceback); end;
		return false;
	end;
	success = EEPGetTrainSpeed(name2);
	if not success then
		print(string.format("Train.switchNames(\"%s\", \"%s\"): train2 does not exist", tostring(name1), tostring(name2)));
		if Debug.isSet("Train") then print(debug.traceback); end;
		return false;
	end;
	local tempName = "#$_tmp_$";
	if Train.setName(name1, tempName) and Train.setName(name2, name1) and Train.setName(tempName, name2) then
		Debug.printf("Train", "Train.switchNames(\"%s\", \"%s\"): names exchanged", name1, name2);
		return true;
	else
		return false;
	end;
end;

local function getSpeed(name)
	local success, speed = EEPGetTrainSpeed(name);
	if success then
		Debug.printf("Train", "Train.getSpeed(\"%s\"): runs at %.1fkm/h", name, speed);
		return speed;
	else
		print(string.format("Train.getSpeed(\"%s\"): failed to get speed", tostring(name)));
		if Debug.isSet("Train") then print(debug.traceback); end;
		return nil;
	end;
end;

local function setSpeed(name, speed)
	if EEPSetTrainSpeed(name, speed) then
		Debug.printf("Train", "Train.setSpeed(\"%s\", %.1f): speed set", name, speed);
		return true;
	else
		print(string.format("Train.setSpeed(\"%s\", \"%s\"): failed to set speed", tostring(name), tostring(speed)));
		if Debug.isSet("Train") then print(debug.traceback); end;
		return false;
	end;
end;

local function getRoute(name)
	local success, route = EEPGetTrainRoute(name);
	if success then
		Debug.printf("Train", "Train.getRoute(\"%s\"): has route \"%s\"", name, route);
		return route;
	else
		print(string.format("Train.getRoute(\"%s\"): failed to get route", tostring(name)));
		if Debug.isSet("Train") then print(debug.traceback); end;
		return nil;
	end;
end;

local function setRoute(name, route)
	if EEPSetTrainRoute(name, route) then
		Debug.printf("Train", "Train.setRoute(\"%s\", \"%s\"): route set", name, route);
		return true;
	else
		print(string.format("Train.setRoute(\"%s\", \"%s\"): failed to set speed", tostring(name), tostring(route)));
		if Debug.isSet("Train") then print(debug.traceback); end;
		return false;
	end;
end;

local function getLength(name)
	local trainLength;
	for i, rs in Train.rollingstocks(name) do
		local result, rsLength = EEPRollingstockGetLength(rs);
		if result then
			trainLength = (trainLength or 0) + rsLength;
		else
			trainLength = nil;
			break;
		end;
	end;
	if trainLength then
		Debug.printf("Train", "Train.getLength(\"%s\") = %.1fm", name, trainLength);
	else
		print(string.format("Train.getLength(\"%s\"): failed to get length", tostring(name)));
		if Debug.isSet("Train") then print(debug.traceback); end;
	end;
	return trainLength;
end;

local function getFirstRS(name)
	local speed = getSpeed(name);
	local nrRS = speed >= 0 and 0 or EEPGetRollingstockItemsCount(name) - 1;
	local rsName = EEPGetRollingstockItemName(name, nrRS);
	local result, length;
	if rsName and rsName ~= "" then
		result, length = EEPRollingstockGetLength(rsName);
	end;
	if result then
		Debug.printf("Train", "Train.getFirstRS(\"%s\") = %.1fm (Nr. %d,\"%s\")", name, length, nrRS, rsName);
	else
		print(string.format("Train.getFirstRS(\"%s\"): failed to get length of Nr. %s,\"%s\"", tostring(name), tostring(nrRS), tostring(rsName)));
		if Debug.isSet("Train") then print(debug.traceback); end;
		rsName = nil;
	end;
	return rsName, length;
end;

local function hasMotor(name)
	for i, rs in Train.rollingstocks(name) do
		local result, motor = EEPRollingstockGetMotor(rs);
		if motor == true or motor ~= 0 then
			Debug.printf("Train", "Train.hasMotor(\"%s\"): Rollingstock %d (%s) has a motor", name, i, rs);
			return true;
		elseif not result then
			print(string.format("Train.hasMotor(\"%s\"): failed on rollingstock \"%s\"", tostring(name), tostring(rs)));
			return nil;
		end;
	end;
	Debug.printf("Train", "Train.hasMotor(\"%s\"): none of the rollingstocks has a motor", name);
	return false;
end;

	-- Funktion getShortName(name)
	-- Bedeutung:	verkürzt den übergebenen Namen auf den wesentlichen Bestandteil, im Idealfall die Gattungszeichen
	-- name:			optionaler Name des Rollmaterials (Zugname oder Leerstring, wenn nicht gegeben)
	-- Rückgabe:
	--		verkürzter Name
Train.getShortName = getShortName;

	-- Funktion getTrainComposition(train, maxChars)
	-- Bedeutung:	liefert eine kompakte Zusammenstellung des angegebenen Zuges als Zeichenkette
	-- train:			Name des Zugs (optional)
	-- maxChars:	Gewünschte Maximallänge der Zeichenkette (optional), das Ziel ist nicht immer erreichbar
	-- Rückgabe:	Zeichenkette
Train.getTrainComposition = getTrainComposition;

	-- Funktion function rollingstocks(name)
	-- Bedeutung:	liefert einen Iterator, eine Tabelle und einen Startwert zurÃ¼ck
	-- Zweck:			Einsatz in einem Konstrukt "for i, rsName in Train.rollingstocks(name) do ... end;"
	-- name:			Name des Zuges
	-- Rückgabe:
	--		Iterator für eine Tabelle mit den Feldern name (Zugname) und n (Anzahl Wagen) sowie einen Iterationswert (Index)
	--		Tabelle mit den Feldern name (Zugname) und n (Anzahl Wagen)
	--    -1 als Wert
Train.rollingstocks = rollingstocks;

	-- Funktion setName(oldName, newName)
	-- Bedeutung:	Ändert den Zugnamen von oldName auf newName
	-- oldName:		bisheriger Name des Zuges
	-- newName:		neuer Name des Zuges
	-- Rückgabe:	true bei Erfolg, sonst false
Train.setName = setName;

	-- Funktion switchNames(name1, name2)
	-- Bedeutung:	tauscht die Zugnamen von zwei Zügen
	-- name1:			Name des einen Zuges
	-- name1:			Name des anderen Zuges
	-- Rückgabe:	true bei Erfolg, sonst false
Train.switchNames = switchNames;

	-- Funktion getSpeed(name)
	-- Bedeutung:	liefert die aktuelle Geschwindigkeit des angegebenen Zuges
	-- name:			Name des Zuges
	-- Rückgabe:	Geschwindigkeit bei Erfolg, sonst nil
Train.getSpeed = getSpeed;

	-- Funktion setSpeed(name, speed)
	-- Bedeutung:	setzt die Soll-Geschwindigkeit des angegebenen Zuges
	-- name:			Name des Zuges
	-- speed:			neue Soll-Geschwindigkeit
	-- Rückgabe:	true bei Erfolg, sonst false
Train.setSpeed = setSpeed;

	-- Funktion getRoute(name)
	-- Bedeutung:	liefert die Route des angegebenen Zuges
	-- name:			Name des Zuges
	-- Rückgabe:	Route bei Erfolg, sonst nil
Train.getRoute = getRoute;

	-- Funktion setRoute(name, route)
	-- Bedeutung:	setzt die Route des angegebenen Zuges
	-- name:			Name des Zuges
	-- route:			neue Route
	-- Rückgabe:	true bei Erfolg, sonst false
Train.setRoute = setRoute;

	-- Funktion getLength(name)
	-- Bedeutung:	liefert die Länge des angegebenen Zuges zwischen den äußeren Kupplungen
	-- name:			Name des Zuges
	-- Rückgabe:	Länge bei Erfolg, sonst nil
Train.getLength = getLength;

	-- Funktion getFirstRS(name)
	-- Bedeutung:	liefert Namen und Länge des in Fahrtrichtung ersten Rollmaterials des angegebenen Zuges
	-- name:			Name des Zuges
	-- Rückgabe:
	--    Name des Rollmaterials bei Erfolg, sonst nil
	--		Länge des Rollmaterials bei Erfolg, sonst nil
Train.getFirstRS = getFirstRS;

	-- Funktion hasMotor(name)
	-- Bedeutung:	prüft, ob der angegebene Zug motorisiert ist
	-- name:			Name des Zuges
	-- Rückgabe:	true, wenn er motorisiert ist; false, wenn er nicht motorisiert; nil im Fehlerfall
Train.hasMotor = hasMotor;

print("Train Version " .. Train._VERSION);

return Train;
