/*********************************************************************
 *
 *  $Id: svn_id $
 *
 *  Implements yFindRfidReader(), the high-level API for RfidReader functions
 *
 *  - - - - - - - - - License information: - - - - - - - - -
 *
 *  Copyright (C) 2011 and beyond by Yoctopuce Sarl, Switzerland.
 *
 *  Yoctopuce Sarl (hereafter Licensor) grants to you a perpetual
 *  non-exclusive license to use, modify, copy and integrate this
 *  file into your software for the sole purpose of interfacing
 *  with Yoctopuce products.
 *
 *  You may reproduce and distribute copies of this file in
 *  source or object form, as long as the sole purpose of this
 *  code is to interface with Yoctopuce products. You must retain
 *  this notice in the distributed source file.
 *
 *  You should refer to Yoctopuce General Terms and Conditions
 *  for additional information regarding your rights and
 *  obligations.
 *
 *  THE SOFTWARE AND DOCUMENTATION ARE PROVIDED 'AS IS' WITHOUT
 *  WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
 *  WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS
 *  FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO
 *  EVENT SHALL LICENSOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL,
 *  INDIRECT OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA,
 *  COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR
 *  SERVICES, ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT
 *  LIMITED TO ANY DEFENSE THEREOF), ANY CLAIMS FOR INDEMNITY OR
 *  CONTRIBUTION, OR OTHER SIMILAR COSTS, WHETHER ASSERTED ON THE
 *  BASIS OF CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF
 *  WARRANTY, OR OTHERWISE.
 *
 *********************************************************************/


#define _CRT_SECURE_NO_DEPRECATE //do not use windows secure crt
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#include "yocto_rfidreader.h"
#include "yapi/yjson.h"
#include "yapi/yapi.h"
#define  __FILE_ID__  "rfidreader"

#ifdef YOCTOLIB_NAMESPACE
using namespace YOCTOLIB_NAMESPACE;
#endif

YRfidTagInfo::YRfidTagInfo():
//--- (generated code: YRfidTagInfo initialization)
    _tagType(0)
    ,_nfcType(0)
    ,_size(0)
    ,_usable(0)
    ,_blksize(0)
    ,_fblk(0)
    ,_lblk(0)
//--- (end of generated code: YRfidTagInfo initialization)
{
}

YRfidTagInfo::~YRfidTagInfo()
{
//--- (generated code: YRfidTagInfo cleanup)
//--- (end of generated code: YRfidTagInfo cleanup)
}
//--- (generated code: YRfidTagInfo implementation)
// static attributes


/**
 * Returns the RFID tag identifier.
 *
 * @return a string with the RFID tag identifier.
 */
string YRfidTagInfo::get_tagId(void)
{
    return _tagId;
}

/**
 * Returns the type of the RFID tag, as a numeric constant.
 * (IEC_14443_MIFARE_CLASSIC1K, ...).
 *
 * @return an integer corresponding to the RFID tag type
 */
int YRfidTagInfo::get_tagType(void)
{
    return _tagType;
}

/**
 * Returns the type of the RFID tag, as a string.
 *
 * @return a string corresponding to the RFID tag type
 */
string YRfidTagInfo::get_tagTypeStr(void)
{
    return _typeStr;
}

/**
 * Returns the type of NFC type usable on the RFID tag, between 1 and 5.
 * If no known NFC type is know for the RFID tag, returns zero.
 *
 * @return an integer corresponding to the RFID tag type
 */
int YRfidTagInfo::get_tagNFCtype(void)
{
    return _nfcType;
}

/**
 * Returns the total memory size of the RFID tag, in bytes.
 *
 * @return the total memory size of the RFID tag
 */
int YRfidTagInfo::get_tagMemorySize(void)
{
    return _size;
}

/**
 * Returns the usable storage size of the RFID tag, in bytes.
 *
 * @return the usable storage size of the RFID tag
 */
int YRfidTagInfo::get_tagUsableSize(void)
{
    return _usable;
}

/**
 * Returns the block size of the RFID tag, in bytes.
 *
 * @return the block size of the RFID tag
 */
int YRfidTagInfo::get_tagBlockSize(void)
{
    return _blksize;
}

/**
 * Returns the index of the block available for data storage on the RFID tag.
 * Some tags have special block used to configure the tag behavior, these
 * blocks must be handled with precaution. However, the  block return by
 * get_tagFirstBlock() can be locked, use get_tagLockState()
 * to find out  which block are locked.
 *
 * @return the index of the first usable storage block on the RFID tag
 */
int YRfidTagInfo::get_tagFirstBlock(void)
{
    return _fblk;
}

/**
 * Returns the index of the last last black available for data storage on the RFID tag,
 * However, this block can be locked, use get_tagLockState() to find out
 * which block are locked.
 *
 * @return the index of the last usable storage block on the RFID tag
 */
int YRfidTagInfo::get_tagLastBlock(void)
{
    return _lblk;
}

void YRfidTagInfo::imm_init(string tagId,int tagType,int size,int usable,int blksize,int fblk,int lblk)
{
    string typeStr;
    int nfcType = 0;
    typeStr = "unknown";
    if (tagType == YRfidTagInfo::IEC_15693) {
        typeStr = "IEC 15693";
    }
    if (tagType == YRfidTagInfo::IEC_14443) {
        typeStr = "IEC 14443";
    }
    if (tagType == YRfidTagInfo::IEC_14443_MIFARE_ULTRALIGHT) {
        typeStr = "MIFARE Ultralight";
    }
    if (tagType == YRfidTagInfo::IEC_14443_MIFARE_CLASSIC1K) {
        typeStr = "MIFARE Classic 1K";
    }
    if (tagType == YRfidTagInfo::IEC_14443_MIFARE_CLASSIC4K) {
        typeStr = "MIFARE Classic 4K";
    }
    if (tagType == YRfidTagInfo::IEC_14443_MIFARE_DESFIRE) {
        typeStr = "MIFARE DESFire";
    }
    if (tagType == YRfidTagInfo::IEC_14443_NTAG_213) {
        typeStr = "NTAG 213";
    }
    if (tagType == YRfidTagInfo::IEC_14443_NTAG_215) {
        typeStr = "NTAG 215";
    }
    if (tagType == YRfidTagInfo::IEC_14443_NTAG_216) {
        typeStr = "NTAG 216";
    }
    if (tagType == YRfidTagInfo::IEC_14443_NTAG_424_DNA) {
        typeStr = "NTAG 424 DNA";
    }
    if (tagType == YRfidTagInfo::IEC_15693_ST25DV) {
        typeStr = "ST25DVxx";
    }
    if (tagType == YRfidTagInfo::IEC_15693_ST25TV) {
        typeStr = "ST25TVxx";
    }
    if (tagType == YRfidTagInfo::IEC_15693_TAGIT_HFI) {
        typeStr = "TI TAGIT HFI";
    }
    if (tagType == YRfidTagInfo::IEC_15693_MB89R) {
        typeStr = "MB89Rxx";
    }
    if (tagType == YRfidTagInfo::IEC_15693_ICODE_DNA) {
        typeStr = "ICODE DNA";
    }
    if (tagType == YRfidTagInfo::IEC_15693_ICODE_SLI) {
        typeStr = "ICODE SLI";
    }
    nfcType = 0;
    if ((tagType == YRfidTagInfo::IEC_14443_MIFARE_ULTRALIGHT) || (tagType == YRfidTagInfo::IEC_14443_MIFARE_CLASSIC1K) || (tagType == YRfidTagInfo::IEC_14443_MIFARE_CLASSIC4K) || (tagType == YRfidTagInfo::IEC_14443_NTAG_213) || (tagType == YRfidTagInfo::IEC_14443_NTAG_215) || (tagType == YRfidTagInfo::IEC_14443_NTAG_216) || (tagType == YRfidTagInfo::IEC_14443_NTAG_424_DNA)) {
        nfcType = 2;
    }
    if (tagType == YRfidTagInfo::IEC_14443_MIFARE_DESFIRE) {
        nfcType = 4;
    }
    if ((tagType == YRfidTagInfo::IEC_15693) || (tagType == YRfidTagInfo::IEC_15693_ST25DV) || (tagType == YRfidTagInfo::IEC_15693_ST25TV) || (tagType == YRfidTagInfo::IEC_15693_TAGIT_HFI) || (tagType == YRfidTagInfo::IEC_15693_MB89R) || (tagType == YRfidTagInfo::IEC_15693_ICODE_DNA) || (tagType == YRfidTagInfo::IEC_15693_ICODE_SLI)) {
        nfcType = 5;
    }
    _tagId = tagId;
    _tagType = tagType;
    _nfcType = nfcType;
    _typeStr = typeStr;
    _size = size;
    _usable = usable;
    _blksize = blksize;
    _fblk = fblk;
    _lblk = lblk;
}
//--- (end of generated code: YRfidTagInfo implementation)


YRfidOptions::YRfidOptions():
//--- (generated code: YRfidOptions initialization)
    KeyType(0)
    ,ForceSingleBlockAccess(0)
    ,ForceMultiBlockAccess(0)
    ,EnableRawAccess(0)
    ,DisableBoundaryChecks(0)
    ,EnableDryRun(0)
