--[[
---------------------------------------------------------------------------
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
title = "Multithread Train Control"
--
-- First test to use train control commands in a multithreaded environment
--
-- This example is based on Roberto's book, Programming in Lua, fourth edition.
-- Chapter 26
--
-- When run under Windows 7 or 10,
-- this script requires luasocket 3.0-rc1 as
-- modified for RTC by Mark DiVecchio
-- luasec 0.8 and OpenSSL 1.0.2
--
-- When run under Windows XP,
-- this script requires luasocket 2.0.2,
-- luasec 0.4.1 and OpenSSL 1.0.2
--
-- 26 May 2019
--
-- four folders should be present in the folder
-- that contains Remote_Control.exe - 'lua", 'socket', 'mime' and 'ssl'
--
-- the folder that contains Remote_Control.exe should 
-- contain the following DLL's:
-- ssl.dll
-- ssleay32.dll
-- libeay32.dll
--
--[[https://github.com/diegonehab/luasocket
This is the LuaSocket 3.0-rc1. It has been tested on Windows 7, Mac OS X,
and Linux. 

Please use the project page at GitHub 

    https://github.com/diegonehab/luasocket

to file bug reports or propose changes. 

Have fun,
Diego Nehab.]]

--[[
 OpenSSL 1.0.2s 28 May 2019

 Copyright (c) 1998-2019 The OpenSSL Project
 Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson
 All rights reserved.
--]]

--[[
LuaSec 0.8
===============
LuaSec depends  on OpenSSL, and  integrates with LuaSocket to  make it
easy to add secure connections to any Lua applications or scripts.

Important: This version requires at least OpenSSL 1.0.2.
           For old versions of OpenSSL, use LuaSec 0.7.

Documentation: https://github.com/brunoos/luasec/wiki
--]]

local socket = require ("socket")
require("defines");

-- Counter Labels
Counter01Label = "get()";

tasks = {};

function get (host, file)
-- create coroutine for a task
local co = coroutine.wrap(function()
	download(host, file)
end)
-- insert it in the list
table.insert(tasks, co)
end

function dispatchX()
local i = 1;
while true do
	if tasks[i] == nil then	-- no other tasks?
		if tasks[1] == nil then	-- list is empty?
			break;	-- break the loop
		end
	i = 1;		-- else restart the loop
	end
	local res = tasks[i]() -- run a task
	if not res then -- task finished?
		table.remove(tasks, i)
		else
			i = i + 1;	-- go to next task
		end
	end
end

-- dispatcher using socket.select
function dispatch()
local i = 1;
local timedout = {}
while true do
	if tasks[i] == nil then	-- no other tasks?
		if tasks[1] == nil then	-- list is empty?
			break;	-- break the loop
		end
	i = 1;		-- else restart the loop
	timedout = {}
	end
	local res = tasks[i]() -- run a task
	if not res then -- task finished?
		table.remove(tasks, i)
		else		-- time out
			i = i + 1;	-- go to next task
			timedout[#timedout + 1] = res
			if (#timedout == #tasks) then	-- all tasks blocked
				socket.select(timedout)		-- wait
			end
		end
	end
end


function download(host, file)
local c = assert(socket.connect(host,80));
local count = 0;	-- counts the nubmer of bytes read
local request = "GET "..file.." HTTP/1.1\r\nUser-agent: "..socket._VERSION.."\r\nHost: "..host.."\r\nConnection: close\r\n\r\n";
-- next line request from pg 267 gets a "400 Bad Request" error
--local request = string.format("GET %s HTTP/1.1\r\nhost: %s\r\n\r\n", file, host)
print("Sending request to "..tostring(c));
c:send(request);
--[[ both the repeat and while true do loops work correctly
repeat
	local s, status = receive(c)
	if (s ~= nil) then count = count + #s; end;
until status == "closed";
--]]
---[[
local s, status;
while true do
	s,status = receive(c)
	--print (s);
	--if (#s > 0) then print(tostring(c).. " - "..tostring(status) .. " len = " ..#s) end
	count = count + #s
	if status == "closed" then break end
end
--]]
SetSpeed(0, 0, MyEngine, MyTIU);
c:close();
print(tostring(c).. " - "..file," : ", count);
BumpCounter(COUNTER01);
end

function receiveX(connection)
	local s, status, partial = connection:receive(2^10);
	return s or partial, status
end;

function receive(connection)
	connection:settimeout(0);	-- do not block
	local s, status, partial = connection:receive(2^10);
	--print (status);
	if status == "timeout" then
		coroutine.yield(connection)
		else
		--print(status);
	end
	
	return s or partial, status
end;

function setup(Engine, TIU)
MyEngine = Engine;
MyTIU = TIU;
host = "www.silogic.com"
--file = "/trains/RTC/copyright.txt"
file = "/index.html";
print(string.format("Run Time = %.2f seconds", RunTime()));

Smoke(OFF, 0, Engine, TIU);
EngineStartUp(Engine, TIU);
print(string.format("Run Time = %.2f seconds", RunTime()));
--[[ Sequential Downloads
-- if you call download(), swap receive and receiveX
download(host,file);
download(host,file);
download(host,file);
download(host,file);
download(host,file);
--]]
---[[ Parallel Downloads using dispatch()
get(host, file);
get(host, file);
get(host, file);
get(host, file);
get(host, file);
dispatch();		-- main loop
--]]
print(string.format("Run Time = %.2f seconds", RunTime()));
return true;	-- false=setup failed, true=setup succeeded
end

function cleanup(Engine, TIU)
--
print("cleanup : TIU " .. TIU .. " Engine " .. Engine .. " Debug " .. Debug());
print(string.format("Run Time = %.2f seconds", RunTime()));
EngineShutDown(Engine, TIU);
return true;
end

print("Initialization Complete");
