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

© Copyright 2022 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
-- Tape Reading using Lua Sockets
title = "Tag Reader Test TCP Server w/Lua Sockets"
--
-- This sketch communicates with Tag Detectors using the TCP sockets support built into
-- Lua. This is different than the "normal" RTC Program Control based sketches. Those
-- sketches call complied c++ code that interfaces with components contained in the Internet 
-- Suite by Francois Piette (ICS) v5r20 for BCB4.0/6.0 code and v8w (pre v8.8) for CB10.1.
--
-- Lua sockets seem to be able to handle just one client at a time. Complicated solutions
-- require coroutines (like copas) or and external library like ev (libev):
-- https://github.com/lipp/lua-websockets/blob/master/examples/echo-client-ev.lua
--
-- https://stackoverflow.com/questions/15191122/multiple-connections-lua-sockets
--
-- load the socket support and its friends
-- https://defold.com/ref/alpha/socket/
--[[
  WiFiClient.cpp - TCP/IP client for esp8266, mostly compatible
                   with Arduino WiFi shield library

  Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
  This file is part of the esp8266 core for Arduino environment.

https://arduino-esp8266.readthedocs.io/en/latest/libraries.html
https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/readme.html
#######################################
# Datatypes (KEYWORD1)
#######################################

WiFi	KEYWORD1
WiFiClient	KEYWORD1
WiFiServer	KEYWORD1


#######################################
# Methods and Functions (KEYWORD2)
#######################################

#WiFiClient
status	KEYWORD2
connect	KEYWORD2
write	KEYWORD2
write_P	KEYWORD2
available	KEYWORD2
read	KEYWORD2
peek	KEYWORD2
peekBytes	KEYWORD2
flush	KEYWORD2
stop	KEYWORD2
connected	KEYWORD2
bool	KEYWORD2
remoteIP	KEYWORD2
remotePort	KEYWORD2
localIP	KEYWORD2
localPort	KEYWORD2
getNoDelay	KEYWORD2
setNoDelay	KEYWORD2
setLocalPortStart	KEYWORD2
stopAll	KEYWORD2
stopAllExcept	KEYWORD2

#######################################
# Constants (LITERAL1)
#######################################
WIFI_OFF	LITERAL1
WIFI_AP	LITERAL1
WIFI_STA	LITERAL1
WIFI_AP_STA	LITERAL1
WIFI_PHY_MODE_11B	LITERAL1
WIFI_PHY_MODE_11G	LITERAL1
WIFI_PHY_MODE_11N	LITERAL1
WIFI_NONE_SLEEP	LITERAL1
WIFI_LIGHT_SLEEP	LITERAL1
WIFI_MODEM_SLEEP	LITERAL1
WIFICLIENT_MAX_PACKET_SIZE	LITERAL1
UDP_TX_PACKET_MAX_SIZE	LITERAL1
DEBUG_ESP_WIFI	LITERAL1
]]


local socket = require("socket")
print(socket._VERSION)

require("defines");
require("functions");

local ip, port = "192.168.1.100", 8897;
local server;
local ReceivedPacketCount = 0;
local TimeoutSeconds = 2;

Counter03Label = "Client Count";
local C3 = COUNTER03;

totalclient = 0 	-- store the total connections
clients = {} 		-- e.g. clients[1] = 0xSocketAddress
clientnames = {} 	-- e.g. clientnames[1] = "john"

--[[---------------------------------------------------------------------------------------]]
function setup() 
--
-- Setup the sketch as a server
--
--port = 8897;	-- TCP Socket in RTC Program
--ip = "*";	-- this failed to receive packet from client if the ip = "*" in luasocket 3.0-rc1 (CB10.1)
			-- works ok in luasocket 2.0.2a (BCB4 and BCB6)
			-- in 2.0.2a, getsockname returns "0.0.0.0"
			-- in 3.0-rc1 getsockname returns "::"