//--- (end of generated code: YRfidOptions initialization)
{
}

YRfidOptions::~YRfidOptions()
{
//--- (generated code: YRfidOptions cleanup)
//--- (end of generated code: YRfidOptions cleanup)
}
//--- (generated code: YRfidOptions implementation)
// static attributes


string YRfidOptions::imm_getParams(void)
{
    int opt = 0;
    string res;
    if (ForceSingleBlockAccess) {
        opt = 1;
    } else {
        opt = 0;
    }
    if (ForceMultiBlockAccess) {
        opt = (opt | 2);
    }
    if (EnableRawAccess) {
        opt = (opt | 4);
    }
    if (DisableBoundaryChecks) {
        opt = (opt | 8);
    }
    if (EnableDryRun) {
        opt = (opt | 16);
    }
    res = YapiWrapper::ysprintf("&o=%d",opt);
    if (KeyType != 0) {
        res = YapiWrapper::ysprintf("%s&k=%02x:%s",res.c_str(),KeyType,HexKey.c_str());
    }
    return res;
}
//--- (end of generated code: YRfidOptions implementation)


YRfidStatus::YRfidStatus():
//--- (generated code: YRfidStatus initialization)
    _errCode(0)
    ,_errBlk(0)
    ,_yapierr(0)
    ,_fab(0)
    ,_lab(0)
//--- (end of generated code: YRfidStatus initialization)
{
}

YRfidStatus::~YRfidStatus()
{
//--- (generated code: YRfidStatus cleanup)
//--- (end of generated code: YRfidStatus cleanup)
}
//--- (generated code: YRfidStatus implementation)
// static attributes


/**
 * Returns RFID tag identifier related to the status.
 *
 * @return a string with the RFID tag identifier.
 */
string YRfidStatus::get_tagId(void)
{
    return _tagId;
}

/**
 * Returns the detailled error code, or 0 if no error happened.
 *
 * @return a numeric error code
 */
int YRfidStatus::get_errorCode(void)
{
    return _errCode;
}

/**
 * Returns the RFID tag memory block number where the error was encountered, or -1 if the
 * error is not specific to a memory block.
 *
 * @return an RFID tag block number
 */
int YRfidStatus::get_errorBlock(void)
{
    return _errBlk;
}

/**
 * Returns a string describing precisely the RFID commande result.
 *
 * @return an error message string
 */
string YRfidStatus::get_errorMessage(void)
{
    return _errMsg;
}

int YRfidStatus::get_yapiError(void)
{
    return _yapierr;
}

/**
 * Returns the block number of the first RFID tag memory block affected
 * by the operation. Depending on the type of operation and on the tag
 * memory granularity, this number may be smaller than the requested
 * memory block index.
 *
 * @return an RFID tag block number
 */
int YRfidStatus::get_firstAffectedBlock(void)
{
    return _fab;
}

/**
 * Returns the block number of the last RFID tag memory block affected
 * by the operation. Depending on the type of operation and on the tag
 * memory granularity, this number may be bigger than the requested
 * memory block index.
 *
 * @return an RFID tag block number
 */
int YRfidStatus::get_lastAffectedBlock(void)
{
    return _lab;
}

