--[[
ģƣ⴮AT
ģ鹦ܣAT
ģ޸ʱ䣺2017.02.13
]]
--ģ,
local base = _G
local table = require"table"
local string = require"string"
local uart = require"uart"
local rtos = require"rtos"
local sys = require"sys"
module("ril")

--سõȫֺ
local setmetatable = base.setmetatable
local print = base.print
local type = base.type
local smatch,sfind,slen = string.match,string.find,string.len
local vwrite = uart.write
local vread = uart.read

--ǷΪ͸ģʽtrueΪ͸ģʽfalsenilΪ͸ģʽ
--ĬϷ͸ģʽ
local transparentmode
--͸ģʽ£⴮ݽյĴ
local rcvfunc

--ִAT1޷жatִʧܣ
local TIMEOUT = 60000 
local DATA_TIMEOUT = 60000

--ATӦ
--NORESULTյӦݵurc֪ͨ͵ATӦûͣĬΪ
--NUMBERICͣ緢AT+CGSNӦΪ862991527986589\r\nOKָ862991527986589һΪ
--SLINEǰ׺ĵַͣ緢AT+CSQӦΪ+CSQ: 23,99\r\nOKָ+CSQ: 23,99һΪַ
--MLINEǰ׺Ķַͣ緢AT+CMGR=5ӦΪ+CMGR: 0,,84\r\n0891683108200105F76409A001560889F800087120315123842342050003590404590D003A59\r\nOKָOK֮ǰΪַ
--STRINGǰ׺ַͣ緢AT+ATWMFT=99ӦΪSUCC\r\nOKָSUCC
--SPECIALͣҪAT⴦CIPSENDCIPCLOSECIFSR
local NORESULT,NUMBERIC,SLINE,MLINE,STRING,SPECIAL = 0,1,2,3,4,10

--ATӦͱԤ¼
local RILCMD = {
	["+CSQ"] = 2,
	["+CGSN"] = 1,
	["+WISN"] = 4,
	["+CIMI"] = 1,
	["+CCID"] = 1,
	["+CGATT"] = 2,
	["+CCLK"] = 2,
	["+ATWMFT"] = 4,
	["+CMGR"] = 3,
	["+CMGS"] = 2,
	["+CPBF"] = 3,
	["+CPBR"] = 3,
 	["+CIPSEND"] = 10,
	["+CIPCLOSE"] = 10,
	["+SSLINIT"] = 10,
	["+SSLCERT"] = 10,
	["+SSLCREATE"] = 10,
	["+SSLCONNECT"] = 10,
	["+SSLSEND"] = 10,
	["+SSLDESTROY"] = 10,
	["+SSLTERM"] = 10,
	["+CIFSR"] = 10,
}

--radioreadyATͨǷ׼
--delayingִĳЩATǰҪʱһʱ䣬ִЩAT˱־ʾǷʱ״̬
local radioready,delaying = false

--AT
local cmdqueue = {
	"ATE0",
	"AT+CMEE=0",
}
--ǰִеAT,,ص,ӳִʱ,ͷ,,ʽ
local currcmd,currarg,currsp,curdelay,cmdhead,cmdtype,rspformt
--,мϢ,Ϣ
local result,interdata,respdata

--ril: 
--ATյӦ
--ATʱûӦ
--ײϱ֪ͨǼΪurc

--[[
atimeout
  ATʱûӦĴ
  
ֵ
]]
local function atimeout()
	--
	sys.restart("ril.atimeout_"..(currcmd or ""))
end

--[[
defrsp
  ATĬӦûжĳATӦߵ
  
		cmdӦӦAT
		successATִнtruefalse
		responseATӦеִнַ
		intermediateATӦемϢ
ֵ
]]
local function defrsp(cmd,success,response,intermediate)
	print("default response:",cmd,success,response,intermediate)
end

--ATӦ
local rsptable = {}
setmetatable(rsptable,{__index = function() return defrsp end})

--ԶATӦʽATӦΪSTRINGʽʱûԽһĸʽ
local formtab = {}

