--[[
---------------------------------------------------------------------------
Remote Train Control Program for Windows

© Copyright 2019 by Mark DiVecchio

	This file is part of Remote Train Control.

	Remote Train Control is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Remote Train Control is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Remote Train Control.  If not, see <http://www.gnu.org/licenses/>.

 Home Page : http://www.silogic.com/trains/RTC_Running.html
---------------------------------------------------------------------------]]
-- Semicolons are not required in Lua code

require("defines");
require("functions");
--
--[[
When using this module, all variables (including functions!) must be declared through 
a regular assignment (even assigning nil will do) in a strict scope before being used 
anywhere or assigned to inside a nested scope.
https://github.com/lua-stdlib/strict
]]
local _ENV = require 'std.strict' (_G)	-- strict.lua will detect undeclared global variables
local _;
--
--[[---------------------------------------------------------------------------------------]]
local MyEngineNo, MyTIUNo;

local title = "Run to Next Tag"
	
local TagCount = 0;
	
-- state machine states
local IDLE = 0;
local STOPATNEXTSTATION = 1;
local SM = IDLE;
local BeepOn = false;
local MyEngineNo, MyTIUNo;
--local AC, IAC;
local RunningSpeed = 20;

local TIUSerialPortConnected, TIUConnected, RFIDSerialPortConnected;
--[[---------------------------------------------------------------------------------------]]
function setup(Engine)
	-- this function is called once when the [Run Script] button is pushed
	-- Engine : Engine number selected on the RTC Main window
	print("setup()");
	MyEngineNo = Engine;	-- Engine number selected on the RTC Main window
	MyTIUNo = GetTIUNo(MyEngineNo);	-- TIU associated with MyEngineNo	
	-- make sure that RFID is connected
	TIUSerialPortConnected, TIUConnected, RFIDSerialPortConnected = CommStatus();	
	--print("TIUSerialPortConnected : " .. tostring(TIUSerialPortConnected) .. " - TIUConnected : " .. tostring(TIUConnected) .. " - RFIDSerialPortConnected : " .. tostring(RFIDSerialPortConnected));
	if (not RFIDSerialPortConnected) then
		print("RFID Port must be enabled for this script");
		return false;	-- false=setup failed
		end
		
	--[[--------------------------------------------]]
	local EngineNameList = {};	-- table to hold all of the Engine numbers and Names
	local EngineNameIndex = 1;
	local EngineNameCount = 1;

	for iEng = 1, 99 do	-- check all possible engines on one TIU
		if (Exists(iEng)) then
			local Name, PS;
			Name, PS = GetName(iEng);
			--print(iEng .. " " .. Name .. " PS" .. PS);
			EngineNameList[iEng] = iEng .. " " .. Name .. " PS" .. PS;
			if (MyEngineNo == iEng) then
				EngineNameIndex = EngineNameCount;
				--print("EngineNameIndex = " .. EngineNameIndex);
				end;
			EngineNameCount = EngineNameCount + 1;		
			end
		end
	--
	-- Ask the dispatcher for the engine number
	--
	print("Select Engine");
	local Enum = InputDropDown(title, "Select Engine", EngineNameIndex, EngineNameList);
	--print("Enum ", Enum);
	local typ = type(Enum);
	--print("Type ", typ);

	if (typ == "boolean" and Enum == false) then
		return Stop("Canceled");
		end;
			
	if (Enum ~= nil and Enum ~= "") then	-- if the result is a string, then use first number in the string, otherwise, ignore it
		-- pull first integer found in the string
		local EngNo = string.match(Enum, "%d+");
		if (EngNo ~= nil) then
			MyEngineNo = tonumber(EngNo);
			MyTIUNo = GetTIUNo(MyEngineNo);	-- TIU associated with MyEngineNo
			else
			return Stop("Invalid Engine Number");
			end;
		else
		if (typ == "nil") then
			-- nil returned only if parameters are invalid
			return Stop("Invalid Parameters to InputDropDown");
			else
			MyEngineNo = 1;
			end
		end	
	--[[--------------------------------------------]]	
	print("Setup : Engine Number = " .. MyEngineNo);	

	--
	-- Ask the Engineer for Speed
	--	
	print("Select Running Speed");
	local SpdValue = InputDropDown(title, "Select Running Speed", 4, {"5 Smph", "10 Smph", "15 Smph", "20 Smph", "25 Smph", "30 Smph", "35 Smph", "40 Smph", });
	--print("SpdValue ", SpdValue);
	typ = type(SpdValue);
	--print("Type ", typ);

	if (typ == "boolean" and SpdValue == false) then
		return Stop("Canceled");
		end;
			
	if (SpdValue ~= nil and SpdValue ~= "") then	-- if the result is a string, then use first number in the string, otherwise, ignore it
		-- pull first integer found in the string
		local Spd = string.match(SpdValue, "%d+");
		if (Spd ~= nil) then
			RunningSpeed = tonumber(Spd);
			else
			return Stop("Invalid Running Speed");
			end;
		else
		if (typ == "nil") then
			-- nil returned only if parameters are invalid
			return Stop("Invalid Parameters to InputDropDown");
			else
			--RunningSpeed is not changed
			end
		end	
		
	print("Setup : Running Speed = " .. RunningSpeed);	

	-- debug level as selected on the Debug window, values 0 (off) to 9 (maximum)
	if (Debug() > 0) then 
		print(string.format("setup(): Debug level %d", Debug())); 
		print("setup(): Engine Number " .. MyEngineNo .. " TIU Number " .. MyTIUNo);
		end
	print("MyTIUNo : " .. MyTIUNo);
	assert((MyTIUNo >= 1 and MyTIUNo <= 5),"TIU Number out of range");
	-- initialize the layout as needed
	--AC, IAC = Counts();	-- Counts() returns two values
	--print("TIU" .. MyTIUNo .. ": " .. AC .. " Active Engines - " .. IAC .. " Inactive Engines");
		-- if you used defines.lua, you can use print() in place of print()
	-- switches and accessories
	--AIUNo = 1;
	--Switch(STRAIGHT, 1, AIUNo, MyTIUNo, 3);  -- College East
	--Switch(STRAIGHT, 2, AIUNo, MyTIUNo, 5);  -- College West
	CabChat(OFF, 0, MyEngineNo, MyTIUNo);
	Sleep(5);
	--StartUp(0, MyEngineNo, MyTIUNo);
	EngineStartUp(MyEngineNo, MyTIUNo);
	Sleep(10);
	Whistle(ON, 2, MyEngineNo, MyTIUNo);
	Whistle(OFF, 4, MyEngineNo, MyTIUNo);
	print("Trip Odometer " .. DTO(MyEngineNo, MyTIUNo)); 	
	Sleep(5);
	SetSpeed(RunningSpeed, 0, MyEngineNo, MyTIUNo);
	Sleep(7);
	print(string.format("Speed %d Smph",GetSpeed(MyEngineNo, MyTIUNo)));
	local Dir = GetDirection(MyEngineNo, MyTIUNo);
	print(string.format(Dir == true and "Direction - Forward" or "Direction - Reverse"));
	-- set State Machine to first state
	SM = STOPATNEXTSTATION;
	return true;		-- false=setup failed, true=setup succeeded
	end
