--[[
ģƣп
ģ鹦ܣʼпܡϢַʱӿ
ģ޸ʱ䣺2017.02.17
]]

--ģ,
require"patch"
local base = _G
local table = require"table"
local rtos = require"rtos"
local uart = require"uart"
local io = require"io"
local os = require"os"
local string = require"string"
module(...,package.seeall)

--سõȫֺ
local print = base.print
local unpack = base.unpack
local ipairs = base.ipairs
local type = base.type
local pairs = base.pairs
local assert = base.assert
local tonumber = base.tonumber

--libű汾ţֻҪlibеκһű޸ģҪ´˰汾
SCRIPT_LIB_VER = "1.2.3"

--Ƿű쳣ʱ  űsys.restartӿʱ
--Ƿйĵȴ¼
--Ƿluaڼ﷨
local restartflg,restartpending,luarunerr = 0,false,0

--luaгʱǷΪдİ汾
local sRestoreFlag = true

--ǷҪˢ½桱ı־GUIĿŻõ˱־
local refreshflag = false
--[[
refresh
  ýˢ±־GUIĿŻõ˽ӿ
  
ֵ
]]
function refresh()
	refreshflag = true
end

--ʱֵ֧ĵʱλ
local MAXMS = (0x7fffffff-(0x7fffffff%17))/17
--ʱid
local uniquetid = 0
--ʱid
local tpool = {}
--ʱ
local para = {}
--ʱǷѭ
local loop = {}
--lprfunûԶġ͵ػ
--lpringǷѾԶػʱ
local lprfun,lpring
--ϢļԼϢ
local LIB_ERR_FILE,liberr,extliberr = "/lib_err.txt",""
--ģʽ
--SIMPLE_MODEģʽĬϲῪÿһӲһڲϢʱѯcsqʱѯcengĹ
--FULL_MODEģʽĬϻῪÿһӲһڲϢʱѯcsqʱѯcengĹ
SIMPLE_MODE,FULL_MODE = 0,1
--ĬΪģʽ
local workmode = FULL_MODE

--[[
timerfnc
  ײcoreϱⲿʱϢ
  
		tidʱid
ֵ
]]
local function timerfnc(tid)
	--ʱidЧ
	if tpool[tid] ~= nil then
		--˶ʱĻص
		local cb = tpool[tid]
		--ʱֵ֧ʱΪʱ
		if type(tpool[tid]) == "table" then
			local tval = tpool[tid]
			tval.times = tval.times+1
			--ֵļʱδִϣִһ
			if tval.times < tval.total then
				rtos.timer_start(tid,tval.step)
				return
			end
			cb = tval.cb
		end
		--ѭʱӶʱid˶ʱidλõ
		if not loop[tid] then tpool[tid] = nil end
		--Զɱ
		if para[tid] ~= nil then
			local pval = para[tid]
			--ѭʱӶʱ˶ʱidλõ
			if not loop[tid] then para[tid] = nil end
			--ִжʱص
			cb(unpack(pval))
		--Զɱ
		else
			--ִжʱص
			cb()
		end
		--ѭʱ˶ʱ
		if loop[tid] then rtos.timer_start(tid,loop[tid]) end
	end
end

--[[
comp_table
  ȽtableǷͬע⣺tableвٰtable
  
		t1һtable
		t2ڶtable
ֵͬtruefalse
]]
local function comp_table(t1,t2)
	if not t2 then return #t1 == 0 end
	if #t1 == #t2 then
		for i=1,#t1 do
			if unpack(t1,i,i) ~= unpack(t2,i,i) then
				return false
			end
		end
		return true
	end
	return false
end

--[[
timer_start
  һʱ
  
		fncʱĻص
		msʱʱλΪ
		...ԶɱûصʱԶĿɱشû
		ע⣺fncͿɱ...ͬΨһһʱ
ֵʱIDʧܷnil
]]
function timer_start(fnc,ms,...)
	--صʱЧ
	assert(fnc~=nil,"timer_start:callback function==nil")
	assert(ms>0,"timer_start:ms==0")
	--رȫͬĶʱ
	if arg.n == 0 then
		timer_stop(fnc)
	else
		timer_stop(fnc,unpack(arg))
	end
	--ʱֵ֧ʱΪʱ
	if ms > MAXMS then
		local count = (ms-(ms%MAXMS))/MAXMS + (ms%MAXMS == 0 and 0 or 1)
		local step = (ms-(ms%count))/count
		tval = {cb = fnc, step = step, total = count, times = 0}
		ms = step
	--ʱδֵ֧ʱ
	else
		tval = fnc
	end
	--Ӷʱidҵһδʹõidʹ
	while true do
		uniquetid = uniquetid + 1
		if tpool[uniquetid] == nil then
			tpool[uniquetid] = tval
			break
		end
	end
	--õײӿʱ
	if rtos.timer_start(uniquetid,ms) ~= 1 then print("rtos.timer_start error") return end
	--ڿɱڶʱб
	if arg.n ~= 0 then
		para[uniquetid] = arg
	end
	--ضʱid
	return uniquetid