void YRfidStatus::imm_init(string tagId,int errCode,int errBlk,int fab,int lab)
{
    string errMsg;
    if (errCode == 0) {
        _yapierr = YAPI_SUCCESS;
        errMsg = "Success (no error)";
    } else {
        if (errCode < 0) {
            if (errCode > -50) {
                _yapierr = errCode;
                errMsg = YapiWrapper::ysprintf("YoctoLib error %d",errCode);
            } else {
                _yapierr = YAPI_RFID_HARD_ERROR;
                errMsg = YapiWrapper::ysprintf("Non-recoverable RFID error %d",errCode);
            }
        } else {
            if (errCode > 1000) {
                _yapierr = YAPI_RFID_SOFT_ERROR;
                errMsg = YapiWrapper::ysprintf("Recoverable RFID error %d",errCode);
            } else {
                _yapierr = YAPI_RFID_HARD_ERROR;
                errMsg = YapiWrapper::ysprintf("Non-recoverable RFID error %d",errCode);
            }
        }
        if (errCode == YRfidStatus::TAG_NOTFOUND) {
            errMsg = "Tag not found";
        }
        if (errCode == YRfidStatus::TAG_JUSTLEFT) {
            errMsg = "Tag left during operation";
        }
        if (errCode == YRfidStatus::TAG_LEFT) {
            errMsg = "Tag not here anymore";
        }
        if (errCode == YRfidStatus::READER_BUSY) {
            errMsg = "Reader is busy";
        }
        if (errCode == YRfidStatus::INVALID_CMD_ARGUMENTS) {
            errMsg = "Invalid command arguments";
        }
        if (errCode == YRfidStatus::UNKNOWN_CAPABILITIES) {
            errMsg = "Unknown capabilities";
        }
        if (errCode == YRfidStatus::MEMORY_NOT_SUPPORTED) {
            errMsg = "Memory no present";
        }
        if (errCode == YRfidStatus::INVALID_BLOCK_INDEX) {
            errMsg = "Invalid block index";
        }
        if (errCode == YRfidStatus::MEM_SPACE_UNVERRUN_ATTEMPT) {
            errMsg = "Tag memory space overrun attempt";
        }
        if (errCode == YRfidStatus::COMMAND_NOT_SUPPORTED) {
            errMsg = "The command is not supported";
        }
        if (errCode == YRfidStatus::COMMAND_NOT_RECOGNIZED) {
            errMsg = "The command is not recognized";
        }
        if (errCode == YRfidStatus::COMMAND_OPTION_NOT_RECOGNIZED) {
            errMsg = "The command option is not supported.";
        }
        if (errCode == YRfidStatus::COMMAND_CANNOT_BE_PROCESSED_IN_TIME) {
            errMsg = "The command cannot be processed in time";
        }
        if (errCode == YRfidStatus::UNDOCUMENTED_ERROR) {
            errMsg = "Error with no information given";
        }
        if (errCode == YRfidStatus::BLOCK_NOT_AVAILABLE) {
            errMsg = "Block is not available";
        }
        if (errCode == YRfidStatus::BLOCK_ALREADY_LOCKED) {
            errMsg = "Block / byte is already locked and thus cannot be locked again.";
        }
        if (errCode == YRfidStatus::BLOCK_LOCKED) {
            errMsg = "Block / byte is either locked and its content cannot be changed or operation might require a password.";
        }
        if (errCode == YRfidStatus::BLOCK_NOT_SUCESSFULLY_PROGRAMMED) {
            errMsg = "Block was not successfully programmed";
        }
        if (errCode == YRfidStatus::BLOCK_NOT_SUCESSFULLY_LOCKED) {
            errMsg = "Block was not successfully locked";
        }
        if (errCode == YRfidStatus::BLOCK_IS_PROTECTED) {
            errMsg = "Block is protected";
        }
        if (errCode == YRfidStatus::CRYPTOGRAPHIC_ERROR) {
            errMsg = "Generic cryptographic error";
        }
        if (errCode == YRfidStatus::BROWNOUT_DETECTED) {
            errMsg = "BrownOut detected (BOD)";
        }
        if (errCode == YRfidStatus::BUFFER_OVERFLOW) {
            errMsg = "Buffer Overflow (BOF)";
        }
        if (errCode == YRfidStatus::CRC_ERROR) {
            errMsg = "Communication CRC Error (CCE)";
        }
        if (errCode == YRfidStatus::COLLISION_DETECTED) {
            errMsg = "Collision Detected (CLD/CDT)";
        }
        if (errCode == YRfidStatus::COMMAND_RECEIVE_TIMEOUT) {
            errMsg = "Command Receive Timeout (CRT)";
        }
        if (errCode == YRfidStatus::DID_NOT_SLEEP) {
            errMsg = "Did Not Sleep (DNS)";
        }
        if (errCode == YRfidStatus::ERROR_DECIMAL_EXPECTED) {
            errMsg = "Error Decimal Expected (EDX)";
        }
        if (errCode == YRfidStatus::HARDWARE_FAILURE) {
            errMsg = "Error Hardware Failure (EHF)";
        }
        if (errCode == YRfidStatus::ERROR_HEX_EXPECTED) {
            errMsg = "Error Hex Expected (EHX)";
        }
        if (errCode == YRfidStatus::FIFO_LENGTH_ERROR) {
            errMsg = "FIFO length error (FLE)";
        }
        if (errCode == YRfidStatus::FRAMING_ERROR) {
            errMsg = "Framing error (FER)";
        }
        if (errCode == YRfidStatus::NOT_IN_CNR_MODE) {
            errMsg = "Not in CNR Mode (NCM)";
        }
        if (errCode == YRfidStatus::NUMBER_OU_OF_RANGE) {
            errMsg = "Number Out of Range (NOR)";
        }
        if (errCode == YRfidStatus::NOT_SUPPORTED) {
            errMsg = "Not Supported (NOS)";
        }
        if (errCode == YRfidStatus::NO_RF_FIELD_ACTIVE) {
            errMsg = "No RF field active (NRF)";
        }
        if (errCode == YRfidStatus::READ_DATA_LENGTH_ERROR) {
            errMsg = "Read data length error (RDL)";
        }
        if (errCode == YRfidStatus::WATCHDOG_RESET) {
            errMsg = "Watchdog reset (SRT)";
        }
        if (errCode == YRfidStatus::TAG_COMMUNICATION_ERROR) {
            errMsg = "Tag Communication Error (TCE)";
        }
        if (errCode == YRfidStatus::TAG_NOT_RESPONDING) {
            errMsg = "Tag Not Responding (TNR)";
        }
        if (errCode == YRfidStatus::TIMEOUT_ERROR) {
            errMsg = "TimeOut Error (TOE)";
        }
        if (errCode == YRfidStatus::UNKNOW_COMMAND) {
            errMsg = "Unknown Command (UCO)";
        }
        if (errCode == YRfidStatus::UNKNOW_ERROR) {
            errMsg = "Unknown error (UER)";
        }
        if (errCode == YRfidStatus::UNKNOW_PARAMETER) {
            errMsg = "Unknown Parameter (UPA)";
        }
        if (errCode == YRfidStatus::UART_RECEIVE_ERROR) {
            errMsg = "UART Receive Error (URE)";
        }
        if (errCode == YRfidStatus::WRONG_DATA_LENGTH) {
            errMsg = "Wrong Data Length (WDL)";
        }
        if (errCode == YRfidStatus::WRONG_MODE) {
            errMsg = "Wrong Mode (WMO)";
        }
        if (errCode == YRfidStatus::UNKNOWN_DWARFxx_ERROR_CODE) {
            errMsg = "Unknown DWARF15 error code";
        }
        if (errCode == YRfidStatus::UNEXPECTED_TAG_ID_IN_RESPONSE) {
            errMsg = "Unexpected Tag id in response";
        }
        if (errCode == YRfidStatus::UNEXPECTED_TAG_INDEX) {
            errMsg = "internal error : unexpected TAG index";
        }
        if (errCode == YRfidStatus::TRANSFER_CLOSED) {
            errMsg = "transfer closed";
        }
        if (errCode == YRfidStatus::WRITE_DATA_MISSING) {
            errMsg = "Missing write data";
        }
        if (errCode == YRfidStatus::WRITE_TOO_MUCH_DATA) {
            errMsg = "Attempt to write too much data";
        }
        if (errCode == YRfidStatus::COULD_NOT_BUILD_REQUEST) {
            errMsg = "Could not not request";
        }
        if (errCode == YRfidStatus::INVALID_OPTIONS) {
            errMsg = "Invalid transfer options";
        }
        if (errCode == YRfidStatus::UNEXPECTED_RESPONSE) {
            errMsg = "Unexpected Tag response";
        }
        if (errCode == YRfidStatus::AFI_NOT_AVAILABLE) {
            errMsg = "AFI not available";
        }
        if (errCode == YRfidStatus::DSFID_NOT_AVAILABLE) {
            errMsg = "DSFID not available";
        }
        if (errCode == YRfidStatus::TAG_RESPONSE_TOO_SHORT) {
            errMsg = "Tag's response too short";
        }
        if (errCode == YRfidStatus::DEC_EXPECTED) {
            errMsg = "Error Decimal value Expected, or is missing";
        }
        if (errCode == YRfidStatus::HEX_EXPECTED) {
            errMsg = "Error Hexadecimal value Expected, or is missing";
        }
        if (errCode == YRfidStatus::NOT_SAME_SECOR) {
            errMsg = "Input and Output block are not in the same Sector";
        }
        if (errCode == YRfidStatus::MIFARE_AUTHENTICATED) {
            errMsg = "No chip with MIFARE Classic technology Authenticated";
        }
        if (errCode == YRfidStatus::NO_DATABLOCK) {
            errMsg = "No Data Block";
        }
        if (errCode == YRfidStatus::KEYB_IS_READABLE) {
            errMsg = "Key B is Readable";
        }
        if (errCode == YRfidStatus::OPERATION_NOT_EXECUTED) {
            errMsg = "Operation Not Executed, would have caused an overflow";
        }
        if (errCode == YRfidStatus::BLOK_MODE_ERROR) {
            errMsg = "Block has not been initialized as a 'value block'";
        }
        if (errCode == YRfidStatus::BLOCK_NOT_WRITABLE) {
            errMsg = "Block Not Writable";
        }
        if (errCode == YRfidStatus::BLOCK_ACCESS_ERROR) {
            errMsg = "Block Access Error";
        }
        if (errCode == YRfidStatus::BLOCK_NOT_AUTHENTICATED) {
            errMsg = "Block Not Authenticated";
        }
        if (errCode == YRfidStatus::ACCESS_KEY_BIT_NOT_WRITABLE) {
            errMsg = "Access bits or Keys not Writable";
        }
        if (errCode == YRfidStatus::USE_KEYA_FOR_AUTH) {
            errMsg = "Use Key B for authentication";
        }
        if (errCode == YRfidStatus::USE_KEYB_FOR_AUTH) {
            errMsg = "Use Key A for authentication";
        }
        if (errCode == YRfidStatus::KEY_NOT_CHANGEABLE) {
            errMsg = "Key(s) not changeable";
        }
        if (errCode == YRfidStatus::BLOCK_TOO_HIGH) {
            errMsg = "Block index is too high";
        }
        if (errCode == YRfidStatus::AUTH_ERR) {
            errMsg = "Authentication Error (i.e. wrong key)";
        }
        if (errCode == YRfidStatus::NOKEY_SELECT) {
            errMsg = "No Key Select, select a temporary or a static key";
        }
        if (errCode == YRfidStatus::CARD_NOT_SELECTED) {
            errMsg = " Card is Not Selected";
        }
        if (errCode == YRfidStatus::BLOCK_TO_READ_NONE) {
            errMsg = "Number of Blocks to Read is 0";
        }
        if (errCode == YRfidStatus::NO_TAG) {
            errMsg = "No Tag detected";
        }
        if (errCode == YRfidStatus::TOO_MUCH_DATA) {
            errMsg = "Too Much Data (i.e. Uart input buffer overflow)";
        }
        if (errCode == YRfidStatus::CON_NOT_SATISFIED) {
            errMsg = "Conditions Not Satisfied";
        }
        if (errCode == YRfidStatus::BLOCK_IS_SPECIAL) {
            errMsg = "Bad parameter: block is a special block";
        }
        if (errCode == YRfidStatus::READ_BEYOND_ANNOUNCED_SIZE) {
            errMsg = "Attempt to read more than announced size.";
        }
        if (errCode == YRfidStatus::BLOCK_ZERO_IS_RESERVED) {
            errMsg = "Block 0 is reserved and cannot be used";
        }
        if (errCode == YRfidStatus::VALUE_BLOCK_BAD_FORMAT) {
            errMsg = "One value block is not properly initialized";
        }
        if (errCode == YRfidStatus::ISO15693_ONLY_FEATURE) {
            errMsg = "Feature available on ISO 15693 only";
        }
        if (errCode == YRfidStatus::ISO14443_ONLY_FEATURE) {
            errMsg = "Feature available on ISO 14443 only";
        }
        if (errCode == YRfidStatus::MIFARE_CLASSIC_ONLY_FEATURE) {
            errMsg = "Feature available on ISO 14443 MIFARE Classic only";
        }
        if (errCode == YRfidStatus::BLOCK_MIGHT_BE_PROTECTED) {
            errMsg = "Block might be protected";
        }
        if (errCode == YRfidStatus::NO_SUCH_BLOCK) {
            errMsg = "No such block";
        }
        if (errCode == YRfidStatus::COUNT_TOO_BIG) {
            errMsg = "Count parameter is too large";
        }
        if (errCode == YRfidStatus::UNKNOWN_MEM_SIZE) {
            errMsg = "Tag memory size is unknown";
        }
        if (errCode == YRfidStatus::MORE_THAN_2BLOCKS_MIGHT_NOT_WORK) {
            errMsg = "Writing more than two blocks at once might not be supported by this tag";
        }
        if (errCode == YRfidStatus::READWRITE_NOT_SUPPORTED) {
            errMsg = "Read/write operation not supported for this tag";
        }
        if (errCode == YRfidStatus::UNEXPECTED_VICC_ID_IN_RESPONSE) {
            errMsg = "Unexpected VICC ID in response";
        }
        if (errCode == YRfidStatus::LOCKBLOCK_NOT_SUPPORTED) {
            errMsg = "This tag does not support the Lock block function";
        }
        if (errCode == YRfidStatus::INTERNAL_ERROR_SHOULD_NEVER_HAPPEN) {
            errMsg = "Yoctopuce RFID code ran into an unexpected state, please contact support";
        }
        if (errCode == YRfidStatus::INVLD_BLOCK_MODE_COMBINATION) {
            errMsg = "Invalid combination of block mode options";
        }
        if (errCode == YRfidStatus::INVLD_ACCESS_MODE_COMBINATION) {
            errMsg = "Invalid combination of access mode options";
        }
        if (errCode == YRfidStatus::INVALID_SIZE) {
            errMsg = "Invalid data size parameter";
        }
        if (errCode == YRfidStatus::BAD_PASSWORD_FORMAT) {
            errMsg = "Bad password format or type";
        }
        if (errCode == YRfidStatus::RADIO_IS_OFF) {
            errMsg = "Radio is OFF (refreshRate=0).";
        }
        if (errCode == YRfidStatus::NOT_AVAILABLE_ON_THIS_TAG) {
            errMsg = "Tag does not provide this feature.";
        }
        if (errCode == YRfidStatus::PASSWORD_FEATURE_NOT_SUPPORTED) {
            errMsg = "Password feature not supported this tag.";
        }
        if (errCode == YRfidStatus::BAD_PASSWORD_LENGTH) {
            errMsg = "Incorrect password length";
        }
        if (errCode == YRfidStatus::BAD_PASSWORD_TYPE) {
            errMsg = "Bad password type.";
        }
        if (errCode == YRfidStatus::BAD_PASSWORD) {
            errMsg = "Bad password.";
        }
        if (errCode == YRfidStatus::PASSWORD_REQUIRED) {
            errMsg = "Operation requires a password";
        }
        if (errCode == YRfidStatus::MULTIWRITE_NOT_SUPPORTED) {
            errMsg = "Multi block write unavailable on this tag.";
        }
        if (errCode == YRfidStatus::MULTIREAD_NOT_SUPPORTED) {
            errMsg = "Multi block read unavailable on this tag.";
        }
        if (errBlk >= 0) {
            errMsg = YapiWrapper::ysprintf("%s (block %d)",errMsg.c_str(),errBlk);
        }
    }
    _tagId = tagId;
    _errCode = errCode;
    _errBlk = errBlk;
    _errMsg = errMsg;
    _fab = fab;
    _lab = lab;
}
//--- (end of generated code: YRfidStatus implementation)


