local cmds = require('commands')
local getopt = require('getopt')
local bin = require('bin')
local lib14a = require('read14a')
local utils = require('utils')

example = [[
    -- generate commands
    1. script run formatMifare

    -- generate command, replacing key with new key.
    2. script run formatMifare -k aabbccddeeff -n 112233445566 -a FF0780

    -- generate commands and execute them against card.
    3. script run formatMifare -x
]]
copyright = ''
version = ''
author = 'Iceman'
usage = [[
   script run formatMifare -k <key> -n <key> -a <access> -x
]]
desc = [[
This script will generate 'hf mf wrbl' commands for each block to format a Mifare card.

Alla datablocks gets 0x00
As default the script sets the keys A/B to 0xFFFFFFFFFFFF
and the access bytes will become 0x78,0x77,0x88
The GDB will become 0x00

The script will skip the manufactoring block 0.


Arguments:
    -h             - this help
    -k <key>       - the current six byte key with write access
    -n <key>       - the new key that will be written to the card
    -a <access>    - the new access bytes that will be written to the card
    -x             - execute the commands aswell.
]]
local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
local DEBUG = true -- the debug flag
local CmdString = 'hf mf wrbl %d B %s %s'
local numBlocks = 64
local numSectors = 16
---
-- A debug printout-function
function dbg(args)
    if not DEBUG then
        return
    end

    if type(args) == "table" then
        local i = 1
        while result[i] do
            dbg(result[i])
            i = i+1
        end
    else
        print("###", args)
    end
end
---
-- This is only meant to be used when errors occur
function oops(err)
    print("ERROR: ",err)
end
---
-- Usage help
function help()
    print(copyright)
    print(author)
    print(version)
    print(desc)
    print("Example usage")
    print(example)
end
--
-- Exit message
function ExitMsg(msg)
    print( string.rep('--',20) )
    print( string.rep('--',20) )
    print(msg)
    print()
end
--
-- Read information from a card
function GetCardInfo()
    result, err = lib14a.read(false, true)
    if not result then
        print(err)
        return
    end
    print(("Found:  %s"):format(result.name))

    core.clearCommandBuffer()

    if 0x18 == result.sak then -- NXP MIFARE Classic 4k | Plus 4k
        -- IFARE Classic 4K offers 4096 bytes split into forty sectors,
        -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors.
        numSectors = 40
    elseif 0x08 == result.sak then  -- NXP MIFARE CLASSIC 1k | Plus 2k
        -- 1K offers 1024 bytes of data storage, split into 16 sector
        numSectors = 16
    elseif 0x09 == result.sak then  -- NXP MIFARE Mini 0.3k
        -- MIFARE Classic mini offers 320 bytes split into five sectors.
        numSectors = 5
    elseif  0x10 == result.sak then -- NXP MIFARE Plus 2k
        numSectors = 32
    elseif  0x01 == result.sak then -- NXP MIFARE TNP3xxx 1K
        numSectors = 16
    else
        print("I don't know how many sectors there are on this type of card, defaulting to 16")
    end
    --[[
     The mifare Classic 1k card has 16 sectors of 4 data blocks each.
     The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining
     8 sectors consist of 16 data blocks.
    --]]

    -- Defaults to 16 * 4 = 64  - 1 = 63
    numBlocks = numSectors * 4 - 1

    if numSectors > 32 then
        numBlocks = 32*4+ (numSectors-32)*16 -1
    end

end

local function main(args)

    print( string.rep('--',20) )
    print( string.rep('--',20) )
    print()

    local OldKey, NewKey, Accessbytes
    local x = false

    -- Arguments for the script
    for o, a in getopt.getopt(args, 'hk:n:a:x') do
        if o == "h" then return help() end
        if o == "k" then OldKey = a end
        if o == "n" then NewKey = a end
        if o == "a" then Accessbytes = a end
        if o == "x" then x = true end
    end

    -- validate input args.
    OldKey =  OldKey or 'FFFFFFFFFFFF'
    if #(OldKey) ~= 12 then
        return oops( string.format('Wrong length of write key (was %d) expected 12', #OldKey))
    end

    NewKey =  NewKey or 'FFFFFFFFFFFF'
    if #(NewKey) ~= 12 then
        return oops( string.format('Wrong length of new key (was %d) expected 12', #NewKey))
    end

    --Accessbytes =  Accessbytes or '787788'
    Accessbytes =  Accessbytes or 'FF0780'
    if #(Accessbytes) ~= 6 then
        return oops( string.format('Wrong length of accessbytes (was %d) expected 12', #Accessbytes))
    end

    GetCardInfo()

    -- Show info
    print( string.format('Estimating number of blocks: %d', numBlocks))
    print( string.format('Old key:    %s', OldKey))
    print( string.format('New key:    %s', NewKey))
    print( string.format('New Access: %s', Accessbytes))
    print( string.rep('--',20) )

        -- Set new block data
    local EMPTY_BL = string.rep('00',16)
    local EMPTY_SECTORTRAIL = string.format('%s%s%s%s',NewKey,Accessbytes,'00',NewKey)

    dbg( string.format('New sector-trailer : %s',EMPTY_SECTORTRAIL))
    dbg( string.format('New emptyblock: %s',EMPTY_BL))
    dbg('')

    if x then
        print('[Warning] you have used the EXECUTE parameter, which means this will run these commands against card.')
    end
    -- Ask
    local dialogResult = utils.confirm("Do you want to erase this card")
    if dialogResult == false then
        return ExitMsg('Quiting it is then. Your wish is my command...')
    end

    print( string.rep('--',20) )

    -- main loop
    for block=0,numBlocks,1 do

        local reminder = (block+1) % 4
        local cmd
        if reminder == 0 then
            cmd = CmdString:format(block, OldKey , EMPTY_SECTORTRAIL)
        else
            cmd = CmdString:format(block, OldKey , EMPTY_BL)
        end

        if block ~= 0 then
            print(cmd)
            if x then core.console(cmd) end
        end

        if core.ukbhit() then
            print("aborted by user")
            break
        end
    end
end

main(args)