
-- create the module's table
local fwupdate = {}

-- import required modules
local dict = require "scripts.app.dict"
local help = require "scripts.app.help"

-- file constants

-- local functions


local function erase_main()

	--dict.fwupdate("ERASE_1KB_PAGE", 2)	--page 0 & 1 (first 2KByte) are forbidden
	--dict.fwupdate("ERASE_1KB_PAGE", 3)	--this is redundant for RB (aligns C6 to RB when done with above)
	--dict.fwupdate("ERASE_1KB_PAGE", 4)	--0x0800_1000 - 0x0800_17FF
	--dict.fwupdate("ERASE_1KB_PAGE", 5)	--redundant RB
	--dict.fwupdate("ERASE_1KB_PAGE", 6)	--0x0800_1800 - 0x0800_1FFF
	--dict.fwupdate("ERASE_1KB_PAGE", 7)
	--dict.fwupdate("ERASE_1KB_PAGE", 8)	--0x0800_2000 - 0x0800_27FF
	--dict.fwupdate("ERASE_1KB_PAGE", 9)
	
	curpage = 2	--skip the first pages
	rv = dict.fwupdate("GET_FLASH_ADDR") 
	print("flash addr:", string.format("%X", rv) )

	while (curpage<32) do
--	while (curpage<128) do	--RB has 128KB but last 96KB isn't used (yet)
		if(curpage%4 ==0) then
			print("erasing page:", curpage)
		end
		dict.fwupdate("ERASE_1KB_PAGE", curpage)

		--rv = dict.fwupdate("GET_FLASH_ADDR") 
		--print("flash addr:", string.format("%X", rv) )

		curpage = curpage+1
	end

end