YRfidReader::YRfidReader(const string& func): YFunction(func)
//--- (generated code: YRfidReader initialization)
    ,_nTags(NTAGS_INVALID)
    ,_refreshRate(REFRESHRATE_INVALID)
    ,_valueCallbackRfidReader(NULL)
    ,_eventCallback(NULL)
    ,_isFirstCb(0)
    ,_prevCbPos(0)
    ,_eventPos(0)
    ,_eventStamp(0)
//--- (end of generated code: YRfidReader initialization)
{
    _className="RfidReader";
}

YRfidReader::~YRfidReader()
{
//--- (generated code: YRfidReader cleanup)
//--- (end of generated code: YRfidReader cleanup)
}
//--- (generated code: YRfidReader implementation)
void YRfidReader::yInternalEventCallback(YRfidReader *obj, const string& value)
{
    obj->_internalEventHandler(value);
}

// static attributes

int YRfidReader::_parseAttr(YJSONObject *json_val)
{
    if(json_val->has("nTags")) {
        _nTags =  json_val->getInt("nTags");
    }
    if(json_val->has("refreshRate")) {
        _refreshRate =  json_val->getInt("refreshRate");
    }
    return YFunction::_parseAttr(json_val);
}


/**
 * Returns the number of RFID tags currently detected.
 *
 * @return an integer corresponding to the number of RFID tags currently detected
 *
 * On failure, throws an exception or returns YRfidReader::NTAGS_INVALID.
 */
int YRfidReader::get_nTags(void)
{
    int res = 0;
    yEnterCriticalSection(&_this_cs);
    try {
        if (_cacheExpiration <= YAPI::GetTickCount()) {
            if (this->_load_unsafe(YAPI::_yapiContext.GetCacheValidity()) != YAPI_SUCCESS) {
                {
                    yLeaveCriticalSection(&_this_cs);
                    return YRfidReader::NTAGS_INVALID;
                }
            }
        }
        res = _nTags;
    } catch (std::exception &) {
        yLeaveCriticalSection(&_this_cs);
        throw;
    }
    yLeaveCriticalSection(&_this_cs);
    return res;
}

/**
 * Returns the tag list refresh rate, measured in Hz.
 *
 * @return an integer corresponding to the tag list refresh rate, measured in Hz
 *
 * On failure, throws an exception or returns YRfidReader::REFRESHRATE_INVALID.
 */
int YRfidReader::get_refreshRate(void)
{
    int res = 0;
    yEnterCriticalSection(&_this_cs);
    try {
        if (_cacheExpiration <= YAPI::GetTickCount()) {
            if (this->_load_unsafe(YAPI::_yapiContext.GetCacheValidity()) != YAPI_SUCCESS) {
                {
                    yLeaveCriticalSection(&_this_cs);
                    return YRfidReader::REFRESHRATE_INVALID;
                }
            }
        }
        res = _refreshRate;
    } catch (std::exception &) {
        yLeaveCriticalSection(&_this_cs);
        throw;
    }
    yLeaveCriticalSection(&_this_cs);
    return res;
}

/**
 * Changes the present tag list refresh rate, measured in Hz. The reader will do
 * its best to respect it. Note that the reader cannot detect tag arrival or removal
 * while it is  communicating with a tag.  Maximum frequency is limited to 100Hz,
 * but in real life it will be difficult to do better than 50Hz.  A zero value
 * will power off the device radio.
 * Remember to call the saveToFlash() method of the module if the
 * modification must be kept.
 *
 * @param newval : an integer corresponding to the present tag list refresh rate, measured in Hz
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code.
 */
int YRfidReader::set_refreshRate(int newval)
{
    string rest_val;
    int res;
    yEnterCriticalSection(&_this_cs);
    try {
        char buf[32]; SAFE_SPRINTF(buf, 32, "%d", newval); rest_val = string(buf);
        res = _setAttr("refreshRate", rest_val);
    } catch (std::exception &) {
         yLeaveCriticalSection(&_this_cs);
         throw;
    }
    yLeaveCriticalSection(&_this_cs);
    return res;
}

/**
 * Retrieves a RFID reader for a given identifier.
 * The identifier can be specified using several formats:
 *
 * - FunctionLogicalName
 * - ModuleSerialNumber.FunctionIdentifier
 * - ModuleSerialNumber.FunctionLogicalName
 * - ModuleLogicalName.FunctionIdentifier
 * - ModuleLogicalName.FunctionLogicalName
 *
 *
 * This function does not require that the RFID reader is online at the time
 * it is invoked. The returned object is nevertheless valid.
 * Use the method isOnline() to test if the RFID reader is
 * indeed online at a given time. In case of ambiguity when looking for
 * a RFID reader by logical name, no error is notified: the first instance
 * found is returned. The search is performed first by hardware name,
 * then by logical name.
 *
 * If a call to this object's is_online() method returns FALSE although
 * you are certain that the matching device is plugged, make sure that you did
 * call registerHub() at application initialization time.
 *
 * @param func : a string that uniquely characterizes the RFID reader, for instance
 *         MyDevice.rfidReader.
 *
 * @return a YRfidReader object allowing you to drive the RFID reader.
 */
YRfidReader* YRfidReader::FindRfidReader(string func)
{
    YRfidReader* obj = NULL;
    int taken = 0;
    if (YAPI::_apiInitialized) {
        yEnterCriticalSection(&YAPI::_global_cs);
        taken = 1;
    }try {
        obj = (YRfidReader*) YFunction::_FindFromCache("RfidReader", func);
        if (obj == NULL) {
            obj = new YRfidReader(func);
            YFunction::_AddToCache("RfidReader", func, obj);
        }
    } catch (std::exception &) {
        if (taken) yLeaveCriticalSection(&YAPI::_global_cs);
        throw;
    }
    if (taken) yLeaveCriticalSection(&YAPI::_global_cs);
    return obj;
}

/**
 * Registers the callback function that is invoked on every change of advertised value.
 * The callback is then invoked only during the execution of ySleep or yHandleEvents.
 * This provides control over the time when the callback is triggered. For good responsiveness,
 * remember to call one of these two functions periodically. The callback is called once juste after beeing
 * registered, passing the current advertised value  of the function, provided that it is not an empty string.
 * To unregister a callback, pass a NULL pointer as argument.
 *
 * @param callback : the callback function to call, or a NULL pointer. The callback function should take two
 *         arguments: the function object of which the value has changed, and the character string describing
 *         the new advertised value.
 * @noreturn
 */
int YRfidReader::registerValueCallback(YRfidReaderValueCallback callback)
{
    string val;
    if (callback != NULL) {
        YFunction::_UpdateValueCallbackList(this, true);
    } else {
        YFunction::_UpdateValueCallbackList(this, false);
    }
    _valueCallbackRfidReader = callback;
    // Immediately invoke value callback with current value
    if (callback != NULL && this->isOnline()) {
        val = _advertisedValue;
        if (!(val == "")) {
            this->_invokeValueCallback(val);
        }
    }
    return 0;
}

int YRfidReader::_invokeValueCallback(string value)
{
    if (_valueCallbackRfidReader != NULL) {
        _valueCallbackRfidReader(this, value);
    } else {
        YFunction::_invokeValueCallback(value);
    }
    return 0;
}

