function Acked(s)

	local result = false	

	if s ~= nil then

		local slen = string.len(s)

		if (slen >= 3) and (string.sub(s, slen-2) == "\r\n>") then
			result = true
		elseif (slen >= 1) and (string.sub(s, slen) == ">") then
			result = true
		end
	end

	SMC:Log("debug", "Acked("..tostring(s).."): "..tostring(result))
	return result

end

function AppInit0()

	if port == nil then
		port = SMC:NewSerialPort("config1")
	end

end

function AppInit()

	gasynch = SMCContext
	gasynch:SetAsynch()

	AppInit0()

end

function DoPower(newState)

	if newState == 0 then
		part1="Dis"
	else
		part1="En"
	end

	SMC:Log("debug", part1.."abling the power");
	
	local executeResult = os.execute("sudo SetPowerSwitch 3 "..tostring(newState))
	
	if executeResult ~= 0 then
		SMC:Log("error", "--> exit code is "..tostring(executeResult))
	end

	return executeResult == 0;

end

function DoPowerOff()

	local result = DoPower(0)

	if result then
		Wait(20000)
	end

	return result

end

function DoPowerOn()

	local result = DoPower(1)

	if result then

		local rxd = "Not nil"
		local found = false

		result = false

		while rxd ~= nil and not found do

			rxd = AwaitData(60000)

			if rxd ~= nil then
				if Acked(rxd) then
					found = true
					result = true
				elseif string.find(rxd, "CONNECT") ~= nil then
					rxd = AwaitData(3000)
					found = true
					result = DoTestComms()
				end
			else
				result = DoTestComms()
			end
		end
		
	end

-- Send("ATI\r")
-- AwaitData(5000)
-- Send("ATS?\r")
-- AwaitData(5000)

	SMC:Log("debug", "DoPowerOn: "..tostring(result))
	return result

end

function DoTestComms()

	local result = false
	local rxd, rxd2

	Send("++++")
	rxd = AwaitData(1000)
	Send("\r")
	rxd2 = AwaitData(1000)

	if Acked(rxd) or Acked(rxd2) then
		result = true
	end

	SMC:Log("debug", "DoTestComms: "..tostring(result))
	return result

end

function Escape(s)

	local result

	if s == nil then
		result = "nil"
	else
		result = "'"..string.gsub(string.gsub(string.gsub(s,"\\","\\\\"),"\r","\\r"),"\n","\\n").."'"
	end

	return result

end

function PowerOff()

	AppInit()

	appRtn = coroutine.create(function()
		DoPowerOff()
		gasynch:ReportComplete()
		gasynch = nil
	end)

	coroutine.resume(appRtn)

end

function PowerOn()

	AppInit()

	appRtn = coroutine.create(function()
		DoPowerOn()
		gasynch:ReportComplete()
		gasynch = nil
	end)

	coroutine.resume(appRtn)

end

function ReportStringAsTelemetry(str)

	local evt = { }
	
	evt.header = { name = "LWTelem1", typename = "LWTelem1", domainname="LqrSMAC" }
	evt.properties = { }
	evt.properties["Telem-BoardID"] = { 32, "UINT8" }
	evt.properties["Telem-TaskID"] = { 64, "UINT8" }
	evt.properties["StringMessage"] = str
	
	SMC:PublishEvent(evt)

end

function AwaitData(timeout)

	SMC:Log("debug", "AwaitData timeout="..timeout)

	local rxd

	awaitTimer = SMC:NewTimer("await", timeout)
	rxd = coroutine.yield()
	awaitTimer = nil
	collectgarbage("collect")

	SMC:Log("debug", "AwaitData:"..Escape(rxd))
	return rxd

end

function AwaitResponse(timeout)

	SMC:Log("debug", "AwaitResponse timeout="..timeout)

	local rxdTotal, rxd, busy

	awaitTimer = SMC:NewTimer("await", timeout)
	busy = true
	rxdTotal = ""

	while (awaitTimer ~= nil) and busy do

		rxd = coroutine.yield()

		if rxd ~= nil then
			rxdTotal = rxdTotal..rxd
		end

		if Acked(rxdTotal) then
			SMC:Log("debug", "AwaitResponse: not busy because total was acked")
			busy = false
		end

	end

	awaitTimer = nil
	collectgarbage("collect")
	SMC:Log("debug", "AwaitResponse:"..Escape(rxdTotal))
	return rxdTotal

end

function Send(msg)

	SMC:Log("debug", "Sending "..Escape(msg))
	port:Transmit(msg)

end

function DoSendCmd(cmdname)

	SMC:Log("debug", "DoSendCmd "..Escape(cmdname))

	if not DoTestComms() then
		DoPowerOn()
	end

	Send(cmdname.."\r")

	local response = AwaitResponse(60000)

	gasynch:AppendOutput(Escape(response))
	gasynch:ReportComplete()
	gasynch = nil
		
end

function SendCmd(cmdname)

	AppInit()
	appRtn = coroutine.create(function() DoSendCmd(cmdname) end)
	coroutine.resume(appRtn)

end

function Wait(timeout)

	waitTimer = SMC:NewTimer("wait", timeout)

	while waitTimer ~= nil
	do
		coroutine.yield()
	end 

end

function SMCReportSerialData(portName,data)

	if (appRtn ~= nil) and (data ~= nil) and (string.len(data) ~= 0) then

		SMC:Log("debug", "Received "..tostring(#data).." bytes:"..Escape(data).." on "..portName)

		if awaitTimer ~= nil then
			--awaitTimer = nil
			--collectgarbage("collect")
			coroutine.resume(appRtn, data)
		end

	end

end

function SMCReportTimeout(timerName)

	SMC:Log("debug", "SMCReportTimeout timerName="..timerName)

	if appRtn ~= nil then
		if timerName == "wait" then
			waitTimer = nil
			collectgarbage("collect")
			coroutine.resume(appRtn, nil)
		elseif timerName == "await" then
			awaitTimer = nil
			collectgarbage("collect")
			coroutine.resume(appRtn, nil)
		end
	end

	SMC:Log("debug", "exiting SMCReportTimeout")

end

function PingLoop(addr)
	AppInit()
	appRtn = coroutine.create(function()
		ReportStringAsTelemetry("Starting PingLoop")
		while true do
			Send("ATR"..tostring(addr).."\r")
			local response = AwaitResponse(60000)
			ReportStringAsTelemetry("Ping:"..response)
			local timeNow = os.time()
			local timeNext10 = (timeNow-(timeNow%600))+600
			Wait(1000*(timeNext10-timeNow))
		end
	end)
	coroutine.resume(appRtn)
end

function StopLoop()
	if gasynch ~= nil then
		gasynch:AppendOutput("** Stopped")
		gasynch:ReportComplete()
		gasynch = nil
		appRtn = nil
	end
end


gasynch = nil
appRtn = nil