--skip is used because there is a ram pointer that often varies between builds
--we're never going back to main so this mismatch is allowed
local function update_firmware(newbuild, skip, forceup)

	local error = false


	--open new file first, don't bother continuing if can't find it.
	file = assert(io.open(newbuild, "rb"))

	--TODO read the fwupdater & app version from the provided file
	--compare to current device and determine if they're compatible
	--test let's tinker with SRAM
	dict.bootload("SET_PTR_HI", 0x0800)
	dict.bootload("SET_PTR_LO", 0x0800)	--application version 0x08000800 "AV00"
	local av = dict.bootload("RD_PTR_OFFSET")
	local ver = dict.bootload("RD_PTR_OFFSET",1)
	local avstring = string.format("%s%s%s%s", string.char(av&0x00FF), string.char(av>>8),
					string.char(ver&0x00FF), string.char(ver>>8))

	if avstring == "AV00" then
		print("application version:", avstring)
	else
		print("app version", avstring, "unknown, may need to update to firmware v2.3 or later using STmicro dfuse")
	end


	--verify the first 2KByte match, don't continue if not..
	--use the bootload dictionary to complete this
	--want to wait to enter firmware updater
	
	--set bootloader 16bit pointer to start of flash
	dict.bootload("SET_PTR_HI", 0x0800)
	dict.bootload("SET_PTR_LO", 0x0000)
	local offset = 0 --first read has no offset
	
	--advance the file past first 2KByte
	local buffsize = 1
	local byte, data
	local byte_num = 0
	local readdata
	local data_l

	print("Verifing first 2KByte of updater..")
	while byte_num < (2*1024) do

		--read next byte from the file and convert to binary
		--gotta be a better way to read a half word (16bits) at a time but don't care right now...
		byte_str = file:read(buffsize)
		if byte_str then
			data_l = string.unpack("B", byte_str, 1)
		else
			--should only have to make this check for lower byte
			--binary file should be even
			print("There's a problem, file provided is smaller than 2KB fwupdater..")
			--TODO test this
			error = true 
			break
		end
		byte_str = file:read(buffsize)
		data = string.unpack("B", byte_str, 1)
		data = (data<<8)+data_l
	
		if (true) then
			--both these options work, but the later is limited to reading 64KByte space
			readdata = dict.bootload("RD_PTR_OFF_UP", offset) 
			--readdata = dict.bootload("RD_PTR_OFFSET", byte_num>>1) --shift by one 16bit read 

		--	print("read data:", string.format("%X", readdata) )
			if readdata ~= data then
				print("\n\nUnable to verify byte number", help.hex(byte_num), 
					" to flash expected:", help.hex(data), "was:", help.hex(readdata))
				if forceup then
					print("continuing anyway because force update was set...")
				elseif byte_num == skip then
					print("there was an expected mismatch at byte:", help.hex(byte_num))
				else
					print("\n\nPROBLEM! with firmware updater verification exiting")
					print("exiting because it's not safe to proceed...")
					print("no changes to device flash were made\n\n")
					error = true
					break
				end
			--else
			--	print("verified byte number", help.hex(byte_num), 
			--		" of flash ", help.hex(data), help.hex(readdata))
			end
		end

		offset = 1 --this is zero for first byte, but one for all others..
		byte_num = byte_num + 2
	end

	if (not error) then
		print("\nSuccessfully verified first 2KB of provided file")
		print("matches the device's firmware updater section\n")
	end

	--enter fwupdate mode
	dict.bootload("PREP_FWUPDATE")	

	--now the device will only respond to FWUPDATE dictionary commands
	
	--erase 30KByte of application code
	if (not error) then
		erase_main()
	end

	--Set FLASH->AR to beginging of application section
	--this can be done be re-erasing it..
	--maybe we could have skipped page 2 in erase_main
	--or have erase_main count down..
	if (not error) then
		dict.fwupdate("ERASE_1KB_PAGE", 2)
		print("flash addr:", help.hex(dict.fwupdate("GET_FLASH_ADDR")))
		print("\n");
	end
	

	offset = 0 --first write has no offset

	if (not error) then
		print("Updating device main application flash..")
		while byte_num < (32*1024) do

			--read next byte from the file and convert to binary
			--gotta be a better way to read a half word (16bits) at a time but don't care right now...
			byte_str = file:read(buffsize)
			if byte_str then
				data_l = string.unpack("B", byte_str, 1)
			else
				--should only have to make this check for lower byte
				--binary file should be even
				print("end of file")
				break
			end
			byte_str = file:read(buffsize)
			data = string.unpack("B", byte_str, 1)
			data = (data<<8)+data_l
		
			if( (byte_num % (4*1024)) == 0 ) then
				print("flashing KB", byte_num/1024)
			end

			--print("writting:", string.format("%X", data), "addr:", string.format("%X", byte_num))

			--write the data
			dict.fwupdate("WR_HWORD", data, offset)

			if (true) then
				readdata = dict.fwupdate("READ_FLASH", byte_num, 0x00) 
			--	print("read data:", string.format("%X", readdata) )
				if readdata ~= data then
					print("\n\nERROR!!!! flashing byte number", help.hex(byte_num), 
						" to flash expected:", help.hex(data), "was:", help.hex(readdata))
					print("exiting before causing more damage...\n\n")
					error = true
					break
				--else
				--	print("verified byte number", help.hex(byte_num), 
				--		" to flash ", help.hex(data), help.hex(readdata))
				end
			end

			offset = 1 --this is zero for first write, but one for all others..
			byte_num = byte_num + 2
		end
	end
	
	if (not error) then
		print("\nSuccessfully updated the device firmware!")
	end

	--close file
	assert(file:close())

	print("\n\n DONE, Reseting device \n\n IGNORE the errors that comes next.. \n\n")

	--TODO maybe don't reset if we got an error, allow for correction while fwupdate still has control..?
	dict.fwupdate("RESET_DEVICE")

	--write build to flash

	print("updated")	--this doesn't print because reset errored us out..
end

-- global variables so other modules can use them


-- call functions desired to run when script is called/imported


-- functions other modules are able to call
fwupdate.update_firmware = update_firmware

-- return the module's table
return fwupdate