int YRfidReader::_chkerror(string tagId,string json,YRfidStatus& status)
{
    string jsonStr;
    int errCode = 0;
    int errBlk = 0;
    int fab = 0;
    int lab = 0;
    int retcode = 0;

    if ((int)(json).size() == 0) {
        errCode = this->get_errorType();
        errBlk = -1;
        fab = -1;
        lab = -1;
    } else {
        jsonStr = json;
        errCode = atoi((this->_json_get_key(json, "err")).c_str());
        errBlk = atoi((this->_json_get_key(json, "errBlk")).c_str())-1;
        if (_ystrpos(jsonStr, "\"fab\":") >= 0) {
            fab = atoi((this->_json_get_key(json, "fab")).c_str())-1;
            lab = atoi((this->_json_get_key(json, "lab")).c_str())-1;
        } else {
            fab = -1;
            lab = -1;
        }
    }
    status.imm_init(tagId, errCode, errBlk, fab, lab);
    retcode = status.get_yapiError();
    if (!(retcode == YAPI_SUCCESS)) {
        _throw((YRETCODE)(retcode), status.get_errorMessage());
        return retcode;
    }
    return YAPI_SUCCESS;
}

int YRfidReader::reset(void)
{
    string json;
    YRfidStatus status;
    status = YRfidStatus();

    json = this->_download("rfid.json?a=reset");
    return this->_chkerror("", json, status);
}

/**
 * Returns the list of RFID tags currently detected by the reader.
 *
 * @return a list of strings, corresponding to each tag identifier (UID).
 *
 * On failure, throws an exception or returns an empty list.
 */
vector<string> YRfidReader::get_tagIdList(void)
{
    string json;
    vector<string> jsonList;
    vector<string> taglist;

    json = this->_download("rfid.json?a=list");
    taglist.clear();
    if ((int)(json).size() > 3) {
        jsonList = this->_json_get_array(json);
        for (unsigned ii_0 = 0; ii_0 < jsonList.size(); ii_0++) {
            taglist.push_back(this->_json_get_string(jsonList[ii_0]));
        }
    }
    return taglist;
}

/**
 * Returns a description of the properties of an existing RFID tag.
 * This function can cause communications with the tag.
 *
 * @param tagId : identifier of the tag to check
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return a YRfidTagInfo object.
 *
 * On failure, throws an exception or returns an empty YRfidTagInfo objact.
 * When it happens, you can get more information from the status object.
 */
YRfidTagInfo YRfidReader::get_tagInfo(string tagId,YRfidStatus& status)
{
    string url;
    string json;
    int tagType = 0;
    int size = 0;
    int usable = 0;
    int blksize = 0;
    int fblk = 0;
    int lblk = 0;
    YRfidTagInfo res;
    url = YapiWrapper::ysprintf("rfid.json?a=info&t=%s",tagId.c_str());

    json = this->_download(url);
    this->_chkerror(tagId, json, status);
    tagType = atoi((this->_json_get_key(json, "type")).c_str());
    size = atoi((this->_json_get_key(json, "size")).c_str());
    usable = atoi((this->_json_get_key(json, "usable")).c_str());
    blksize = atoi((this->_json_get_key(json, "blksize")).c_str());
    fblk = atoi((this->_json_get_key(json, "fblk")).c_str());
    lblk = atoi((this->_json_get_key(json, "lblk")).c_str());
    res = YRfidTagInfo();
    res.imm_init(tagId, tagType, size, usable, blksize, fblk, lblk);
    return res;
}

/**
 * Changes an RFID tag configuration to prevents any further write to
 * the selected blocks. This operation is definitive and irreversible.
 * Depending on the tag type and block index, adjascent blocks may become
 * read-only as well, based on the locking granularity.  Note that some tags
 * may allow only a few blocks to be locked, for instance ST25DVxxx  tags
 * allows a lock on block 0 and 1 only.
 *
 *
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : first block to lock
 * @param nBlocks : number of blocks to lock
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagLockBlocks(string tagId,int firstBlock,int nBlocks,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=lock&t=%s&b=%d&n=%d%s",tagId.c_str(),firstBlock,nBlocks,optstr.c_str());

    json = this->_download(url);
    return this->_chkerror(tagId, json, status);
}

/**
 * Reads the locked state for RFID tag memory data blocks.
 * FirstBlock cannot be a special block, and any special
 * block encountered in the middle of the read operation will be
 * skipped automatically.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : number of the first block to check
 * @param nBlocks : number of blocks to check
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return a list of booleans with the lock state of selected blocks
 *
 * On failure, throws an exception or returns an empty list. When it
 * happens, you can get more information from the status object.
 */
vector<bool> YRfidReader::get_tagLockState(string tagId,int firstBlock,int nBlocks,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    string binRes;
    vector<bool> res;
    int idx = 0;
    int val = 0;
    bool isLocked = 0;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=chkl&t=%s&b=%d&n=%d%s",tagId.c_str(),firstBlock,nBlocks,optstr.c_str());

    json = this->_download(url);
    this->_chkerror(tagId, json, status);
    if (status.get_yapiError() != YAPI_SUCCESS) {
        return res;
    }
    binRes = YAPI::_hexStr2Bin(this->_json_get_key(json, "bitmap"));
    idx = 0;
    while (idx < nBlocks) {
        val = ((u8)binRes[(idx >> 3)]);
        isLocked = ((val & (1 << (idx & 7))) != 0);
        res.push_back(isLocked);
        idx = idx + 1;
    }
    return res;
}

/**
 * Tells which block of a RFID tag memory are special and cannot be used
 * to store user data. Mistakely writing a special block can lead to
 * an irreversible alteration of the tag.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : number of the first block to check
 * @param nBlocks : number of blocks to check
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return a list of booleans with the lock state of selected blocks
 *
 * On failure, throws an exception or returns an empty list. When it
 * happens, you can get more information from the status object.
 */
vector<bool> YRfidReader::get_tagSpecialBlocks(string tagId,int firstBlock,int nBlocks,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    string binRes;
    vector<bool> res;
    int idx = 0;
    int val = 0;
    bool isLocked = 0;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=chks&t=%s&b=%d&n=%d%s",tagId.c_str(),firstBlock,nBlocks,optstr.c_str());

    json = this->_download(url);
    this->_chkerror(tagId, json, status);
    if (status.get_yapiError() != YAPI_SUCCESS) {
        return res;
    }
    binRes = YAPI::_hexStr2Bin(this->_json_get_key(json, "bitmap"));
    idx = 0;
    while (idx < nBlocks) {
        val = ((u8)binRes[(idx >> 3)]);
        isLocked = ((val & (1 << (idx & 7))) != 0);
        res.push_back(isLocked);
        idx = idx + 1;
    }
    return res;
}

/**
 * Reads data from an RFID tag memory, as an hexadecimal string.
 * The read operation may span accross multiple blocks if the requested
 * number of bytes is larger than the RFID tag block size. By default
 * firstBlock cannot be a special block, and any special block encountered
 * in the middle of the read operation will be skipped automatically.
 * If you rather want to read special blocks, use the EnableRawAccess
 * field from the options parameter.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : block number where read should start
 * @param nBytes : total number of bytes to read
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return an hexadecimal string if the call succeeds.
 *
 * On failure, throws an exception or returns an empty binary buffer. When it
 * happens, you can get more information from the status object.
 */
string YRfidReader::tagReadHex(string tagId,int firstBlock,int nBytes,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    string hexbuf;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=read&t=%s&b=%d&n=%d%s",tagId.c_str(),firstBlock,nBytes,optstr.c_str());

    json = this->_download(url);
    this->_chkerror(tagId, json, status);
    if (status.get_yapiError() == YAPI_SUCCESS) {
        hexbuf = this->_json_get_key(json, "res");
    } else {
        hexbuf = "";
    }
    return hexbuf;
}

/**
 * Reads data from an RFID tag memory, as a binary buffer. The read operation
 * may span accross multiple blocks if the requested number of bytes
 * is larger than the RFID tag block size.  By default
 * firstBlock cannot be a special block, and any special block encountered
 * in the middle of the read operation will be skipped automatically.
 * If you rather want to read special blocks, use the EnableRawAccess
 * field frrm the options parameter.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : block number where read should start
 * @param nBytes : total number of bytes to read
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return a binary object with the data read if the call succeeds.
 *
 * On failure, throws an exception or returns an empty binary buffer. When it
 * happens, you can get more information from the status object.
 */
string YRfidReader::tagReadBin(string tagId,int firstBlock,int nBytes,YRfidOptions options,YRfidStatus& status)
{
    return YAPI::_hexStr2Bin(this->tagReadHex(tagId, firstBlock, nBytes, options, status));
}

/**
 * Reads data from an RFID tag memory, as a byte list. The read operation
 * may span accross multiple blocks if the requested number of bytes
 * is larger than the RFID tag block size.  By default
 * firstBlock cannot be a special block, and any special block encountered
 * in the middle of the read operation will be skipped automatically.
 * If you rather want to read special blocks, use the EnableRawAccess
 * field from the options parameter.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : block number where read should start
 * @param nBytes : total number of bytes to read
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return a byte list with the data read if the call succeeds.
 *
 * On failure, throws an exception or returns an empty list. When it
 * happens, you can get more information from the status object.
 */
vector<int> YRfidReader::tagReadArray(string tagId,int firstBlock,int nBytes,YRfidOptions options,YRfidStatus& status)
{
    string blk;
    int idx = 0;
    int endidx = 0;
    vector<int> res;
    blk = this->tagReadBin(tagId, firstBlock, nBytes, options, status);
    endidx = (int)(blk).size();
    idx = 0;
    while (idx < endidx) {
        res.push_back(((u8)blk[idx]));
        idx = idx + 1;
    }
    return res;
}