--[[
regrsp
  עĳATӦĴ
  
		headӦӦATͷȥǰATַ
		fncATӦĴ
		typATӦͣȡֵΧNORESULT,NUMBERIC,SLINE,MLINE,STRING,SPECIAL
		formttypΪSTRINGʱһSTRINGеϸʽ
ֵɹtrueʧfalse
]]
function regrsp(head,fnc,typ,formt)
	--ûжӦ
	if typ == nil then
		rsptable[head] = fnc
		return true
	end
	--˺ϷӦ
	if typ == 0 or typ == 1 or typ == 2 or typ == 3 or typ == 4 or typ == 10 then
		--ATӦѴڣõĲһ
		if RILCMD[head] and RILCMD[head] ~= typ then
			return false
		end
		--
		RILCMD[head] = typ
		rsptable[head] = fnc
		formtab[head] = formt
		return true
	else
		return false
	end
end

--[[
rsp
  ATӦ
  
ֵ
]]
local function rsp()
	--ֹͣӦʱʱ
	sys.timer_stop(atimeout)
	--ATʱѾָͬӦ
	if currsp then
		currsp(currcmd,result,respdata,interdata)
	--ûעӦҵ
	else
		rsptable[cmdhead](currcmd,result,respdata,interdata)
	end
	--ȫֱ
	currcmd,currarg,currsp,curdelay,cmdhead,cmdtype,rspformt = nil
	result,interdata,respdata = nil
end

--[[
defurc
  urcĬϴûжĳurcӦߵ
  
		dataurc
ֵ
]]
local function defurc(data)
	print("defurc:",data)
end

--urcĴ
local urctable = {}
setmetatable(urctable,{__index = function() return defurc end})

--[[
regurc
  עĳurcĴ
  
		prefixurcǰ׺ǰַ+дֵַ
		handlerurcĴ
ֵ
]]
function regurc(prefix,handler)
	urctable[prefix] = handler
end

--[[
deregurc
  עĳurcĴ
  
		prefixurcǰ׺ǰַ+дֵַ
ֵ
]]
function deregurc(prefix)
	urctable[prefix] = nil
end

--ݹ⴮յʱҪô˺˴һ
local urcfilter,urcfilterlen

--[[
urc
  urc
  
		dataurc
ֵ
]]
local function urc(data)
	--ATͨ׼
	if data == "RDY" then
		radioready = true
	else
		local prefix = smatch(data,"(%+*[%u%d& ]+)")
		--ִprefixurcݹ
		urcfilter,urcfilterlen = urctable[prefix](data,prefix)
	end
end

