/**************************************************************************/
/*!
    @file Adafruit_PN532.h

    Forked from https://github.com/adafruit/Adafruit-PN532 on 2023-04-21 to add FeliCa support

    v2.0  - Refactored to add I2C support from Adafruit_NFCShield_I2C library.

    v1.1  - Added full command list
          - Added 'verbose' mode flag to constructor to toggle debug output
          - Changed readPassiveTargetID() to return variable length values
 */
/**************************************************************************/

#ifndef ADAFRUIT_PN532_H
#define ADAFRUIT_PN532_H

#include "Arduino.h"

#include <Adafruit_I2CDevice.h>
#include <Adafruit_SPIDevice.h>

#define PN532_PREAMBLE (0x00)   ///< Command sequence start, byte 1/3
#define PN532_STARTCODE1 (0x00) ///< Command sequence start, byte 2/3
#define PN532_STARTCODE2 (0xFF) ///< Command sequence start, byte 3/3
#define PN532_POSTAMBLE (0x00)  ///< EOD

#define PN532_HOSTTOPN532 (0xD4) ///< Host-to-PN532
#define PN532_PN532TOHOST (0xD5) ///< PN532-to-host

// PN532 Commands
#define PN532_COMMAND_DIAGNOSE (0x00)              ///< Diagnose
#define PN532_COMMAND_GETFIRMWAREVERSION (0x02)    ///< Get firmware version
#define PN532_COMMAND_GETGENERALSTATUS (0x04)      ///< Get general status
#define PN532_COMMAND_READREGISTER (0x06)          ///< Read register
#define PN532_COMMAND_WRITEREGISTER (0x08)         ///< Write register
#define PN532_COMMAND_READGPIO (0x0C)              ///< Read GPIO
#define PN532_COMMAND_WRITEGPIO (0x0E)             ///< Write GPIO
#define PN532_COMMAND_SETSERIALBAUDRATE (0x10)     ///< Set serial baud rate
#define PN532_COMMAND_SETPARAMETERS (0x12)         ///< Set parameters
#define PN532_COMMAND_SAMCONFIGURATION (0x14)      ///< SAM configuration
#define PN532_COMMAND_POWERDOWN (0x16)             ///< Power down
#define PN532_COMMAND_RFCONFIGURATION (0x32)       ///< RF config
#define PN532_COMMAND_RFREGULATIONTEST (0x58)      ///< RF regulation test
#define PN532_COMMAND_INJUMPFORDEP (0x56)          ///< Jump for DEP
#define PN532_COMMAND_INJUMPFORPSL (0x46)          ///< Jump for PSL
#define PN532_COMMAND_INLISTPASSIVETARGET (0x4A)   ///< List passive target
#define PN532_COMMAND_INATR (0x50)                 ///< ATR
#define PN532_COMMAND_INPSL (0x4E)                 ///< PSL
#define PN532_COMMAND_INDATAEXCHANGE (0x40)        ///< Data exchange
#define PN532_COMMAND_INCOMMUNICATETHRU (0x42)     ///< Communicate through
#define PN532_COMMAND_INDESELECT (0x44)            ///< Deselect
#define PN532_COMMAND_INRELEASE (0x52)             ///< Release
#define PN532_COMMAND_INSELECT (0x54)              ///< Select
#define PN532_COMMAND_INAUTOPOLL (0x60)            ///< Auto poll
#define PN532_COMMAND_TGINITASTARGET (0x8C)        ///< Init as target
#define PN532_COMMAND_TGSETGENERALBYTES (0x92)     ///< Set general bytes
#define PN532_COMMAND_TGGETDATA (0x86)             ///< Get data
#define PN532_COMMAND_TGSETDATA (0x8E)             ///< Set data
#define PN532_COMMAND_TGSETMETADATA (0x94)         ///< Set metadata
#define PN532_COMMAND_TGGETINITIATORCOMMAND (0x88) ///< Get initiator command
#define PN532_COMMAND_TGRESPONSETOINITIATOR (0x90) ///< Response to initiator
#define PN532_COMMAND_TGGETTARGETSTATUS (0x8A)     ///< Get target status