/**
 * Reads data from an RFID tag memory, as a text string. The read operation
 * may span accross multiple blocks if the requested number of bytes
 * is larger than the RFID tag block size.  By default
 * firstBlock cannot be a special block, and any special block encountered
 * in the middle of the read operation will be skipped automatically.
 * If you rather want to read special blocks, use the EnableRawAccess
 * field from the options parameter.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : block number where read should start
 * @param nChars : total number of characters to read
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return a text string with the data read if the call succeeds.
 *
 * On failure, throws an exception or returns an empty string. When it
 * happens, you can get more information from the status object.
 */
string YRfidReader::tagReadStr(string tagId,int firstBlock,int nChars,YRfidOptions options,YRfidStatus& status)
{
    return this->tagReadBin(tagId, firstBlock, nChars, options, status);
}

/**
 * Writes data provided as a binary buffer to an RFID tag memory.
 * The write operation may span accross multiple blocks if the
 * number of bytes to write is larger than the RFID tag block size.
 * By default firstBlock cannot be a special block, and any special block
 * encountered in the middle of the write operation will be skipped
 * automatically. The last data block affected by the operation will
 * be automatically padded with zeros if neccessary.  If you rather want
 * to rewrite special blocks as well,
 * use the EnableRawAccess field from the options parameter.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : block number where write should start
 * @param buff : the binary buffer to write
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagWriteBin(string tagId,int firstBlock,string buff,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string hexstr;
    int buflen = 0;
    string fname;
    string json;
    buflen = (int)(buff).size();
    if (buflen <= 16) {
        // short data, use an URL-based command
        hexstr = YAPI::_bin2HexStr(buff);
        return this->tagWriteHex(tagId, firstBlock, hexstr, options, status);
    } else {
        // long data, use an upload command
        optstr = options.imm_getParams();
        fname = YapiWrapper::ysprintf("Rfid:t=%s&b=%d&n=%d%s",tagId.c_str(),firstBlock,buflen,optstr.c_str());
        json = this->_uploadEx(fname, buff);
        return this->_chkerror(tagId, json, status);
    }
}

/**
 * Writes data provided as a list of bytes to an RFID tag memory.
 * The write operation may span accross multiple blocks if the
 * number of bytes to write is larger than the RFID tag block size.
 * By default firstBlock cannot be a special block, and any special block
 * encountered in the middle of the write operation will be skipped
 * automatically. The last data block affected by the operation will
 * be automatically padded with zeros if neccessary.
 * If you rather want to rewrite special blocks as well,
 * use the EnableRawAccess field from the options parameter.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : block number where write should start
 * @param byteList : a list of byte to write
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagWriteArray(string tagId,int firstBlock,vector<int> byteList,YRfidOptions options,YRfidStatus& status)
{
    int bufflen = 0;
    string buff;
    int idx = 0;
    int hexb = 0;
    bufflen = (int)byteList.size();
    buff = string(bufflen, (char)0);
    idx = 0;
    while (idx < bufflen) {
        hexb = byteList[idx];
        buff[idx] = (char)(hexb);
        idx = idx + 1;
    }

    return this->tagWriteBin(tagId, firstBlock, buff, options, status);
}

/**
 * Writes data provided as an hexadecimal string to an RFID tag memory.
 * The write operation may span accross multiple blocks if the
 * number of bytes to write is larger than the RFID tag block size.
 * By default firstBlock cannot be a special block, and any special block
 * encountered in the middle of the write operation will be skipped
 * automatically. The last data block affected by the operation will
 * be automatically padded with zeros if neccessary.
 * If you rather want to rewrite special blocks as well,
 * use the EnableRawAccess field from the options parameter.
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : block number where write should start
 * @param hexString : a string of hexadecimal byte codes to write
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagWriteHex(string tagId,int firstBlock,string hexString,YRfidOptions options,YRfidStatus& status)
{
    int bufflen = 0;
    string optstr;
    string url;
    string json;
    string buff;
    int idx = 0;
    int hexb = 0;
    bufflen = (int)(hexString).length();
    bufflen = (bufflen >> 1);
    if (bufflen <= 16) {
        // short data, use an URL-based command
        optstr = options.imm_getParams();
        url = YapiWrapper::ysprintf("rfid.json?a=writ&t=%s&b=%d&w=%s%s",tagId.c_str(),firstBlock,hexString.c_str(),optstr.c_str());
        json = this->_download(url);
        return this->_chkerror(tagId, json, status);
    } else {
        // long data, use an upload command
        buff = string(bufflen, (char)0);
        idx = 0;
        while (idx < bufflen) {
            hexb = (int)YAPI::_hexStr2Long(hexString.substr(2 * idx, 2));
            buff[idx] = (char)(hexb);
            idx = idx + 1;
        }
        return this->tagWriteBin(tagId, firstBlock, buff, options, status);
    }
}

/**
 * Writes data provided as an ASCII string to an RFID tag memory.
 * The write operation may span accross multiple blocks if the
 * number of bytes to write is larger than the RFID tag block size.
 * Note that only the characters present in the provided string
 * will be written, there is no notion of string length. If your
 * string data have variable length, you'll have to encode the
 * string length yourself, with a terminal zero for instannce.
 *
 * This function only works with ISO-latin characters, if you wish to
 * write strings encoded with alternate character sets, you'll have to
 * use tagWriteBin() function.
 *
 * By default firstBlock cannot be a special block, and any special block
 * encountered in the middle of the write operation will be skipped
 * automatically. The last data block affected by the operation will
 * be automatically padded with zeros if neccessary.
 * If you rather want to rewrite special blocks as well,
 * use the EnableRawAccess field from the options parameter
 * (definitely not recommanded).
 *
 * @param tagId : identifier of the tag to use
 * @param firstBlock : block number where write should start
 * @param text : the text string to write
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagWriteStr(string tagId,int firstBlock,string text,YRfidOptions options,YRfidStatus& status)
{
    string buff;
    buff = text;

    return this->tagWriteBin(tagId, firstBlock, buff, options, status);
}

/**
 * Writes data provided as a binary object to an RFID tag, using NFC
 * compatible encoding.
 * The function will automatically create a NFC Capability Container,
 * and encapsulate the content with the required NDEF header corresponding
 * to the given content type.
 *
 * @param tagId : identifier of the tag to use
 * @param ndefType : the content type, either "U" for a URL, or a
 *         generic MIME type like "text/vcard"
 * @param payload : the payload of the NDEF record
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagWriteBinNFC(string tagId,string ndefType,string payload,YRfidOptions options,YRfidStatus& status)
{
    YRfidTagInfo tagInfo;
    int nfcType = 0;
    int usableSize = 0;
    int nfcBlocks = 0;
    int ccLen = 0;
    int typeLen = 0;
    int payloadLen = 0;
    int srBit = 0;
    int ndefLen = 0;
    int tlvHdrLen = 0;
    int totalLen = 0;
    int absPos = 0;
    int idx = 0;
    string binType;
    string buff;

    tagInfo = this->get_tagInfo(tagId, status);
    // for now, we only allow NFC Type 5 (T5T)
    nfcType = tagInfo.get_tagNFCtype();
    if (!(nfcType == 5)) {
        _throw((YRETCODE)(YAPI_INVALID_ARGUMENT), "no NFC support for this tag");
        return YAPI_INVALID_ARGUMENT;
    }
    usableSize = tagInfo.get_tagUsableSize();
    ccLen = 4;
    nfcBlocks = ((usableSize - ccLen) / 8);
    if (nfcBlocks > 255) {
        if (nfcType == 5) {
            ccLen = 8;
            nfcBlocks = ((usableSize - ccLen) / 8);
        } else {
            nfcBlocks = 255;
        }
    }
    binType = ndefType;
    typeLen = (int)(binType).size();
    payloadLen = (int)(payload).size();
    // compute the total length of the NDEF record
    ndefLen = 3 + typeLen + payloadLen;
    srBit = 0x10;
    if (payloadLen > 255) {
        ndefLen = ndefLen + 3;
        srBit = 0;
    }
    // compute the total length of the TLV record
    tlvHdrLen = 2;
    if (ndefLen > 254) {
        tlvHdrLen = 4;
    }
    // make sure the content fits on the tag
    if (!((tlvHdrLen + ndefLen + 1) <= (8 * nfcBlocks))) {
        _throw((YRETCODE)(YAPI_INVALID_ARGUMENT), "content is too large");
        return YAPI_INVALID_ARGUMENT;
    }
    totalLen = ccLen + tlvHdrLen + ndefLen + 1;
    buff = string(totalLen, (char)0);
    // CC header
    if (ccLen == 4) {
        buff[0] = (char)(0xE1);
        if (nfcType == 5) {
            buff[1] = (char)(0x40);
        } else {
            buff[1] = (char)(0x10);
        }
        buff[2] = (char)(nfcBlocks);
        buff[3] = (char)(0x00);
    } else {
        buff[0] = (char)(0xE2);
        buff[1] = (char)(0x40);
        buff[2] = (char)(0x00);
        buff[3] = (char)(0x01);
        buff[4] = (char)(0x00);
        buff[5] = (char)(0x00);
        buff[6] = (char)((nfcBlocks / 256));
        buff[7] = (char)((nfcBlocks & 255));
    }
    // TLV header
    buff[ccLen] = (char)(3);
    if (tlvHdrLen == 2) {
        buff[ccLen + 1] = (char)(ndefLen);
    } else {
        buff[ccLen + 1] = (char)(0xff);
        buff[ccLen + 2] = (char)((ndefLen / 256));
        buff[ccLen + 3] = (char)((ndefLen & 255));
    }
    absPos = ccLen + tlvHdrLen;
    // NDEF record
    if (typeLen <= 3) {
        // NFC Forum type
        buff[absPos] = (char)(0xC1 + srBit);
    } else {
        // MIME type
        buff[absPos] = (char)(0xC2 + srBit);
    }
    if (srBit > 0) {
        buff[absPos + 1] = (char)(typeLen);
        buff[absPos + 2] = (char)(payloadLen);
        absPos = absPos + 3;
    } else {
        buff[absPos + 1] = (char)(typeLen);
        buff[absPos + 2] = (char)(0);
        buff[absPos + 3] = (char)(0);
        buff[absPos + 4] = (char)((payloadLen / 256));
        buff[absPos + 5] = (char)((payloadLen & 255));
        absPos = absPos + 6;
    }
    idx = 0;
    while (idx < typeLen) {
        buff[absPos + idx] = (char)(((u8)binType[idx]));
        idx = idx + 1;
    }
    absPos = absPos + typeLen;
    idx = 0;
    while (idx < payloadLen) {
        buff[absPos + idx] = (char)(((u8)payload[idx]));
        idx = idx + 1;
    }
    absPos = absPos + payloadLen;
    // TLV trailer
    buff[absPos] = (char)(0xfe);
    idx = tagInfo.get_tagFirstBlock();
    return this->tagWriteBin(tagId, idx, buff, options, status);
}

/**
 * Writes an URL to an RFID tag using NFC compatible encoding, so that
 * mobile phones with NFC support automatically offer to open
 * the URL when reading the tag.
 *
 * @param tagId : identifier of the tag to use
 * @param url : the URL to write on the tag
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagWriteUrlNFC(string tagId,string url,YRfidOptions options,YRfidStatus& status)
{
    int prefix = 0;
    string binUrl;
    prefix = 0;
    if (url.substr(0, 8) == "https://") {
        prefix = 4;
        url = url.substr(8, (int)(url).length() - 8);
    } else {
        if (url.substr(0, 8) == "http://") {
            prefix = 3;
            url = url.substr(7, (int)(url).length() - 7);
        }
    }
    if (url.substr(0, 8) == "www.") {
        prefix = prefix - 2;
        url = url.substr(4, (int)(url).length() - 4);
    }
    binUrl = "_" + url;
    binUrl[0] = (char)(prefix);
    return this->tagWriteBinNFC(tagId, "U", binUrl, options, status);
}

/**
 * Writes WiFi settings to an RFID tag using NFC compatible encoding, so that
 * mobile phones with NFC support automatically offer to connect to this WiFi
 * network.
 *
 * @param tagId : identifier of the tag to use
 * @param ssid : the SSID of the WiFi network to connect to
 * @param auth : the network authentication type (currently always "WPA2")
 * @param secret : the network password
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagWriteWifiConfigNFC(string tagId,string ssid,string auth,string secret,YRfidOptions options,YRfidStatus& status)
{
    string ssidBin;
    int ssidLen = 0;
    string secretBin;
    int secretLen = 0;
    int payloadLen = 0;
    string payload;
    int idx = 0;
    ssidBin = ssid;
    ssidLen = (int)(ssidBin).size();
    secretBin = secret;
    secretLen = (int)(secretBin).size();
    payloadLen = ssidLen + secretLen + 39;
    payload = string(payloadLen, (char)0);
    // Credential header
    payload[0] = (char)(0x10);
    payload[1] = (char)(0x0e);
    payload[2] = (char)(0);
    payload[3] = (char)(payloadLen - 4);
    // Network index
    payload[4] = (char)(0x10);
    payload[5] = (char)(0x26);
    payload[6] = (char)(0);
    payload[7] = (char)(1);
    payload[8] = (char)(1);
    // SSID
    payload[9] = (char)(0x10);
    payload[10] = (char)(0x45);
    payload[11] = (char)(0);
    payload[12] = (char)(ssidLen);
    idx = 0;
    while (idx < ssidLen) {
        payload[13 + idx] = (char)(((u8)ssidBin[idx]));
        idx = idx + 1;
    }
    // Auth: WPA2-Personal
    payload[13 + ssidLen] = (char)(0x10);
    payload[14 + ssidLen] = (char)(0x03);
    payload[15 + ssidLen] = (char)(0);
    payload[16 + ssidLen] = (char)(2);
    payload[17 + ssidLen] = (char)(0);
    payload[18 + ssidLen] = (char)(32);
    // Encryption: AES
    payload[19 + ssidLen] = (char)(0x10);
    payload[20 + ssidLen] = (char)(0x0f);
    payload[21 + ssidLen] = (char)(0);
    payload[22 + ssidLen] = (char)(2);
    payload[23 + ssidLen] = (char)(0);
    payload[24 + ssidLen] = (char)(8);
    // Network key
    payload[25 + ssidLen] = (char)(0x10);
    payload[26 + ssidLen] = (char)(0x27);
    payload[27 + ssidLen] = (char)(0);
    payload[28 + ssidLen] = (char)(secretLen);
    idx = 0;
    while (idx < secretLen) {
        payload[29 + ssidLen + idx] = (char)(((u8)secretBin[idx]));
        idx = idx + 1;
    }
    // MAC broadcast
    payload[29 + ssidLen + secretLen] = (char)(0x10);
    payload[30 + ssidLen + secretLen] = (char)(0x20);
    payload[31 + ssidLen + secretLen] = (char)(0);
    payload[32 + ssidLen + secretLen] = (char)(6);
    idx = 0;
    while (idx < 6) {
        payload[33 + ssidLen + secretLen + idx] = (char)(0xff);
        idx = idx + 1;
    }
    return this->tagWriteBinNFC(tagId, "application/vnd.wfa.wsc", payload, options, status);
}

/**
 * Reads an RFID tag AFI byte (ISO 15693 only).
 *
 * @param tagId : identifier of the tag to use
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return the AFI value (0...255)
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagGetAFI(string tagId,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    int res = 0;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=rdsf&t=%s&b=0%s",tagId.c_str(),optstr.c_str());

    json = this->_download(url);
    this->_chkerror(tagId, json, status);
    if (status.get_yapiError() == YAPI_SUCCESS) {
        res = atoi((this->_json_get_key(json, "res")).c_str());
    } else {
        res = status.get_yapiError();
    }
    return res;
}

/**
 * Reads a byte from the Tag configuration (ISO 15693 ST25DVxx only).
 * This function is actually a call to the 0xA0 RFID command and is specific to
 * ST25DVxx tags. Check ST25DVxx datasheet for more information about
 * the data organisation of ST25DVxx tags configuration.
 *
 * @param tagId : identifier of the tag to use
 * @param addr  : offset of the byte in the tag configuation
 *
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return the requested byte value (0...255)
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagGetConfigByte(string tagId,int addr,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    int res = 0;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=gcfg&t=%s&b=%d%s",tagId.c_str(),addr,optstr.c_str());

    json = this->_download(url);
    this->_chkerror(tagId, json, status);
    if (status.get_yapiError() == YAPI_SUCCESS) {
        res = atoi((this->_json_get_key(json, "res")).c_str());
    } else {
        res = status.get_yapiError();
    }
    return res;
}

/**
 * Changes a byte in the tag's configuration (ISO 15693 ST25DVxx only).
 * Warning: modifing the tag configation may alter its behavior in a non-reversible way.
 * This operation requires the CONFIG_PWD password to be set in the options,
 * default value is "0000000000000000" (16 zeros). This function is actually
 * a call to the 0xA1 RFID command and is specific to ST25DVxx tags. Check
 * ST25DVxx datasheet for more information about the data organisation
 * of ST25DVxx tags configuration.
 *
 * @param tagId : identifier of the tag to use
 * @param addr  : address of the byte to write
 * @param value : the value to write (0...255)
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagSetConfigByte(string tagId,int addr,int value,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=scfg&t=%s&b=%d&v=%d%s",tagId.c_str(),addr,value,optstr.c_str());

    json = this->_download(url);
    return this->_chkerror(tagId, json, status);
}

/**
 * Set a password that will be required to access the tag  (ISO 15693 ST25DVxx only).
 * The password must be a string of characters representing 8 bytes in hexadecimal.
 * There are several types of password for the same tag; please consult your tags
 * documentation to understand their respective applications. Once the password
 * has been configured, operations requiring this password must be initiated with
 * the password defined in the KeyType and HexKey fields of the
 * options parameter for the operations in question. It is not necessarily
 * required to consistently provide the same password for every operation during the
 * same session with a tag.
 *
 * @param tagId : identifier of the tag to use
 * @param passwordType  : type of password to be set (YRfidOptions.ST25D_CONFIG_PWD,YRfidOptions.ST25D_PWD1,YRfidOptions.ST25D_PWD2..)
 * @param password : the password (16 characters hex string encoding 8 bytes)
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagSetPassword(string tagId,int passwordType,string password,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=spwd&t=%s&b=%d&p=%s%s",tagId.c_str(),passwordType,password.c_str(),optstr.c_str());

    json = this->_download(url);
    return this->_chkerror(tagId, json, status);
}

/**
 * Changes an RFID tag AFI byte (ISO 15693 only).
 *
 * @param tagId : identifier of the tag to use
 * @param afi : the AFI value to write (0...255)
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagSetAFI(string tagId,int afi,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=wrsf&t=%s&b=0&v=%d%s",tagId.c_str(),afi,optstr.c_str());

    json = this->_download(url);
    return this->_chkerror(tagId, json, status);
}

/**
 * Locks the RFID tag AFI byte (ISO 15693 only).
 * This operation is definitive and irreversible.
 *
 * @param tagId : identifier of the tag to use
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagLockAFI(string tagId,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=lksf&t=%s&b=0%s",tagId.c_str(),optstr.c_str());

    json = this->_download(url);
    return this->_chkerror(tagId, json, status);
}

/**
 * Reads an RFID tag DSFID byte (ISO 15693 only).
 *
 * @param tagId : identifier of the tag to use
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return the DSFID value (0...255)
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagGetDSFID(string tagId,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    int res = 0;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=rdsf&t=%s&b=1%s",tagId.c_str(),optstr.c_str());

    json = this->_download(url);
    this->_chkerror(tagId, json, status);
    if (status.get_yapiError() == YAPI_SUCCESS) {
        res = atoi((this->_json_get_key(json, "res")).c_str());
    } else {
        res = status.get_yapiError();
    }
    return res;
}

/**
 * Changes an RFID tag DSFID byte (ISO 15693 only).
 *
 * @param tagId : identifier of the tag to use
 * @param dsfid : the DSFID value to write (0...255)
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagSetDSFID(string tagId,int dsfid,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=wrsf&t=%s&b=1&v=%d%s",tagId.c_str(),dsfid,optstr.c_str());

    json = this->_download(url);
    return this->_chkerror(tagId, json, status);
}

/**
 * Locks the RFID tag DSFID byte (ISO 15693 only).
 * This operation is definitive and irreversible.
 *
 * @param tagId : identifier of the tag to use
 * @param options : an YRfidOptions object with the optional
 *         command execution parameters, such as security key
 *         if required
 * @param status : an RfidStatus object that will contain
 *         the detailled status of the operation
 *
 * @return YAPI::SUCCESS if the call succeeds.
 *
 * On failure, throws an exception or returns a negative error code. When it
 * happens, you can get more information from the status object.
 */
