#include "nfc.h"
#include <SPI.h>
#include <PubSubClient.h>
#include <DESFire.h>
#include "helpers.h"

NFC::NFC(int pin_ss, int pin_rst)
{
    rfid = new DESFire(pin_ss, pin_rst);

    SPI.begin();
    rfid->PCD_Init();
    rfid->PCD_DumpVersionToSerial();
}

MFRC522::Uid NFC::getUID()
{
    return uid;
}

bool NFC::hasCardSelected()
{
    return cardSelected;
}

bool NFC::testNFC()
{
    return rfid->PCD_PerformSelfTest();
}

bool NFC::checkforCard()
{
    // RequestA
    if (!rfid->PICC_IsNewCardPresent())
    {
        return false;
    }

    // PICC_Select ???
    if (!rfid->PICC_ReadCardSerial())
    {
        return false;
    }

    // Check for DESFire
    if (rfid->uid.sak != 0x20)
    {
        return false;
    }

    return true;
}

bool NFC::connecttoCard()
{
    // RATS
    byte ats[16];
    byte atsLength = 16;
    MFRC522::StatusCode state = rfid->PICC_RequestATS(ats, &atsLength);
    if (state != MFRC522::STATUS_OK)
    {
        Serial.println(F("Failed ATS"));
        Serial.println(state);
        rfid->PICC_HaltA();
        return false;
    }

    // PPS
    state = rfid->PICC_ProtocolAndParameterSelection(0x00, 0x11, 0x00);
    if (state != MFRC522::STATUS_OK)
    {
        Serial.println("Failed PPS");
        Serial.println(state);
        rfid->PICC_HaltA();
        return false;
    }

    cardSelected = true;
    uid = rfid->uid;
    pcb = 0x0A;
    return true;
}

bool NFC::disconnectCard()
{
    MFRC522::StatusCode state = rfid->PICC_HaltA();
    if (state != MFRC522::STATUS_OK)
    {
        Serial.println(F("Failed PICC_HaltA"));
        Serial.println(state);
        return false;
    }

    return true;
}

bool NFC::testCard()
{
    return true;
}

MFRC522::StatusCode NFC::Transceive(byte *command, byte command_len, byte *response, byte *response_len)
{
    if (command_len >= 0xFF - 4)
    {
        return MFRC522::STATUS_NO_ROOM;
    }

    MFRC522::StatusCode state;
    byte request_buffer[APDU_BUFFER_SIZE + 4]; // APDU Size + PCB + CID + 2x CRC
    byte request_buffer_size = command_len + 4;

    request_buffer[0] = pcb;
    request_buffer[1] = cid;

    memcpy(&request_buffer[2], command, command_len);

    // Update the PCB
    if (pcb == 0x0A)
        pcb = 0x0B;
    else
        pcb = 0x0A;

    // Calculate CRC_A
    state = rfid->PCD_CalculateCRC(request_buffer, request_buffer_size - 2, &request_buffer[request_buffer_size - 2]);
    if (state != MFRC522::STATUS_OK)
    {
        return state;
    }

    byte response_buffer[APDU_BUFFER_SIZE + 4] = {0}; // APDU Size + PCB + CID + 2x CRC
    byte response_buffer_size = 0xFF;

    printbytes(request_buffer, request_buffer_size);
    state = rfid->PCD_TransceiveData(request_buffer, request_buffer_size, response_buffer, &response_buffer_size);
    if (state != MFRC522::STATUS_OK)
    {
        return state;
    }

    memcpy(response, response_buffer + 2, response_buffer_size - 4);
    *response_len = response_buffer_size - 4;

    return state;
}