#define PN532_RESPONSE_INDATAEXCHANGE (0x41)      ///< Data exchange
#define PN532_RESPONSE_INLISTPASSIVETARGET (0x4B) ///< List passive target

#define PN532_WAKEUP (0x55) ///< Wake

#define PN532_SPI_STATREAD (0x02)  ///< Stat read
#define PN532_SPI_DATAWRITE (0x01) ///< Data write
#define PN532_SPI_DATAREAD (0x03)  ///< Data read
#define PN532_SPI_READY (0x01)     ///< Ready

#define PN532_I2C_ADDRESS (0x48 >> 1) ///< Default I2C address
#define PN532_I2C_READBIT (0x01)      ///< Read bit
#define PN532_I2C_BUSY (0x00)         ///< Busy
#define PN532_I2C_READY (0x01)        ///< Ready
#define PN532_I2C_READYTIMEOUT (20)   ///< Ready timeout

#define PN532_MIFARE_ISO14443A (0x00) ///< MiFare
#define PN532_FELICA_212       (0x01)
#define PN532_FELICA_424       (0x02)

// Mifare Commands
#define MIFARE_CMD_AUTH_A (0x60)           ///< Auth A
#define MIFARE_CMD_AUTH_B (0x61)           ///< Auth B
#define MIFARE_CMD_READ (0x30)             ///< Read
#define MIFARE_CMD_WRITE (0xA0)            ///< Write
#define MIFARE_CMD_TRANSFER (0xB0)         ///< Transfer
#define MIFARE_CMD_DECREMENT (0xC0)        ///< Decrement
#define MIFARE_CMD_INCREMENT (0xC1)        ///< Increment
#define MIFARE_CMD_STORE (0xC2)            ///< Store
#define MIFARE_ULTRALIGHT_CMD_WRITE (0xA2) ///< Write (MiFare Ultralight)

// Prefixes for NDEF Records (to identify record type)
#define NDEF_URIPREFIX_NONE (0x00)         ///< No prefix
#define NDEF_URIPREFIX_HTTP_WWWDOT (0x01)  ///< HTTP www. prefix
#define NDEF_URIPREFIX_HTTPS_WWWDOT (0x02) ///< HTTPS www. prefix
#define NDEF_URIPREFIX_HTTP (0x03)         ///< HTTP prefix
#define NDEF_URIPREFIX_HTTPS (0x04)        ///< HTTPS prefix
#define NDEF_URIPREFIX_TEL (0x05)          ///< Tel prefix
#define NDEF_URIPREFIX_MAILTO (0x06)       ///< Mailto prefix
#define NDEF_URIPREFIX_FTP_ANONAT (0x07)   ///< FTP
#define NDEF_URIPREFIX_FTP_FTPDOT (0x08)   ///< FTP dot
#define NDEF_URIPREFIX_FTPS (0x09)         ///< FTPS
#define NDEF_URIPREFIX_SFTP (0x0A)         ///< SFTP
#define NDEF_URIPREFIX_SMB (0x0B)          ///< SMB
#define NDEF_URIPREFIX_NFS (0x0C)          ///< NFS
#define NDEF_URIPREFIX_FTP (0x0D)          ///< FTP
#define NDEF_URIPREFIX_DAV (0x0E)          ///< DAV
#define NDEF_URIPREFIX_NEWS (0x0F)         ///< NEWS
#define NDEF_URIPREFIX_TELNET (0x10)       ///< Telnet prefix
#define NDEF_URIPREFIX_IMAP (0x11)         ///< IMAP prefix
#define NDEF_URIPREFIX_RTSP (0x12)         ///< RTSP
#define NDEF_URIPREFIX_URN (0x13)          ///< URN
#define NDEF_URIPREFIX_POP (0x14)          ///< POP
#define NDEF_URIPREFIX_SIP (0x15)          ///< SIP
#define NDEF_URIPREFIX_SIPS (0x16)         ///< SIPS
#define NDEF_URIPREFIX_TFTP (0x17)         ///< TFPT
#define NDEF_URIPREFIX_BTSPP (0x18)        ///< BTSPP
#define NDEF_URIPREFIX_BTL2CAP (0x19)      ///< BTL2CAP
#define NDEF_URIPREFIX_BTGOEP (0x1A)       ///< BTGOEP
#define NDEF_URIPREFIX_TCPOBEX (0x1B)      ///< TCPOBEX
#define NDEF_URIPREFIX_IRDAOBEX (0x1C)     ///< IRDAOBEX
#define NDEF_URIPREFIX_FILE (0x1D)         ///< File
#define NDEF_URIPREFIX_URN_EPC_ID (0x1E)   ///< URN EPC ID
#define NDEF_URIPREFIX_URN_EPC_TAG (0x1F)  ///< URN EPC tag
#define NDEF_URIPREFIX_URN_EPC_PAT (0x20)  ///< URN EPC pat
#define NDEF_URIPREFIX_URN_EPC_RAW (0x21)  ///< URN EPC raw
#define NDEF_URIPREFIX_URN_EPC (0x22)      ///< URN EPC
#define NDEF_URIPREFIX_URN_NFC (0x23)      ///< URN NFC