--[[
procatc
  ⴮յ
  
		dataյ
ֵ
]]
local function procatc(data)
	--ӦǶַʽ
	if interdata and cmdtype == MLINE then
		--OK\r\nΪӦδ
		if data ~= "OK\r\n" then
			--ȥ\r\n
			if sfind(data,"\r\n",-2) then
				data = string.sub(data,1,-3)
			end
			--ƴӵм
			interdata = interdata .. "\r\n" .. data
			return
		end
	end
	--ڡݹ
	if urcfilter then
		if urcfilterlen and urcfilterlen<200 then print(data) end
		data,urcfilter = urcfilter(data)
	else
		print("atc:",data)
	end
	--ȥ\r\n
	if sfind(data,"\r\n",-2) then
		data = string.sub(data,1,-3)
	end
	--Ϊ
	if data == "" then
		return
	end
	--ǰִжΪurc
	if currcmd == nil then
		urc(data)
		return
	end

	local isurc = false

	--һЩĴϢתΪERRORͳһ
	if sfind(data,"^%+CMS ERROR:") or sfind(data,"^%+CME ERROR:") or (data == "CONNECT FAIL" and currcmd and smatch(currcmd,"CIPSTART")) then
		data = "ERROR"
	end
	--ִгɹӦ
	if data == "OK" or data == "SHUT OK" then
		result = true
		respdata = data
	--ִʧܵӦ
	elseif data == "ERROR" or data == "NO ANSWER" or data == "NO DIALTONE" then
		result = false
		respdata = data
	--ҪATӦ
	elseif data == "> " then
		--Ͷ
		if cmdhead == "+CMGS" then
			print("send:",currarg)
			vwrite(uart.ATC,currarg,"\026")
		--
		elseif cmdhead == "+CIPSEND" or cmdhead == "+SSLSEND" or cmdhead == "+SSLCERT" then
			print("send first 200 bytes",currarg:sub(1,200))
			vwrite(uart.ATC,currarg)
		else
			print("error promot cmd:",currcmd)
		end
	else
		--
		if cmdtype == NORESULT then
			isurc = true
		--ȫ
		elseif cmdtype == NUMBERIC then
			local numstr = smatch(data,"(%x+)")
			if numstr == data then
				interdata = data
			else
				isurc = true
			end
		--ַ
		elseif cmdtype == STRING then
			--һʽ
			if smatch(data,rspformt or "^.+$") then
				interdata = data
			else
				isurc = true
			end
		elseif cmdtype == SLINE or cmdtype == MLINE then
			if interdata == nil and sfind(data, cmdhead) == 1 then
				interdata = data
			else
				isurc = true
			end
		--⴦
		elseif cmdhead == "+CIFSR" then
			local s = smatch(data,"%d+%.%d+%.%d+%.%d+")
			if s ~= nil then
				interdata = s
				result = true
			else
				isurc = true
			end
		--⴦
		elseif cmdhead == "+CIPSEND" or cmdhead == "+CIPCLOSE" then
			local keystr = cmdhead == "+CIPSEND" and "SEND" or "CLOSE"
			local lid,res = smatch(data,"(%d), *([%u%d :]+)")

			if lid and res then
				if (sfind(res,keystr) == 1 or sfind(res,"TCP ERROR") == 1 or sfind(res,"UDP ERROR") == 1 or sfind(data,"DATA ACCEPT")) and (lid == smatch(currcmd,"=(%d)")) then
					result = true
					respdata = data
				else
					isurc = true
				end
			elseif data == "+PDP: DEACT" then
				result = true
				respdata = data
			else
				isurc = true
			end		
		elseif cmdhead=="+SSLINIT" or cmdhead=="+SSLTERM" then
			local keystr = smatch(cmdhead,"SSL(%w+)")
			if smatch(data,"^SSL&%d,"..keystr) then
				respdata = data
				if smatch(data,"ERROR") then
					result = false
				else
					result = true
				end
			else
				isurc = true
			end
		elseif cmdhead=="+SSLCERT" then
			if smatch(data,"^SSL&%d,INPUT CERT") or smatch(data,"^SSL&%d,CONFIG CERT") then
				respdata = data
				if smatch(data,"ERROR") then
					result = false
				else
					result = true
				end
			else
				isurc = true
			end
		elseif cmdhead=="+SSLCREATE" or cmdhead=="+SSLCONNECT" or cmdhead=="+SSLSEND" or cmdhead=="+SSLDESTROY" then
			local keystr = smatch(cmdhead,"SSL(%w+)")
			local lid,res = smatch(data,"^SSL&(%d),(%w+)")
			
			print("ril.ssl",keystr,lid,res,smatch(currcmd,"=(%d)"),smatch(data,"^SSL&%d,(%w+)"))
			
			if lid and res then
				if (lid == smatch(currcmd,"=(%d)")) and (keystr==smatch(data,"^SSL&%d,(%w+)")) then
					respdata = data
					if smatch(data,"ERROR") then
						result = false
					else
						result = true
					end
				else
					isurc = true
				end				
			else
				isurc = true
			end	
		else
			isurc = true
		end
	end
	--urc
	if isurc then
		urc(data)
	--Ӧ
	elseif result ~= nil then
		rsp()
	end
end

--Ƿڶȡ⴮
local readat = false

--[[
getcmd
  һAT
  
		itemAT
ֵǰAT
]]
local function getcmd(item)
	local cmd,arg,rsp,delay
	--string
	if type(item) == "string" then
		--
		cmd = item
	--table
	elseif type(item) == "table" then
		--
		cmd = item.cmd
		--
		arg = item.arg
		--Ӧ
		rsp = item.rsp
		--ʱִʱ
		delay = item.delay
	else
		print("getpack unknown item")
		return
	end
	--ǰ׺
	head = smatch(cmd,"AT([%+%*]*%u+)")

	if head == nil then
		print("request error cmd:",cmd)
		return
	end
	--в
	if head == "+CMGS" or head == "+CIPSEND" then -- в
		if arg == nil or arg == "" then
			print("request error no arg",head)
			return
		end
	end

	--ֵȫֱ
	currcmd = cmd
	currarg = arg
	currsp = rsp
	curdelay = delay
	cmdhead = head
	cmdtype = RILCMD[head] or NORESULT
	rspformt = formtab[head]

	return currcmd
