local cmds = require('commands')
local hexlify = require('hexlify')
local reader = require('read14a')

local Emulator = {
   _VERSION = 'emulator.lua 0.1.0',
   _DESCRIPTION = 'emulator memory interface',
   BLOCK_SZ = 512,
   BLOCK_COUNT = 512 / 16
}

function Emulator:set_mem (data, clear_first)
   if clear_first then
      -- Clear out the emulator memory first
      local emuMemclearCmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMCLR,
                                         arg1 = 0, arg2 = 0, arg3 = 0}

      local _, err = reader.sendToDevice(emuMemclearCmd)
      if err then
         print('Failed to clear emulator memory:', err)
         return false
      else
         print('Cleared emulator memory')
      end
   end

   -- Can fit 32 16 byte blocks per command (512 total bytes max)
   for i = 0, (data:len() / self.BLOCK_SZ) do
      local cur_out_block = data:sub((i*self.BLOCK_SZ) + 1, (i*self.BLOCK_SZ) + self.BLOCK_SZ)
      print(string.format('Transmission #%u: %u bytes', i, cur_out_block:len()))

      -- arg1: start block number
      -- arg2: block count
      local emuMemsetCmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET,
                                       data = hexlify(cur_out_block),
                                       arg1 = i * self.BLOCK_COUNT,
                                       arg2 = self.BLOCK_COUNT}

      -- Send command and wait for response
      local _, err = reader.sendToDevice(emuMemsetCmd)
      if err then
         print('Failed setting memory', err)
         return false
      end
   end

   print('Emulator memory set')
   return true
end

-- Read <size> bytes from emulator memory
function Emulator:get_mem (size)
   local MAX_BLOCKS = 4
   local result = ''

   -- We can request a maximum of 4 blocks (16 bytes each) per command,
   -- according to mifarecmd.c
   for i = 0, (size / (MAX_BLOCKS * 16)) do
      -- arg1: start block number
      -- arg2: block count (max 4)
      local emuMemGetCmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMGET,
                                       arg1 = i * MAX_BLOCKS,
                                       arg2 = MAX_BLOCKS,
                                       arg3 = 0}

      local response, err = reader.sendToDevice(emuMemGetCmd)
      if err then
         print('Failed getting memory:', err)
         return false
      end

      -- USB data begins after four 64-bit values
      local data_begin = ((64/8) * 4) + 1
      response = string.sub(response, data_begin)

      -- Truncate to the received 16 byte blocks
      response = string.sub(response, 1, 16 * MAX_BLOCKS)

      result = result .. response
   end

   return string.sub(result, 1, size)
end

return Emulator
