/*
 * Write encrypted data on Mifare 1K EEPROM 
 * 16 sectors
 * Each sector has 4 blocks 
 * Each block has 16 bytes
 * SECTOR 0 BLOCK 0 IS FOR FACTORY INFO - NO EDIT !!
 * FOR EACH SECTOR : 4rth block meant for Authentication and Access Policy ( block 3, 7, 11..)
 */



#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         9           // Configurable, see typical pin layout above
#define SS_PIN          10          // Configurable, see typical pin layout above

#define BUFFER_SIZE     32          // How many bytes you can write in a block 16 = 1 Sector

MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance

void setup() {
  Serial.begin(9600);        // Initialize serial communications with the PC
  SPI.begin();               // Init SPI bus
  mfrc522.PCD_Init();        // Init MFRC522 card
  Serial.println(F("Starting program : Writing ecrypted data "));
}

void printBufferValues(byte buffer[], int size){
  for (int j=0; j<size; j++){
    Serial.print(buffer[j]);
    Serial.print("-");
  }
  Serial.print("\n");
  return;
}

void loop() {

  // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
  MFRC522::MIFARE_Key key;
  for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;

  // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  /*-------------------------------------------------------------------------------------------------------*/
  /*-------------------------------GET CARD ID INFO--------------------------------------------------------*/
  /*-------------------------------------------------------------------------------------------------------*/

  Serial.print(F("Card UID:"));    //Dump UID
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(mfrc522.uid.uidByte[i], HEX);
  }
  Serial.print(F(" PICC type: "));   // Dump PICC type
  MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
  Serial.println(mfrc522.PICC_GetTypeName(piccType));


  /*-------------------------------------------------------------------------------------------------------*/
  /*-------------------------------SET EEPROM DATA FROM BUFFER---------------------------------------------*/
  /*-------------------------------------------------------------------------------------------------------*/

  byte buffer[16]; // Meant for block 1 2 and 4
  byte dataBuffer[256]; // Buffer for data information 3blocks * 14 sectors left + 2 blocks from sector 1
  byte block;
  MFRC522::StatusCode status; // Status of communication ( OK, ERROR, COLLISION, TIMEOUT, NO_ROOM, INTERNAL_ERROR, INVALID, CRC_WRONG, MIFARE_NACK )
  byte len;
  
  typedef struct{
    byte  famillyName[16];
    byte  firstName[16];
    byte  uid[6]; // user id
    byte  expiringDate[6]; // ddmmyy
    byte  age[3];
    byte  cardType[1]; // Which type of card (allows reader to recognize a certain pattern)
  } userData; // Block 1 2 and 4

  byte serialBuffer[40];
  userData userDataBuffer;

  Serial.setTimeout(60000L) ;     // wait until 60 seconds for input from serial

  // ------------------------------------------IMPORTANT-------------------------------------------------------------//
  // There's a problem with flushing the entry serial buffer, Serial.available() & Serial.read() don't allow --------//
  // me to flush the entry buffer, so try to manually stick to the maximum number of bytes of each entry.------------//
  // If not, Serial.readBytesUntil will automatically read what remains of the buffer and won't ask you for typing.--//
  // IF YOU FIND A SOLUTION, PLEASE SHARE IT WITH ME : zaouali.ale@hotmail.com---------------------------------------//
  // ----------------------------------------------------------------------------------------------------------------//

  /*-------------------------------BLOCK 1 & 2 & 4 - USER DATA--------------------------------------------------*/

  // We put data in userDataBuffer structure
  Serial.println(F("Timeout is set to 1 minute, passed that delay, all bytes will take space value."));
  Serial.println(F("Please stick to the maximum number of bytes for each entry."));
  /*------------------------------------------------famillyName---------------------------------------------*/
  Serial.println(F("Entry : Block 1 - 16 bytes : famillyName"));
  len = Serial.readBytesUntil('\n', (char *) userDataBuffer.famillyName, BUFFER_SIZE) ;                       // read famillyName from serial
  for (byte i = len; i < 16; i++) userDataBuffer.famillyName[i] = ' ';                               // pad with spaces
  printBufferValues(userDataBuffer.famillyName, 16);                                                 // display on Serial
  /*------------------------------------------------firstName-----------------------------------------------*/
  Serial.println(F("Entry : Block 2 - 16 bytes : firstName"));
  len = Serial.readBytesUntil('\n', (char *) userDataBuffer.firstName, BUFFER_SIZE) ;
  for (byte i = len; i < 16; i++) userDataBuffer.firstName[i] = ' ';
  printBufferValues(userDataBuffer.firstName, 16);
  /*------------------------------------------------uid-----------------------------------------------------*/
  Serial.println(F("Entry : Block 4 - 6 bytes : uid           - 0-5"));
  len = Serial.readBytesUntil('\n', (char *) userDataBuffer.uid, BUFFER_SIZE) ; 
  for (byte i = len; i < 6; i++) userDataBuffer.uid[i] = ' ';
  printBufferValues(userDataBuffer.uid, 6);
  /*------------------------------------------------expiringDate--------------------------------------------*/
  Serial.println(F("Entry : Block 4 - 6 bytes : expiringDate  - 6-11"));
  len = Serial.readBytesUntil('\n', (char *) userDataBuffer.expiringDate, BUFFER_SIZE) ; 
  for (byte i = len; i < 6; i++) userDataBuffer.expiringDate[i] = ' ';
  printBufferValues(userDataBuffer.expiringDate, 6);
  /*------------------------------------------------age-----------------------------------------------------*/
  Serial.println(F("Entry : Block 4 - 3 bytes : age           - 12-14"));
  len = Serial.readBytesUntil('\n', (char *) userDataBuffer.age, BUFFER_SIZE) ; 
  for (byte i = len; i < 3; i++) userDataBuffer.age[i] = ' ';
  printBufferValues(userDataBuffer.age, 3);
  /*------------------------------------------------uid-----------------------------------------------------*/
  Serial.println(F("Entry : Block 4 - 1 bytes : cardType        - 15"));
  len = Serial.readBytesUntil('\n', (char *) userDataBuffer.cardType, BUFFER_SIZE) ; 
  for (byte i = len; i < 1; i++) userDataBuffer.cardType[i] = ' ';
  printBufferValues(userDataBuffer.cardType, 1);

  /*--------------------------------------------------------------------------------------------------------*/
  /*----------------------------------------------writing all values----------------------------------------*/
  /*--------------------------------------------------------------------------------------------------------*/

  /*------------------------------------------------famillyName---------------------------------------------*/
  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() block 1 failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  else Serial.println(F("PCD_Authenticate() success: "));
  status = mfrc522.MIFARE_Write(1, userDataBuffer.famillyName, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.println(F("MIFARE_Write() - famillyName - Failure"));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  else Serial.println(F("MIFARE_Write() - famillyName - Success"));
  /*------------------------------------------------firstName-----------------------------------------------*/
  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 2, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() block 2 failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  status = mfrc522.MIFARE_Write(2, userDataBuffer.firstName, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.println(F("MIFARE_Write() - firstName - Failure"));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  else Serial.println(F("MIFARE_Write() - firstName - Success"));
  /*--------------------------------------uid-expiringDate-age-cardType---------------------------------------*/
  // ueag : uid - expiringDate - age - age : 16 bytes => makes a block of 16 bytes
  byte ueag[16];
  int j;
  for(j=0;j<6;j++){ueag[j]=userDataBuffer.uid[j];}
  for(j=6;j<12;j++){ueag[j]=userDataBuffer.expiringDate[j-6];}
  for(j=12;j<15;j++){ueag[j]=userDataBuffer.age[j-12];}
  for(j=15;j<16;j++){ueag[j]=userDataBuffer.cardType[j-15];}

  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 4, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() block 4 failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  status = mfrc522.MIFARE_Write(4, ueag, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.println(F("MIFARE_Write() - ueag - Failure"));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  else Serial.println(F("MIFARE_Write() - ueag - Success"));


  /*------------------------------------------------END-------------------------------------------------*/

  Serial.println(" ");
  mfrc522.PICC_HaltA(); // Halt PICC
  mfrc522.PCD_StopCrypto1();  // Stop encryption on PCD

}