end

--[[
timer_loop_start
  һѭʱ
  
		fncʱĻص
		msʱʱλΪ
		...ԶɱûصʱԶĿɱشû
		ע⣺fncͿɱ...ͬΨһһʱ
ֵʱIDʧܷnil
]]
function timer_loop_start(fnc,ms,...)
	local tid = timer_start(fnc,ms,unpack(arg))
	if tid then loop[tid] = ms end
	return tid
end

--[[
timer_stop
  رһʱ
  
		valʽ
		     һǿʱʱصĶʱidʽʱҪٴɱ...Ψһһʱ
			 һǿʱʱĻصʽʱٴɱ...Ψһһʱ
		...Զɱtimer_starttimer_loop_startеĿɱͬ
ֵ
]]
function timer_stop(val,...)
	--valΪʱid
	if type(val) == "number" then
		tpool[val],para[val],loop[val] = nil
		rtos.timer_stop(val)
	else
		for k,v in pairs(tpool) do
			--صͬ
			if type(v) == "table" and v.cb == val or v == val then
				--Զɱͬ
				if comp_table(arg,para[k])then
					rtos.timer_stop(k)
					tpool[k],para[k],loop[k] = nil
					break
				end
			end
		end
	end
end

--[[
timer_stop_all
  رĳصǵжʱۿʱʱûдԶɱ
  
		fncʱʱĻص
ֵ
]]
function timer_stop_all(fnc)
	for k,v in pairs(tpool) do
		if type(v) == "table" and v.cb == fnc or v == fnc then
			rtos.timer_stop(k)
			tpool[k],para[k],loop[k] = nil
		end
	end
end

--[[
timer_is_active
  жĳʱǷڿ״̬
  
		valʽ
		     һǿʱʱصĶʱidʽʱҪٴɱ...Ψһһʱ
			 һǿʱʱĻصʽʱٴɱ...Ψһһʱ
		...Զɱtimer_starttimer_loop_startеĿɱͬ
ֵtruefalse
]]
function timer_is_active(val,...)
	if type(val) == "number" then
		return tpool[val] ~= nil
	else
		for k,v in pairs(tpool) do
			if type(v) == "table" and v.cb == val or v == val then
				if comp_table(arg,para[k]) then
					return true
				end
			end
		end
		return false
	end
end

--[[
readtxt
  ȡıļеȫ
  
		fļ·
ֵıļеȫݣȡʧΪַnil
]]
local function readtxt(f)
	local file,rt = io.open(f,"r")
	if not file then print("sys.readtxt no open",f) return "" end
	rt = file:read("*a")
	file:close()
	return rt
end

--[[
writetxt
  дıļ
  
		fļ·
		vҪдı
ֵ
]]
local function writetxt(f,v)
	local file = io.open(f,"w")
	if not file then print("sys.writetxt no open",f) return end	
	file:write(v)
	file:close()
end

--[[
appenderr
  ׷ӴϢLIB_ERR_FILEļ
  
		sϢûԶ壬һstringͣtraceлӡ˴Ϣ
ֵ
]]
local function appenderr(s)
	print("appenderr",string.len(liberr),s)
	if string.len(liberr)<2048 then
		liberr = liberr..s
		writetxt(LIB_ERR_FILE,liberr)
	end	
end

--[[
initerr
  ӡLIB_ERR_FILEļеĴϢ
  
ֵ
]]
local function initerr()
	extliberr = readtxt(LIB_ERR_FILE) or ""
	print("sys.initerr",extliberr)
	--ɾLIB_ERR_FILEļ
	os.remove(LIB_ERR_FILE)
end

--[[
getextliberr
  ȡLIB_ERR_FILEļеĴϢⲿģʹ
  
ֵLIB_ERR_FILEļеĴϢ
]]
function getextliberr()
	return extliberr or (readtxt(LIB_ERR_FILE) or "")
