#include "mfrc522data.h"
#include <includes.h>

#include "rfid-spi.h"
#include "time.h"
#include "CRC16.h"
#include "datadesc.h"

// данные считанные с карты
mifaredata_t mifare_card_data;
// валидные данные абонемента
abonement_data abonement;

#define START_BLOCK     12
#define NR_KNOWN_KEYS   2

uint8_t knownKeys[NR_KNOWN_KEYS][MF_KEY_SIZE] =  {
    {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // FF FF FF FF FF FF = factory default
    {0xa5, 0xaa, 0xa5, 0xa5, 0xaa, 0xaa}, // A5 AA A5 A5 AA AA
};

mifaredata_t* get_mifare_data()
{
    return &mifare_card_data;
}

void set_mifare_data(mifaredata_t* data)
{
    memcpy(&mifare_card_data, &data, sizeof(mifaredata_t));
}

abonement_data* get_abonement_data()
{
    return &abonement;
}

void set_abonement_data(abonement_data* data)
{
    memcpy(&abonement, &data, sizeof(abonement_data));
}

uint32_t get_mifare_uid()
{
    uint32_t card_uid = 0;
    uid_struct * uid = get_uid();

    if(uid->size == 4)
    {
        memcpy(&card_uid, &uid->uidByte[0], 4);
    }
    
    return card_uid;
} 

/**
 * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tuples C1 is MSB (=4) and C3 is LSB (=1).
 */
void MIFARE_SetAccessBits(	uint8_t *accessBitBuffer,	///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set.
				uint8_t g0,			///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39)
				uint8_t g1,			///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39)
				uint8_t g2,			///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39)
				uint8_t g3			///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39)
								) {
	uint8_t c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2);
	uint8_t c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1);
	uint8_t c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0);
	
	accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF);
	accessBitBuffer[1] =          c1 << 4 | (~c3 & 0xF);
	accessBitBuffer[2] =          c3 << 4 | c2;
} // End MIFARE_SetAccessBits()

bool try_key_a(MIFARE_Key *key)
{
    bool result = false;
    uint8_t buffer[18];
    uint8_t block = START_BLOCK + 3;
    status_code status;

    status = pcd_authenticate(PICC_CMD_MF_AUTH_KEY_A, block, key, get_uid());
    if (status != STATUS_OK) {
        return false;
    }

    // Read block
    uint8_t byteCount = sizeof(buffer);
    status = mifare_read(block, buffer, &byteCount);
    if (status != STATUS_OK) {

    }
    else {
        // Successful read
        result = true;
    }

    return result;
}

bool try_key_b(MIFARE_Key *key)
{
    bool result = false;
    uint8_t buffer[18];
    uint8_t block = START_BLOCK + 3;
    status_code status;

    status = pcd_authenticate(PICC_CMD_MF_AUTH_KEY_B, block, key, get_uid());
    if (status != STATUS_OK) {
        return false;
    }

    // Read block
    uint8_t byteCount = sizeof(buffer);
    status = mifare_read(block, buffer, &byteCount);
    if (status != STATUS_OK) {

    }
    else {
        // Successful read
        result = true;
    }

    return result;
}

mifaredata_t* read_mifare_card()
{
    status_code status;
    uint8_t block = START_BLOCK;
    uint8_t len = 18;
    uint8_t buffer[18];
    uint8_t count = 0;

    MIFARE_Key key;
    for (uint8_t k = 0; k < NR_KNOWN_KEYS; k++) {
        for (uint8_t i = 0; i < MF_KEY_SIZE; i++) {
            key.keyByte[i] = knownKeys[k][i];
        }
        
        if (try_key_a(&key)) {
            break;
        }
        count++;
        
        picc_is_new_card_present();
        picc_read_card_serial();
    }
    
    if(count == NR_KNOWN_KEYS) {
      stop_card_working();
      return NULL;
    }
    
    for(uint8_t i = 0; i < sizeof(mifare_card_data); i += 16)
    {
        status = pcd_authenticate(PICC_CMD_MF_AUTH_KEY_A, block, &key, get_uid());
        if (status != STATUS_OK) {
          return NULL;
        }

        status = mifare_read(block, buffer, &len);
        if (status != STATUS_OK) {
          return NULL;
        }

        block++;
        
        for (uint8_t j = 0; j < 16; j++) {
            ((uint8_t *)&mifare_card_data)[i + j] = buffer[j];
        }
        
        if(block%4 == 3) block++;
    }
    
    CPU_INT32U crc1;
    crc1 = crc16((unsigned char*)&mifare_card_data.abonement_data_copy1.init, sizeof(abonement_data) - sizeof(CPU_INT32U));

    if(crc1 == mifare_card_data.abonement_data_copy1.crc)
    {
        memcpy(&abonement, &mifare_card_data.abonement_data_copy1.init, sizeof(abonement_data));
    }
    else
    {
        CPU_INT32U crc2 = crc16((unsigned char*)&mifare_card_data.abonement_data_copy2.init, sizeof(abonement_data) - sizeof(CPU_INT32U));
        if(crc2 == mifare_card_data.abonement_data_copy2.crc)
        {
            memcpy(&abonement, &mifare_card_data.abonement_data_copy2.init, sizeof(abonement_data));
        }
        else
        {
            for(uint8_t i = 0; i < sizeof(abonement_data); i++)
            {
                ((uint8_t *)&abonement)[i] = 0x00;
            }
        }
    }

    return &mifare_card_data;
}

