--[[
ģƣԶ
ģ鹦ܣֻÿοʱ°汾libӦýűԶ
ģ޸ʱ䣺2017.02.09
]]

--ģ,
local base = _G
local string = require"string"
local io = require"io"
local os = require"os"
local rtos = require"rtos"
local sys  = require"sys"
local link = require"link"
local misc = require"misc"
local common = require"common"
module(...)

--سõȫֺ
local print = base.print
local send = link.send
local dispatch = sys.dispatch

--Զģʽmain.luaУUPDMODEδõĻĬΪ0
--0ԶģʽűºԶ
--1ûԶģʽ̨°汾һϢûӦýűǷ
local updmode = base.UPDMODE or 0

--PROTOCOLЭ飬ֻ֧TCPUDP
--SERVER,PORTΪַͶ˿
local PROTOCOL,SERVER,PORT = "UDP","firmware.openluat.com",12410
--ǷʹûԶ
local usersvr
--·
local UPDATEPACK = "/luazip/update.bin"

-- GETȴʱ
local CMD_GET_TIMEOUT = 10000
-- (ID߳Ȳƥ) һʱ»ȡ
local ERROR_PACK_TIMEOUT = 10000
-- ÿGETԴ
local CMD_GET_RETRY_TIMES = 5
--socket id
local lid,updsuc
--öʱʱڣλ룬0ʾرնʱ
local period = 0
--״̬״̬
--IDLE״̬
--CHECKѯǷ°汾״̬
--UPDATE״̬
local state = "IDLE"
--projectidĿʶid,Լά
--totalǰĸļΪ10235ֽڣtotal=(int)((10235+1022)/1023)=11;ļΪ10230ֽڣtotal=(int)((10230+1022)/1023)=10
--lastһֽļΪ10235ֽڣlast=10235%1023=5;ļΪ10230ֽڣlast=1023
local projectid,total,last
--packidǰ
--getretriesȡÿѾԵĴ
local packid,getretries = 1,0

--ʱģ֧ϵͳʱ书ܣҪصǰʱ
timezone = nil
BEIJING_TIME = 8
GREENWICH_TIME = 0

--[[
print
  ӡӿڣļедӡupdateǰ׺
  
ֵ
]]
local function print(...)
	base.print("update",...)
end

--[[
save
  ݰļ
  
		dataݰ
ֵ
]]
local function save(data)
	--ǵһ򸲸Ǳ棻׷ӱ
	local mode = packid == 1 and "wb" or "a+"
	--ļ
	local f = io.open(UPDATEPACK,mode)

	if f == nil then
		print("save:file nil")
		return
	end
	--дļ
	f:write(data)
	f:close()
end

--[[
retry
  еԶ
  
		paramΪSTOPֹͣԣִ
ֵ
]]
local function retry(param)
	print("retry",param,state,getretries)
	--״̬ѽֱ˳
	if state~="CONNECT" and state~="UPDATE" and state~="CHECK" then
		return
	end
	--ֹͣ
	if param == "STOP" then
		getretries = 0
		sys.timer_stop(retry)
		return
	end
	--ݴERROR_PACK_TIMEOUTԵǰ
	if param == "ERROR_PACK" then
		sys.timer_start(retry,ERROR_PACK_TIMEOUT)
		return
	end
	--Դ1
	getretries = getretries + 1
	if getretries < CMD_GET_RETRY_TIMES then
		-- δԴ,Իȡ
		if state == "CONNECT" then
			link.close(lid)
			lid = nil
			connect()
		elseif state == "UPDATE" then
			reqget(packid)
		else
			reqcheck()
		end
	else
		-- Դ,ʧ
		upend(false)
	end
end

--[[
reqget
  ͡ȡindexݡ
  
		index1ʼ
ֵ
]]
function reqget(index)
	send(lid,string.format("%sGet%d,%d",
							usersvr and "" or string.format("0,%s,%s,%s,%s,%s,",base.PRODUCT_KEY,misc.getimei(),misc.isnvalid() and misc.getsn() or "",base.PROJECT.."_"..sys.getcorever(),base.VERSION),
							index,
							projectid))
	--CMD_GET_TIMEOUTԡʱ
	sys.timer_start(retry,CMD_GET_TIMEOUT)