end

--[[
luaerrexit
  һ﷨ʹcoreеLuaǰеĽűԶĽűԶɾǰнű˵ԭʼдĽű
  
ֵ
˵  Ǵ˽ӿڵµΪдupdatedbgaliyuniototaܣupdatedbgaliyuniototaܻûִнʱܽű﷨󣬴ʱ﷨󻺴
		updatedbgaliyuniototaܽͨһǷluaerrexitfncӿڲ﷨󣬴﷨͵
		﷨tracesaferestartȥ
]]
local function luaerrexit()
	if sRestoreFlag then
		luaerrexitfnc()
	else
		rtos.restart()
	end
end
local function saferestart(r)
	print("saferestart",r,restartflg,rtos.remove_dir)
	appenderr(r or "")
	if restartflg==0 then
		if luarunerr==1 then
			luarunerr = 2
			regapp(luaerrexit,"LUA_ERR_EXIT")
			dispatch("LUA_ERR_EXIT")
		else
			rtos.restart()
		end
	else		
		restartpending = true
	end
end


--[[
restart
  
  
		rԭûԶ壬һstringͣtraceлӡԭ
ֵ
]]
function restart(r)
	assert(r and r ~= "","sys.restart cause null")
	saferestart("restart["..r.."];")
end

--[[
getcorever
  ȡײ汾
  
ֵ汾ַ
]]
function getcorever()
	return rtos.get_version()
end

--[[
init
  luaӦóʼ
  
		mode翪ǷGSMЭջ1
		lprfncûӦýűжġ͵ػк͵ʱļеrunӿڲִκζ򣬻ʱ1Զػ
ֵ
]]
function init(mode,lprfnc)
	--ûӦýűmain.luaб붨MODULE_TYPEPROJECTVERSIONȫֱζοdemoеmain.lua
	assert(base.MODULE_TYPE and base.MODULE_TYPE ~= "" and base.PROJECT and base.PROJECT ~= "" and base.VERSION and base.VERSION ~= "","Undefine MODULE_TYPEPROJECT or VERSION")
	base.collectgarbage("setpause",80)
	require"net"
	--AT⴮
	uart.setup(uart.ATC,0,0,uart.PAR_NONE,uart.STOP_1)
	print("poweron reason:",rtos.poweron_reason(),base.MODULE_TYPE,base.PROJECT,base.VERSION,SCRIPT_LIB_VER,getcorever())
	if mode == 1 then
		--翪
		if rtos.poweron_reason() == rtos.POWERON_CHARGER then
			--رGSMЭջ
			rtos.poweron(0)
		end
	end
	--ڽűдļļӡϢ
	local f = io.open("/luaerrinfo.txt","r")
	if f then
		print(f:read("*a") or "")
		f:close()
	end
	-- ûӦýűжġ͵ػ
	lprfun = lprfnc
	initerr()
end

--[[
poweron
  GSMЭջڳ翪δGSMЭջ״̬£ûʱô˽ӿGSMЭջ
  
ֵ
]]
function poweron()
	rtos.poweron(1)
end

--[[
setworkmode
  ùģʽ
  
		vģʽ
ֵɹtrue򷵻nil
]]
function setworkmode(v)
	if workmode~=v and (v==SIMPLE_MODE or v==FULL_MODE) then
		workmode = v
		--һģʽ仯ڲϢ"SYS_WORKMODE_IND"
		dispatch("SYS_WORKMODE_IND")
		return true
	end
end

--[[
getworkmode
  ȡģʽ
  
ֵǰģʽ
]]
function getworkmode()
	return workmode
end

--[[
opntrace
  ߹رprintĴӡ
  
		vfalsenilΪرգΪ
		uartidLuatraceĶ˿ڣnilʾhostڣ1ʾuart1,2ʾuart2
		baudratenumberͣuartidΪnilʱ˲壬ʾʣĬ115200
				  ֧1200,2400,4800,9600,14400,19200,28800,38400,57600,76800,115200,230400,460800,576000,921600,1152000,4000000
ֵ
]]
function opntrace(v,uartid,baudrate)
	if uartid then
		if v then
			uart.setup(uartid,baudrate or 115200,8,uart.PAR_NONE,uart.STOP_1)
		else
			uart.close(uartid)
		end
	end
	rtos.set_trace(v and 1 or 0,uartid)