// FeliCa Commands
#define FELICA_CMD_POLLING                  (0x00)
#define FELICA_CMD_REQUEST_SERVICE          (0x02)
#define FELICA_CMD_REQUEST_RESPONSE         (0x04)
#define FELICA_CMD_READ_WITHOUT_ENCRYPTION  (0x06)
#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08)
#define FELICA_CMD_REQUEST_SYSTEM_CODE      (0x0C)

// FeliCa consts
#define FELICA_READ_MAX_SERVICE_NUM         16
#define FELICA_READ_MAX_BLOCK_NUM           12 // for typical FeliCa card
#define FELICA_WRITE_MAX_SERVICE_NUM        16
#define FELICA_WRITE_MAX_BLOCK_NUM          10 // for typical FeliCa card
#define FELICA_REQ_SERVICE_MAX_NODE_NUM     32


#define PN532_GPIO_VALIDATIONBIT (0x80) ///< GPIO validation bit
#define PN532_GPIO_P30 (0)              ///< GPIO 30
#define PN532_GPIO_P31 (1)              ///< GPIO 31
#define PN532_GPIO_P32 (2)              ///< GPIO 32
#define PN532_GPIO_P33 (3)              ///< GPIO 33
#define PN532_GPIO_P34 (4)              ///< GPIO 34
#define PN532_GPIO_P35 (5)              ///< GPIO 35

/**
 * @brief Class for working with Adafruit PN532 NFC/RFID breakout boards.
 */
class Adafruit_PN532 {
public:
	Adafruit_PN532(uint8_t clk, uint8_t miso, uint8_t mosi,
	               uint8_t ss);                          // Software SPI
	Adafruit_PN532(uint8_t ss, SPIClass* theSPI = &SPI); // Hardware SPI
	Adafruit_PN532(uint8_t irq, uint8_t reset,
	               TwoWire* theWire = &Wire);              // Hardware I2C
	Adafruit_PN532(uint8_t reset, HardwareSerial* theSer); // Hardware UART
	bool begin(void);

	void reset(void);
	void wakeup(void);

	// Generic PN532 functions
	bool SAMConfig(void);
	uint32_t getFirmwareVersion(void);
	bool sendCommandCheckAck(uint8_t* cmd, uint8_t cmdlen,
	                         uint16_t timeout = 100);
	bool writeGPIO(uint8_t pinstate);
	uint8_t readGPIO(void);
	bool setPassiveActivationRetries(uint8_t maxRetries);