end

--[[
getpack
  ӷյһ
  
		data
ֵ
]]
local function getpack(data)
	--жϰǷȷ
	local len = string.len(data)
	if (packid < total and len ~= 1024) or (packid >= total and (len - 2) ~= last) then
		print("getpack:len not match",packid,len,last)
		retry("ERROR_PACK")
		return
	end

	--жϰǷȷ
	local id = string.byte(data,1)*256+string.byte(data,2)
	if id ~= packid then
		print("getpack:packid not match",id,packid)
		retry("ERROR_PACK")
		return
	end

	--ֹͣ
	retry("STOP")

	--
	save(string.sub(data,3,-1))
	--ûԶģʽһڲϢUP_PROGRESS_INDʾ
	if updmode == 1 then
		dispatch("UP_EVT","UP_PROGRESS_IND",(packid*100-((packid*100)%total))/total)
	end

	--ȡһ
	if packid == total then
		upend(true)
	else
		packid = packid + 1
		reqget(packid)
	end
end

--[[
upbegin
  ·°汾Ϣ
  
		data°汾Ϣ
ֵ
]]
function upbegin(data)
	local p1,p2,p3 = string.match(data,"LUAUPDATE,(%d+),(%d+),(%d+)")
	--̨άĿidĸһֽ
	p1,p2,p3 = base.tonumber(p1),base.tonumber(p2),base.tonumber(p3)
	--ʽȷ
	if p1 and p2 and p3 then
		projectid,total,last = p1,p2,p3
		--Դ0
		getretries = 0
		--Ϊ״̬
		state = "UPDATE"
		--ӵ1ʼ
		packid = 1
		--󣬻ȡ1
		reqget(packid)
	--ʽ
	else
		upend(false)
	end
end

--[[
upend
  
  
		succtrueΪɹΪʧ
ֵ
]]
function upend(succ)
	print("upend",succ)
	if not succ then os.remove(UPDATEPACK) end
	updsuc = succ
	local tmpsta = state
	state = "IDLE"
	--ֹͣԶʱ
	sys.timer_stop(retry)
	--Ͽ
	link.close(lid)
	lid = nil
	getretries = 0
	sys.setrestart(true,1)
	sys.timer_stop(sys.setrestart,true,1)
	--ɹԶģʽ
	if succ == true and updmode == 0 then
		sys.restart("update.upend")
	end
	--ԶģʽһڲϢUP_END_INDʾԼ
	if updmode == 1 and tmpsta ~= "IDLE" then
		dispatch("UP_EVT","UP_END_IND",succ)
	end
	--һڲϢUPDATE_END_INDĿǰģʽʹ
	dispatch("UPDATE_END_IND")
	if period~=0 then sys.timer_start(connect,period*1000,"period") end
end

--[[
reqcheck
  ͡Ƿ°汾ݵ
  
ֵ
]]
function reqcheck()
	print("reqcheck",usersvr)
	os.remove(UPDATEPACK)
	state = "CHECK"
	if usersvr then
		send(lid,string.format("%s,%s,%s",misc.getimei(),base.PROJECT.."_"..sys.getcorever(),base.VERSION))
	else
		send(lid,string.format("0,%s,%s,%s,%s,%s",base.PRODUCT_KEY,misc.getimei(),misc.isnvalid() and misc.getsn() or "",base.PROJECT.."_"..sys.getcorever(),base.VERSION))
	end
	sys.timer_start(retry,CMD_GET_TIMEOUT)
end

--[[
nofity
  socket״̬Ĵ
  
        idsocket idԺԲ
        evtϢ¼
		val Ϣ¼
ֵ
]]
local function nofity(id,evt,val)
	--ӽ
	if evt == "CONNECT" then
		state = "CONNECT"
		--һڲϢUPDATE_BEGIN_INDĿǰģʽʹ
		dispatch("UPDATE_BEGIN_IND")
		--ӳɹ
		if val == "CONNECT OK" then
			reqcheck()
		--ʧ
		else
			sys.timer_start(retry,CMD_GET_TIMEOUT)
		end
	--ӱϿ
	elseif evt == "STATE" and val == "CLOSED" then		 
		upend(false)
	end