--[[---------------------------------------------------------------------------------------]]
function tag(Detector, Reader, EngineNo, TagLocation, CarModel, Railroad, CarNumber, TagPacket)
	-- this function is called each time a tag is detected

--[=[
	-- Line below shows a different way to quote a string using brackets
	if (EngineNo > 0) then	-- its an engine		
		-- next line shows a different way to quote a string using brackets
		printc(clGreen, string.format([[tag(%.2f) : Detector %d Reader %d Eng#%d %s %s #%s %s]],
			RunTime(), Detector, Reader, EngineNo, GetRailroadName(Railroad), GetEngineName(CarModel), CarNumber, GetTagLocation(TagLocation)));

		else	-- it's a car
		printc(clBlue, string.format([[tag(%.2f) : Detector %d Reader %d Car %s %s #%s]],
			RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));
		-- ignore non engine tags
		return true;	-- true=continue to process tags, false=stop processing tags
		end
]=]		
		
	if (EngineNo > 0) then	-- it's an engine
		if (EngineNo == MyEngineNo) then
			if (BeepOn) then Beep(ASTERISK); end;
			-- next line shows a different way to quote a string using brackets
			printc(clGreen, string.format([[tag(%.2f) : Detector %d Reader %d Eng#%d %s %s #%s %s]],
				RunTime(), Detector, Reader, EngineNo, GetRailroadName(Railroad), GetEngineName(CarModel), CarNumber, GetTagLocation(TagLocation)));
			else
			-- ignore engines that are not me
			return true;	-- true=continue to process tags, false=stop processing tags
			end;
		-- follow on to process the detected engine
		else	-- it's a car
			if (BeepOn) then Beep(EXCLAMATION); end;
			printc(clBlue, string.format([[tag(%.2f) : Detector %d Reader %d Car %s %s #%s]],
				RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));
			-- ignore non engine tags
			return true;	-- true=continue to process tags, false=stop processing tags
		end

	-- The complete packet received is in TagPacket 
	-- (some of the most used fields have already been extracted into the first parameters to tag()
	--   First 8 digits are the 4 byte Tag UID in hexidecimal
	--   Final 32 digits are the 16 bytes of Block 4 Information read from the Tag in hexidecimal
	if (Debug() > 4) then print(string.format("Packet  %s", TagPacket)); end
	TagCount = TagCount + 1;	-- count the number of tags
	if (isEngine(EngineNo) and isTagFront(TagLocation)) then
		print(string.format("Speed %d Smph",GetSpeed(EngineNo, MyTIUNo)));
		print("Trip Odometer " .. DTO(EngineNo, MyTIUNo));	
		end
--[[---------------------------------------------------------------------------------------]]
-- STATE MACHINE
--[[---------------------------------------------------------------------------------------]]	

--[[---------------------------------------------------------------------------------------]]	
	if (SM == IDLE) then	-- do nothing 
		return true;
		end
--[[---------------------------------------------------------------------------------------]]		
	if (SM == STOPATNEXTSTATION) then
		if (isEngine(EngineNo) and isTagFront(TagLocation)) then
			-- stop engine
			print("Set speed to 0 Smph")
			SetSpeed(0, 0, EngineNo, MyTIUNo);
			end	
		return true;
		end
--[[---------------------------------------------------------------------------------------]]
	return true;	-- true=continue to process tags, false=stop processing tags
	end
--[[---------------------------------------------------------------------------------------]]
function cleanup()
	-- this function is called once when the user presses the [STOP] button
	print("cleanup()");
	--ShutDown(0, MyEngineNo, MyTIUNo);
	EngineShutdown(MyEngineNo, MyTIUNo);
	print(string.format("cleanup() Complete at %.2f", RunTime()));
	--
	return true;		-- false=cleanup failed, true=cleanup succeeded
	end
--[[---------------------------------------------------------------------------------------]]
function MoveOn()
	print(string.format("Engine %d TIU %d Debug %d", MyEngineNo, MyTIUNo, Debug()));
	print("Set speed to " .. RunningSpeed .. " Smph")
	SetSpeed(RunningSpeed, 0, MyEngineNo, MyTIUNo);
	return true;
end
Function01Name, Function01Label = MoveOn, "To Next Station";
--[[---------------------------------------------------------------------------------------]]
function PlayBell()
	print("PlayBell(): Engine " .. MyEngineNo .. " TIU " .. MyTIUNo .. " Debug " .. Debug());
	Bell(ON, 0, MyEngineNo, MyTIUNo);
	Bell(OFF, 4, MyEngineNo, MyTIUNo);
	return true;
end
Function02Name, Function02Label = PlayBell, "Sound Bell";
--[[---------------------------------------------------------------------------------------]]
function ShowDTO()
	print("ShowDTO(): Engine " .. MyEngineNo .. " TIU " .. MyTIUNo .. " Debug " .. Debug());
	print("Trip Odometer " .. DTO(MyEngineNo, MyTIUNo));
	return true;
end
Function06Name, Function06Label = ShowDTO, "Trip Odometer";
--[[---------------------------------------------------------------------------------------]]
function BeepOffButton()
	print("Beep Off");
	BeepOn = false;
	return true;
end
Function14Name, Function14Label = BeepOffButton, "Beep Off";
--[[---------------------------------------------------------------------------------------]]
function BeepOnButton()
	print("Beep On");
	BeepOn = true;
	return true;
end
Function15Name, Function15Label = BeepOnButton, "Beep On";
--[[---------------------------------------------------------------------------------------]]