int YRfidReader::tagLockDSFID(string tagId,YRfidOptions options,YRfidStatus& status)
{
    string optstr;
    string url;
    string json;
    optstr = options.imm_getParams();
    url = YapiWrapper::ysprintf("rfid.json?a=lksf&t=%s&b=1%s",tagId.c_str(),optstr.c_str());

    json = this->_download(url);
    return this->_chkerror(tagId, json, status);
}

/**
 * Returns a string with last tag arrival/removal events observed.
 * This method return only events that are still buffered in the device memory.
 *
 * @return a string with last events observed (one per line).
 *
 * On failure, throws an exception or returns  YAPI::INVALID_STRING.
 */
string YRfidReader::get_lastEvents(void)
{
    string content;

    content = this->_download("events.txt?pos=0");
    return content;
}

/**
 * Registers a callback function to be called each time that an RFID tag appears or
 * disappears. The callback is invoked only during the execution of
 * ySleep or yHandleEvents. This provides control over the time when
 * the callback is triggered. For good responsiveness, remember to call one of these
 * two functions periodically. To unregister a callback, pass a NULL pointer as argument.
 *
 * @param callback : the callback function to call, or a NULL pointer.
 *         The callback function should take four arguments:
 *         the YRfidReader object that emitted the event, the
 *         UTC timestamp of the event, a character string describing
 *         the type of event ("+" or "-") and a character string with the
 *         RFID tag identifier.
 *         On failure, throws an exception or returns a negative error code.
 */