end

--·°汾ϢԶģʽʹ
local chkrspdat
--[[
upselcb
  Զģʽ£ûѡǷĻص
  
        selǷtrueΪΪ
ֵ
]]
local upselcb = function(sel)
	--
	if sel then
		upbegin(chkrspdat)
	--
	else
		link.close(lid)
		lid = nil
		dispatch("UPDATE_END_IND")
	end
end

--[[
recv
  socketݵĴ
  
        id socket idԺԲ
        dataյ
ֵ
]]
local function recv(id,data)
	--ֹͣԶʱ
	sys.timer_stop(retry)
	--ѯǷ°汾״̬
	if state == "CHECK" then
		--°汾
		if string.find(data,"LUAUPDATE") == 1 then
			--Զģʽ
			if updmode == 0 then
				upbegin(data)
			--Զģʽ
			elseif updmode == 1 then
				chkrspdat = data
				dispatch("UP_EVT","NEW_VER_IND",upselcb)
			else
				upend(false)
			end
		--û°汾
		else
			upend(false)
		end
		--ûӦýűеsettimezoneӿ
		if timezone then
			local clk,a,b = {}
			a,b,clk.year,clk.month,clk.day,clk.hour,clk.min,clk.sec = string.find(data,"(%d+)%-(%d+)%-(%d+) *(%d%d):(%d%d):(%d%d)")
			--ȷʱʽ
			if a and b then
				--ϵͳʱ
				clk = common.transftimezone(clk.year,clk.month,clk.day,clk.hour,clk.min,clk.sec,BEIJING_TIME,timezone)
				misc.setclock(clk)
			end
		end
	--С״̬
	elseif state == "UPDATE" then
		if data == "ERR" then
			upend(false)
		else
			getpack(data)
		end
	else
		upend(false)
	end	
end

--[[
settimezone
  ϵͳʱʱ
  
        zone ʱĿǰָ֧ʱͱʱ䣬BEIJING_TIMEGREENWICH_TIME
ֵ
]]
function settimezone(zone)
	timezone = zone
end

function connect()
	print("connect",lid,updsuc)
	if not lid and not updsuc then
		lid = link.open(nofity,recv,"update")
		link.connect(lid,PROTOCOL,SERVER,PORT)
	end
end

local function defaultbgn()
	print("defaultbgn",usersvr)
	if not usersvr then
		base.assert(base.PRODUCT_KEY and base.PROJECT and base.VERSION,"undefine PRODUCT_KEY or PROJECT or VERSION in main.lua")
		base.assert(not string.match(base.PROJECT,","),"PROJECT in main.lua format error")
		base.assert(string.match(base.VERSION,"%d%.%d%.%d") and string.len(base.VERSION)==5,"VERSION in main.lua format error")
		connect()
	end
end

--[[
setup
  ÷ĴЭ顢ַͶ˿
  
        prot Э飬֧TCPUDP
		serverַ
		port˿
ֵ
]]
function setup(prot,server,port)
	if prot and server and port then
		PROTOCOL,SERVER,PORT = prot,server,port
		usersvr = true
		base.assert(base.PROJECT and base.VERSION,"undefine PROJECT or VERSION in main.lua")		
		connect()
	end
end

--[[
setperiod
  öʱ
  
        prdnumberͣʱڣλ룻0ʾرնʱܣֵҪڵ60
ֵ
]]
function setperiod(prd)
	base.assert(prd==0 or prd>=60,"setperiod prd error")
	print("setperiod",prd)
	period = prd
	if prd==0 then
		sys.timer_stop(connect,"period")
	else
		sys.timer_start(connect,prd*1000,"period")
	end
end

--[[
request
  ʵʱһ
  
ֵ
]]
function request()
	print("request")
	connect()
end

sys.timer_start(defaultbgn,10000)
sys.setrestart(false,1)
sys.timer_start(sys.setrestart,300000,true,1)