--ip = "";	-- in 3.0-rc1 getsockname returns the ipv6 address eg: fe80::153c:6f9b:81b0:c9b3%15
--ip = "192.168.1.100";	-- this works but means we need to know our ip address which is hard to find
--ip = "0.0.0.0";		-- this works with luasocket 2.0.2a and with luasocket 3.0-rc1
						-- using this value, getsockname() returns "0.0.0.0" in both versions of luasocket.
						-- We are binding to all possible ip for the local computer, ie 127.0.0.1 and
						-- its real ip address ie 192.168.1.100
--ip = "127.0.0.1";		-- this does not work

-- create a TCP socket and bind it to the local host, at the RTC port
--server = assert(socket.bind(ip, port));
server, err = socket.bind(ip, port);
if (err ~= nil) then
	return Stop("error : " .. err);
	end;
local boundip, boundport = server:getsockname();
print("(bound to tcp socket " .. boundip .. " - " .. boundport.. ")");
-- settimeout() so as to not block connetcion
server:settimeout(0.01)
return true;
end
--[[---------------------------------------------------------------------------------------]]
-- start the loop to listening connection
function loop()
--Yield();	-- check for [STOP] or function button
-- if we lost the connection then reconnect

-- new client connected
local client, err = server:accept();

-- print the ip of new client and add new client to the table
if (client) then
	totalclient = totalclient + 1;
	BumpCounter(C3);
	clients[totalclient] = client;
	clientnames[totalclient] = "unknown";
	clients[totalclient]:settimeout(0.01);
	print("New client : " .. tostring(client:getpeername()) .. " at " ..os.date("%m/%d/%Y %H:%M:%S"))
	else
	if (err ~= "timeout") then
		print("error : " .. err);
		return true;
		end;
	end;

if (totalclient > 0) then
  -- loop through the client table
	for i = 1, totalclient do
		-- if there is client, listen to that client
		if (clients[i] ~= nil) then
			--print("Listening - client #" .. i);
			clientmessage, err = clients[i]:receive('*l') -- accept data from client
			--print("receive(" .. i .. ") return");
			-- check if there is something sent to server, and no error occured
			if (err) then
				--print("error return from receive() " .. err);
				if (err ~= "timeout") then
					print("error : " .. err);
					if (err == "closed") then
						clients[i].close();
						--clients[i]:shutdown();
						clients[i] = nil;
						print("Client # " .. i .. " closed");
						end;
					return true;
					end;		
				-- ignore the timeout error
				else
				-- pick out the packet type and check if it is a DETECTORNAME (0x07) packet
				type = string.sub(clientmessage, 9, 10);
				type = clientmessage:sub( 9, 10);
				print("packet type = " .. type );
				if (type == "07") then
					-- Packet Type 7 (Detector Name)
					-- FF0C000007104465746563746F722D313220202020208DF0
					clientnames[i] = str2hex(string.sub(clientmessage, 13, 44));
					clientnames[i] = clientnames[i]:gsub("%s+", "");
					end;
				print(tostring("[" .. clientnames[i]) .. "]: " .. clientmessage)
				--clients[i]:send("sending : " .. clientmessage .. "\r\n");
				end--if
			end--if
		end--for
	end-- if
return true;
end
--[[---------------------------------------------------------------------------------------]]
function cleanup(Engine, TIU)
	-- this function is called once when the user presses the [STOP] button or the Stop() function is called
	print("cleanup() Running");
	print(ReceivedPacketCount .. " Packets Received");
	-- close any open client connections
	if (totalclient > 0) then
	    -- loop through the client table
	    for i = 1, totalclient do
			if (clients[i] ~= nil) then
				--print("Listening - client #" .. i);
				-- if there is client
				clients[i]:close();
				clients[i]:shutdown();
				print("Client # " .. i .. " closed");
				end
		    end
	    end
    -- done with server, close the server
	if (server) then
		server:close();
		end;
return true;		-- false=cleanup failed, true=cleanup succeeded
end
--[[---------------------------------------------------------------------------------------]]