bool write_mifare_card()
{
    status_code status;
    uint8_t block = START_BLOCK;
    uint8_t count = 0;

    uint8_t trailerBuffer[] = {
    255, 255, 255, 255, 255, 255,       // Keep default key A
    0, 0, 0,
    0,
    255, 255, 255, 255, 255, 255};      // Keep default key B

    for (uint8_t i = 0; i < MF_KEY_SIZE; i++){
        trailerBuffer[i] = knownKeys[1][i];
        trailerBuffer[i + 10] = knownKeys[1][i];
    }
    MIFARE_SetAccessBits(&trailerBuffer[6], 4, 4, 4, 1);
    
    MIFARE_Key key;
    for (uint8_t k = 0; k < NR_KNOWN_KEYS; k++) {
        for (uint8_t i = 0; i < MF_KEY_SIZE; i++) {
            key.keyByte[i] = knownKeys[k][i];
        }
        
        if (try_key_b(&key)) {
            break;
        }
        
        picc_is_new_card_present();
        picc_read_card_serial();
 
        count++;
    }

    if(count == NR_KNOWN_KEYS) {
      stop_card_working();
      return false;
    }

    CPU_INT32U crc;

    memcpy(&mifare_card_data.abonement_data_copy1.init, &abonement, sizeof(abonement_data) - sizeof(CPU_INT32U));
    crc = crc16((unsigned char*)&mifare_card_data.abonement_data_copy1.init, sizeof(abonement_data) - sizeof(CPU_INT32U));
    mifare_card_data.abonement_data_copy1.crc = crc;

    memcpy(&mifare_card_data.abonement_data_copy2.init, &abonement, sizeof(abonement_data) - sizeof(CPU_INT32U));
    crc = crc16((unsigned char*)&mifare_card_data.abonement_data_copy2.init, sizeof(abonement_data) - sizeof(CPU_INT32U));
    mifare_card_data.abonement_data_copy2.crc = crc;

    for(uint8_t i = 0; i < sizeof(mifare_card_data); i += 16)
    {
        status = pcd_authenticate(PICC_CMD_MF_AUTH_KEY_B, block, &key, get_uid());
        if (status != STATUS_OK) {
          return false;
        }

        status = mifare_write(block, &(((uint8_t *)&mifare_card_data)[i]), 16);
        if (status != STATUS_OK) {
          return false;
        }

        block++;
        
        if(block%4 == 3) {
            // write security
            status = pcd_authenticate(PICC_CMD_MF_AUTH_KEY_B, block, &key, get_uid());
            if (status != STATUS_OK) {
              return false;
            }
            status = mifare_write(block, &trailerBuffer[0], 16);
            if (status != STATUS_OK) {
              return false;
            }
            block++;
        }
    }
    
    return true;
}

void stop_card_working()
{
    picc_halt_a();
    pcd_stop_cryptol();
}

mifaredata_t* init_mifare_card_data()
{
    for(uint8_t i = 0; i < sizeof(mifaredata_t); i++)
    {
      ((uint8_t *)&mifare_card_data)[i] = 0x00;
    }

    for(uint8_t i = 0; i < sizeof(abonement_data); i++)
    {
      ((uint8_t *)&abonement)[i] = 0x00;
    }

    abonement.init = 0xDEADBEEF;

    CPU_INT32U password;
    GetData(&PassDescAdmin, &password, 0, DATA_FLAG_SYSTEM_INDEX);
    abonement.uid[0] = password;
    abonement.number_abonement = get_mifare_uid();

    return &mifare_card_data;
}

bool is_empty_mifare_card()
{
    bool empty = false;

    CPU_INT32U crc1, crc2;
    crc1 = crc16((unsigned char*)&mifare_card_data.abonement_data_copy1.init, sizeof(abonement_data) - sizeof(CPU_INT32U));
    crc2 = crc16((unsigned char*)&mifare_card_data.abonement_data_copy2.init, sizeof(abonement_data) - sizeof(CPU_INT32U));

    if((mifare_card_data.abonement_data_copy1.init != 0xDEADBEEF || crc1 != mifare_card_data.abonement_data_copy1.crc)
    && (mifare_card_data.abonement_data_copy2.init != 0xDEADBEEF || crc2 != mifare_card_data.abonement_data_copy2.crc)
    )
    {
        empty = true;
    }

    return empty;
}

bool need_clear_bonus()
{
  uint32_t SystemTime = GetTimeSec();

  if(SystemTime > abonement.best_before)
  {
      abonement.bonus = 0;
      return true;
  }

  return false;
}