end

--app洢
local apps = {}

--[[
regapp
  עapp
  ɱappĲʽ
		Ժʽעappregapp(fncname,"MSG1","MSG2","MSG3")
		tableʽעappregapp({MSG1=fnc1,MSG2=fnc2,MSG3=fnc3})
ֵ
]]
function regapp(...)
	local app = arg[1]
	--tableʽ
	if type(app) == "table" then
	--ʽ
	elseif type(app) == "function" then
		app = {procer = arg[1],unpack(arg,2,arg.n)}
	else
		error("unknown app type "..type(app),2)
	end
	--һappڲϢ
	dispatch("SYS_ADD_APP",app)
	return app
end

--[[
deregapp
  עapp
  
		idappididַʽһǺһtable
ֵ
]]
function deregapp(id)
	--һƳappڲϢ
	dispatch("SYS_REMOVE_APP",id)
end


--[[
addapp
  app
  
		appĳappʽ
		     Ժʽעappregapp(fncname,"MSG1","MSG2","MSG3"),ʽΪ{procer=arg[1],"MSG1","MSG2","MSG3"}
			 tableʽעappregapp({MSG1=fnc1,MSG2=fnc2,MSG3=fnc3}),ʽΪ{MSG1=fnc1,MSG2=fnc2,MSG3=fnc3}
ֵ
]] 
local function addapp(app)
	-- β
	table.insert(apps,#apps+1,app)
end

--[[
removeapp
  Ƴapp
  
		idappididַʽһǺһtable
ֵ
]] 
local function removeapp(id)
	--app
	for k,v in ipairs(apps) do
		--appidǺ
		if type(id) == "function" then
			if v.procer == id then
				table.remove(apps,k)
				return
			end
		--appidtable
		elseif v == id then
			table.remove(apps,k)
			return
		end
	end
end

--[[
callapp
  ڲϢ
		ͨÿappд
  
		msgϢ
ֵ
]] 
local function callapp(msg)
	local id = msg[1]
	--appϢ
	if id == "SYS_ADD_APP" then
		addapp(unpack(msg,2,#msg))
	--ƳappϢ
	elseif id == "SYS_REMOVE_APP" then
		removeapp(unpack(msg,2,#msg))
	else
		local app
		--app
		for i=#apps,1,-1 do
			app = apps[i]
			--ע᷽ʽapp,Ϣid֪ͨ
			if app.procer then 
				for _,v in ipairs(app) do
					if v == id then
						--ϢĴûзtrueϢڽһֱapp
						if app.procer(unpack(msg)) ~= true then
							return
						end
					end
				end
			--tableע᷽ʽapp,Ϣid֪ͨ
			elseif app[id] then 
				--ϢĴûзtrueϢڽһֱapp
				if app[id](unpack(msg,2,#msg)) ~= true then
					return
				end
			end
		end
	end
end

--ڲϢ
local qmsg = {}

--[[
dispatch
  ڲϢ洢ڲϢ
  ɱûԶ
ֵ
]] 
function dispatch(...)
	table.insert(qmsg,arg)
end

--[[
getmsg
  ȡڲϢ
  
ֵڲϢеĵһϢ򷵻nil
]] 
local function getmsg()
	if #qmsg == 0 then
		return nil
	end

	return table.remove(qmsg,1)
end

--ˢڲϢ
local refreshmsg = {"MMI_REFRESH_IND"}

--[[
runqmsg
  ڲϢ
  
ֵ
]] 
local function runqmsg()
	local inmsg

	while true do
		--ȡڲϢ
		inmsg = getmsg()
		--ڲϢΪ
		if inmsg == nil then
			--Ҫˢ½
			if refreshflag == true then
				refreshflag = false
				--һˢڲϢ
				inmsg = refreshmsg
			else
				break
			end
		end
		--ڲϢ
		callapp(inmsg)
	end
end

--ʱϢϢⲿϢAT⴮ݽϢƵϢϢϢȣĴ
local handlers = {}
base.setmetatable(handlers,{__index = function() return function() end end,})

--[[
regmsg
  עᡰʱϢϢⲿϢAT⴮ݽϢƵϢϢϢȣĴ
  
		idϢid
		fncϢ
ֵ
]] 
function regmsg(id,handler)
	handlers[id] = handler
end

--ڵݽմ
local uartprocs = {}

--ڵݷ֪ͨ
local uartxprocs = {}

--[[
reguart
  עڵݽմ
  
		idںţ1ʾUART12ʾUART2
		fncݽմ
		clearRcvBufǷյǰջݣtrueʾգfalsenilʾ	
ֵ
]] 
function reguart(id,fnc,clearRcvBuf)
	uartprocs[id] = fnc
	if clearRcvBuf and uart.clear then uart.clear(id,uart.RECV_BUF) end
end

--[[
reguartx
  עڵݷɴ
  
		idںţ1ʾUART12ʾUART2
		fncuart.writeӿڷݣݷɺĻص
ֵ
]] 
function reguartx(id,fnc)
	uartxprocs[id] = fnc
end

function setrestore(restoreFlag)
	sRestoreFlag = restoreFlag
end

--[[
setrestart(棺˽ӿֻupdate.luadbg.luaaliyuniotota.luaãطҪʹ)
  Ƿű쳣ʱ  űsys.restartӿʱ
  
		flgtrue಻
		tag1241ʾupdate2ʾdbg4ʾaliyuniotota
ֵ
]] 
function setrestart(flg,tag)
	if flg then
		if bit.band(restartflg,tag)~=0 then restartflg = restartflg-tag end
	else
		if bit.band(restartflg,tag)==0 then restartflg = restartflg+tag end
	end
	if flg and restartflg==0 and restartpending then restart("restartpending") end
end

local msg,msgpara,msgpara2
function saferun()
	--while true do
		--ڲϢ
		runqmsg()
		--ȡⲿϢ
		msg,msgpara,msgpara2 = rtos.receive(rtos.INF_TIMEOUT)

		--صΪ0%ûӦýűûж塰͵ػ򡱣ûԶػʱ		
		if --[[not lprfun and ]]not lpring and type(msg) == "table" and msg.id == rtos.MSG_PMD and msg.level == 0 then
			--Զػʱ60ػ
			lpring = true
			timer_start(rtos.poweroff,60000,"r1")
		end

		--ⲿϢΪtable
		if type(msg) == "table" then
			--ʱϢ
			if msg.id == rtos.MSG_TIMER then
				timerfnc(msg.timer_id)
			--AT⴮ݽϢ
			elseif msg.id == rtos.MSG_UART_RXDATA and msg.uart_id == uart.ATC then
				handlers.atc()
			else
				--ݽϢ
				if msg.id == rtos.MSG_UART_RXDATA then
					if uartprocs[msg.uart_id] ~= nil then
						uartprocs[msg.uart_id]()
					else
						handlers[msg.id](msg)
					end
				--ڷϢ
				elseif msg.id == rtos.MSG_UART_TX_DONE then
					if uartxprocs[msgpara] then
						uartxprocs[msgpara]()				
					end
				--ϢƵϢϢϢȣ
				else
					handlers[msg.id](msg)
				end
			end
		--ⲿϢtable
		else
			--ʱϢ
			if msg == rtos.MSG_TIMER then
				timerfnc(msgpara)
			--ݽϢ
			elseif msg == rtos.MSG_UART_RXDATA then
				--AT⴮
				if msgpara == uart.ATC then
					handlers.atc()
				--
				else
					if uartprocs[msgpara] ~= nil then
						uartprocs[msgpara](msgpara,msgpara2)
					else
						handlers[msg](msg,msgpara)
					end
				end
			--ڷϢ
			elseif msg == rtos.MSG_UART_TX_DONE then
				if uartxprocs[msgpara] then
					uartxprocs[msgpara]()				
				end
			else
				handlers[msg](msg)
			end
		end
		--ӡluaűռõڴ棬λKֽ
		--print("mem:",base.collectgarbage("count"))
	--end
end

--[[
run
  luaӦóп
  
ֵ

пܻϢƣĿǰһϢڲϢⲿϢ
ڲϢluaűñļdispatchӿڲϢϢ洢qmsg
ⲿϢײcoreϢluaűͨrtos.receiveӿڶȡЩⲿϢ
]] 
function run()
	local status,err
	while true do
		if luarunerr==2 or restartflg==0 then
			if sRestoreFlag then
				saferun()
			else
				status,err = pcall(saferun)
				if not status then saferestart(err or "") rtos.restart() end
			end
		else		
			status,err = pcall(saferun)
			--г
			if not status then
				print("run",status,err)
				luarunerr = 1
				saferestart(err or "")			
			end
		end
	end
end