	// ISO14443A functions
	bool readPassiveTargetID(
		uint8_t cardbaudrate, uint8_t* uid, uint8_t* uidLength,
		uint16_t timeout = 0);     // timeout 0 means no timeout - will block forever.
	bool startPassiveTargetIDDetection(uint8_t cardbaudrate);
	bool readDetectedPassiveTargetID(uint8_t* uid, uint8_t* uidLength);
	bool readDetectedPassiveTargetID(uint8_t* uid, uint8_t* uidLength, uint16_t* atqa, uint8_t* sak);
	bool readDetectedPassiveTargetIDFeliCa(uint8_t* IDm, uint8_t* IDmLength);
	bool inDataExchange(uint8_t* send, uint8_t sendLength, uint8_t* response,
	                    uint8_t* responseLength);
	bool inListPassiveTarget();
	uint8_t AsTarget();
	uint8_t getDataTarget(uint8_t* cmd, uint8_t* cmdlen);
	uint8_t setDataTarget(uint8_t* cmd, uint8_t cmdlen);

	// Mifare Classic functions
	bool mifareclassic_IsFirstBlock(uint32_t uiBlock);
	bool mifareclassic_IsTrailerBlock(uint32_t uiBlock);
	uint8_t mifareclassic_AuthenticateBlock(uint8_t* uid, uint8_t uidLen,
	                                        uint32_t blockNumber,
	                                        uint8_t keyNumber, uint8_t* keyData);
	uint8_t mifareclassic_ReadDataBlock(uint8_t blockNumber, uint8_t* data);
	uint8_t mifareclassic_WriteDataBlock(uint8_t blockNumber, uint8_t* data);
	uint8_t mifareclassic_FormatNDEF(void);
	uint8_t mifareclassic_WriteNDEFURI(uint8_t sectorNumber,
	                                   uint8_t uriIdentifier, const char* url);

	// Mifare Ultralight functions
	uint8_t mifareultralight_ReadPage(uint8_t page, uint8_t* buffer);
	uint8_t mifareultralight_WritePage(uint8_t page, uint8_t* data);

	// NTAG2xx functions
	uint8_t ntag2xx_ReadPage(uint8_t page, uint8_t* buffer);
	uint8_t ntag2xx_WritePage(uint8_t page, uint8_t* data);
	uint8_t ntag2xx_WriteNDEFURI(uint8_t uriIdentifier, char* url,
	                             uint8_t dataLen);

	// Help functions to display formatted text
	static void PrintHex(const byte* data, const uint32_t numBytes);
	static void PrintHexChar(const byte* pbtData, const uint32_t numBytes);

private:
	int8_t _irq = -1, _reset = -1, _cs = -1;
	int8_t _uid[7];      // ISO14443A uid
	int8_t _uidLen;      // uid len
	int8_t _key[6];      // Mifare Classic key
	int8_t _inListedTag; // Tg number of inlisted tag.

	// Low level communication functions that handle both SPI and I2C.
	void readdata(uint8_t* buff, uint8_t n);
	void writecommand(uint8_t* cmd, uint8_t cmdlen);
	bool isready();
	bool waitready(uint16_t timeout);
	bool readack();

	Adafruit_SPIDevice* spi_dev = NULL;
	Adafruit_I2CDevice* i2c_dev = NULL;
	HardwareSerial* ser_dev = NULL;

	bool releaseCardFeliCa();

	int8_t requestServiceFeliCa(uint8_t numNode, const uint16_t* nodeCodeList, uint16_t* keyVersions, const uint8_t* idm);
	int8_t readWithoutEncryptionFeliCa(uint8_t numService, const uint16_t* serviceCodeList, uint8_t numBlock, const uint16_t* blockList, uint8_t blockData[][16], const uint8_t* idm);
};

#endif