end

--[[
sendat
  AT
  
ֵ
]]
local function sendat()
	--ATͨδ׼ڶȡ⴮ݡATִл߶ʱĳAT
	if not radioready or readat or currcmd ~= nil or delaying then		
		return
	end

	local item

	while true do
		--AT
		if #cmdqueue == 0 then
			return
		end
		--ȡһ
		item = table.remove(cmdqueue,1)
		--
		getcmd(item)
		--Ҫӳٷ
		if curdelay then
			--ӳٷͶʱ
			sys.timer_start(delayfunc,curdelay)
			--ȫֱ
			currcmd,currarg,currsp,curdelay,cmdhead,cmdtype,rspformt = nil
			item.delay = nil
			--ӳٷͱ־
			delaying = true
			--²еĶ
			table.insert(cmdqueue,1,item)
			return
		end

		if currcmd ~= nil then
			break
		end
	end
	--ATӦʱʱ
	if currcmd:match("^AT%+CIPSTART") or currcmd:match("^AT%+CIPSEND") or currcmd:match("^AT%+SSLCREATE") or currcmd:match("^AT%+SSLCONNECT") or currcmd:match("^AT%+SSLSEND") then
		sys.timer_start(atimeout,DATA_TIMEOUT)
	else
		sys.timer_start(atimeout,TIMEOUT)
	end

	print("sendat:",currcmd)
	--⴮зAT
	vwrite(uart.ATC,currcmd .. "\r")
end

--[[
delayfunc
  ʱִĳATĶʱص
  
ֵ
]]
function delayfunc()
	--ʱ־
	delaying = nil
	--ִAT
	sendat()
end

--[[
atcreader
  AT⴮ݽϢĴ⴮յʱߵ˺
  
ֵ
]]
local function atcreader()
	local s

	if not transparentmode then readat = true end
	--ѭȡ⴮յ
	while true do
		--ÿζȡһ
		s = vread(uart.ATC,"*l",0)
		if slen(s) ~= 0 then
			if transparentmode then
				--͸ģʽֱת
				rcvfunc(s)
			else
				--͸ģʽ´յ
				procatc(s)
			end
		else
			break
		end
	end
	if not transparentmode then
		readat = false
		--ݴԺִAT
		sendat()
	end
end

--עᡰAT⴮ݽϢĴ
sys.regmsg("atc",atcreader)

--[[
request
  ATײ
  
		cmdAT
		argATAT+CMGS=12ִк󣬽ᷢʹ˲AT+CIPSEND=14ִк󣬽ᷢʹ˲
		onrspATӦĴֻǵǰ͵ATӦЧ֮ʧЧ
		delayʱdelay󣬲ŷʹAT
ֵ
]]
function request(cmd,arg,onrsp,delay)
	if transparentmode then return end
	--뻺
	if arg or onrsp or delay or formt then
		table.insert(cmdqueue,{cmd = cmd,arg = arg,rsp = onrsp,delay = delay})
	else
		table.insert(cmdqueue,cmd)
	end
	--ִAT
	sendat()
end

--[[
setransparentmode
  ATͨΪ͸ģʽ
  
		fnc͸ģʽ£⴮ݽյĴ
ֵ
ע⣺͸ģʽͷ͸ģʽֻֿ֧ĵһã֧;л
]]
function setransparentmode(fnc)
	transparentmode,rcvfunc = true,fnc
end

--[[
sendtransparentdata
  ͸ģʽ·
  
		data
ֵɹtrueʧܷnil
]]
function sendtransparentdata(data)
	if not transparentmode then return end
	vwrite(uart.ATC,data)
	return true
end

function setDataTimeout(tm)
    DATA_TIMEOUT = tm
end
