local cmds = require('commands')
local getopt = require('getopt')
local lib14a = require('read14a')
local utils = require('utils')
example = [[
	script run ufodump
	script run ufodump -b 10
]]
author = "Iceman"
desc =
[[
This is a script that reads AZTEK iso14443a tags.
It starts from block 0,  and ends at default block 20.  Use 'b' to say different endblock.

xor:  the first three block (0,1,2) is not XORED.  The rest seems to be xored.

Arguments:
      h   this helptext
      b   endblock in decimal (1-255,  default 20)
]]

-- Some globals
local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
local DEBUG = false -- the debug flag
--- 
-- A debug printout-function
local function dbg(args)
	if DEBUG then
		print("###", args)
	end
end 
--- 
-- This is only meant to be used when errors occur
local function oops(err)
	print("ERROR: ",err)
	core.clearCommandBuffer()	
end
--- 
-- Usage help
local function help()
	print(desc)
	print("Example usage")
	print(example)
end
--
-- writes data to ascii textfile.
function writeDumpFile(uid, blockData)
		local destination = string.format("%s.eml", uid)
		local file = io.open(destination, "w")
		if file == nil then 
			return nil, string.format("Could not write to file %s", destination)
		end
		local rowlen = string.len(blockData[1])

		for i,block in ipairs(blockData) do
			if rowlen ~= string.len(block) then
				print(string.format("WARNING: Dumpdata seems corrupted, line %d was not the same length as line 1",i))
			end
			file:write(block.."\n")
		end
		file:close()	
		return destination
end
--
--- Picks out and displays the data read from a tag
-- Specifically, takes a usb packet, converts to a Command
-- (as in commands.lua), takes the data-array and 
-- reads the number of bytes specified in arg1 (arg0 in c-struct)
-- and displays the data
-- @blockno just to print which block the data belong to
-- @param usbpacket the data received from the device
function showdata(blockno, data)
	local xorkey = '55AA55AA55AA55AA6262'
	local s = data.." | "
	local dex = ''
	local rs 
	for i = 1, 20-4, 4 do
		local item = string.sub(data, i, i+3)
		local xor = string.sub(xorkey, i, i+3)

		if blockno > 2 then
			rs = bit32.bxor(tonumber(item,16) , tonumber(xor,16))
		else
			rs = tonumber(item, 16)
		end
		dex = (dex..'%04X'):format(rs)
	end 
	s = s..dex.." | "
	print( (" %02d | %s"):format(blockno,s))
end
--
-- Send a "raw" iso14443a package, ie "hf 14a raw" command
function sendRaw(rawdata, options)
	--print(">> ", rawdata)	
	local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + lib14a.ISO14A_COMMAND.ISO14A_RAW + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + lib14a.ISO14A_COMMAND.ISO14A_NO_RATS
	local command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, 
									arg1 = flags, -- Send raw 
									-- arg2 contains the length, which is half the length 
									-- of the ASCII-string rawdata
									arg2 = string.len(rawdata)/2, 
									data = rawdata}
	return lib14a.sendToDevice(command, options.ignore_response) 
end
--
-- Sends an instruction to do nothing, only disconnect
function disconnect()
	local command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, arg1 = 0,	}
	-- We can ignore the response here, no ACK is returned for this command
	-- Check /armsrc/iso14443a.c, ReaderIso14443a() for details
	return lib14a.sendToDevice(command, true) 	
	--core.console("hf 14a raw -r")	
end			
--- 
-- The main entry point
function main(args)

	local ignore_response = false
	local endblock = 20
	
	-- Read the parameters
	for o, a in getopt.getopt(args, 'hb:') do
		if o == "h" then return help() end
		if o == "b" then endblock = a end		
	end
	endblock = endblock or 20
	
	-- First of all, connect
	info, err = lib14a.read1443a(true, true)
	if err then disconnect() return oops(err) end	
	core.clearCommandBuffer()
	
	local blockData = {}
	
	-- Show tag info
	print(("\nFound Card UID [%s]\n"):format(info.uid))

	print("blk | data             | xored")
	print("----+------------------+-------------------")
	for block = 00, endblock do
		local cmd = string.format("10%02x00", block)
		res, err = sendRaw(cmd , {ignore_response = ignore_response})		
		if err then disconnect() return oops(err) end
		
		local cmd_response = Command.parse(res)
		local len = tonumber(cmd_response.arg1) * 2
		local data = string.sub(tostring(cmd_response.data), 0, len-4)

		showdata(block, data)
		table.insert(blockData, data)
	end 
	print("----+------------------+-------------------")
	disconnect()
	
	local filename, err = writeDumpFile(info.uid, blockData)
	if err then return oops(err) end

	print(string.format("\nDumped data into %s", filename))
end
	
-------------------------
-- 	Testing
-------------------------
function selftest()
	DEBUG = true
	dbg("Performing test")
	main()
	dbg("Tests done")
end
-- Flip the switch here to perform a sanity check. 
-- It read a nonce in two different ways, as specified in the usage-section
if "--test"==args then 
	selftest()
else 
	-- Call the main 
	main(args)
end