int YRfidReader::registerEventCallback(YEventCallback callback)
{
    _eventCallback = callback;
    _isFirstCb = true;
    if (callback != NULL) {
        this->registerValueCallback(yInternalEventCallback);
    } else {
        this->registerValueCallback((YRfidReaderValueCallback) NULL);
    }
    return 0;
}

int YRfidReader::_internalEventHandler(string cbVal)
{
    int cbPos = 0;
    int cbDPos = 0;
    string url;
    string content;
    string contentStr;
    vector<string> eventArr;
    int arrLen = 0;
    string lenStr;
    int arrPos = 0;
    string eventStr;
    int eventLen = 0;
    string hexStamp;
    int typePos = 0;
    int dataPos = 0;
    int intStamp = 0;
    string binMStamp;
    int msStamp = 0;
    double evtStamp = 0.0;
    string evtType;
    string evtData;
    // detect possible power cycle of the reader to clear event pointer
    cbPos = atoi((cbVal).c_str());
    cbPos = (cbPos / 1000);
    cbDPos = ((cbPos - _prevCbPos) & 0x7ffff);
    _prevCbPos = cbPos;
    if (cbDPos > 16384) {
        _eventPos = 0;
    }
    if (!(_eventCallback != NULL)) {
        return YAPI_SUCCESS;
    }
    if (_isFirstCb) {
        // first emulated value callback caused by registerValueCallback:
        // retrieve arrivals of all tags currently present to emulate arrival
        _isFirstCb = false;
        _eventStamp = 0;
        content = this->_download("events.txt");
        contentStr = content;
        eventArr = _strsplit(contentStr,'\n');
        arrLen = (int)eventArr.size();
        if (!(arrLen > 0)) {
            _throw((YRETCODE)(YAPI_IO_ERROR), "fail to download events");
            return YAPI_IO_ERROR;
        }
        // first element of array is the new position preceeded by '@'
        arrPos = 1;
        lenStr = eventArr[0];
        lenStr = lenStr.substr(1, (int)(lenStr).length()-1);
        // update processed event position pointer
        _eventPos = atoi((lenStr).c_str());
    } else {
        // load all events since previous call
        url = YapiWrapper::ysprintf("events.txt?pos=%d",_eventPos);
        content = this->_download(url);
        contentStr = content;
        eventArr = _strsplit(contentStr,'\n');
        arrLen = (int)eventArr.size();
        if (!(arrLen > 0)) {
            _throw((YRETCODE)(YAPI_IO_ERROR), "fail to download events");
            return YAPI_IO_ERROR;
        }
        // last element of array is the new position preceeded by '@'
        arrPos = 0;
        arrLen = arrLen - 1;
        lenStr = eventArr[arrLen];
        lenStr = lenStr.substr(1, (int)(lenStr).length()-1);
        // update processed event position pointer
        _eventPos = atoi((lenStr).c_str());
    }
    // now generate callbacks for each real event
    while (arrPos < arrLen) {
        eventStr = eventArr[arrPos];
        eventLen = (int)(eventStr).length();
        typePos = _ystrpos(eventStr, ":")+1;
        if ((eventLen >= 14) && (typePos > 10)) {
            hexStamp = eventStr.substr(0, 8);
            intStamp = (int)YAPI::_hexStr2Long(hexStamp);
            if (intStamp >= _eventStamp) {
                _eventStamp = intStamp;
                binMStamp = eventStr.substr(8, 2);
                msStamp = (((u8)binMStamp[0])-64) * 32 + ((u8)binMStamp[1]);
                evtStamp = intStamp + (0.001 * msStamp);
                dataPos = _ystrpos(eventStr, "=")+1;
                evtType = eventStr.substr(typePos, 1);
                evtData = "";
                if (dataPos > 10) {
                    evtData = eventStr.substr(dataPos, eventLen-dataPos);
                }
                if (_eventCallback != NULL) {
                    _eventCallback(this, evtStamp, evtType, evtData);
                }
            }
        }
        arrPos = arrPos + 1;
    }
    return YAPI_SUCCESS;
}

YRfidReader *YRfidReader::nextRfidReader(void)
{
    string  hwid;

    if(YISERR(_nextFunction(hwid)) || hwid=="") {
        return NULL;
    }
    return YRfidReader::FindRfidReader(hwid);
}

YRfidReader *YRfidReader::FirstRfidReader(void)
{
    vector<YFUN_DESCR>   v_fundescr;
    YDEV_DESCR             ydevice;
    string              serial, funcId, funcName, funcVal, errmsg;

    if(YISERR(YapiWrapper::getFunctionsByClass("RfidReader", 0, v_fundescr, sizeof(YFUN_DESCR), errmsg)) ||
       v_fundescr.size() == 0 ||
       YISERR(YapiWrapper::getFunctionInfo(v_fundescr[0], ydevice, serial, funcId, funcName, funcVal, errmsg))) {
        return NULL;
    }
    return YRfidReader::FindRfidReader(serial+"."+funcId);
}

//--- (end of generated code: YRfidReader implementation)

//--- (generated code: YRfidReader functions)
//--- (end of generated code: YRfidReader functions)
