#include "licenceELC1.h"
#ifndef EZ_APPLICATION_LICENCE_DISABLE

#define CID_LENGTH 32
#define CSD_LENGTH 32

const std::string cEzLic_p78ou3_sdinfofilepath = "/sys/block/mmcblk0/device/";
const std::string cEzLic_p78ou3_licencfilepath_f21 = "/mnt/mmc/ez_sys/licence/";
const std::string cEzLic_p78ou3_licencfilepath_f17 = "/home/admin/ez/licence/";
const string cEzLic_p78ou3_licencfilepath_f10 = "/root/ez_sys/licence/";
const WORD maxDataToFile = 10000; // velikost datoveho bufferu pro ulozeni dat licence

const BYTE licMaxCount = 100;
const DWORD cEzLic_p78ou3_CheckLicNumber_ERR = 0xFFFFFFFF;                          // 16#FFFFFFFF; // chybna identifikace licence
const DWORD cEzLic_p78ou3_CheckLicNumber_EOV1 = 3781234965;                         // cislo pro overeni licence EOV, OSV verze 1
const BYTE cEzLic_p78ou3_CheckLicNumberId_ERR = 0;                                  // id licence pro neidentifikovanou licenci
const BYTE cEzLic_p78ou3_CheckLicNumberId_EOV1 = 1;                                 // id licence pro EOV, OSV verze 1
const BYTE cEzLic_p78ou3_MaxCheckLicNumberId = cEzLic_p78ou3_CheckLicNumberId_EOV1; // pocet identifikatoru licenci
const DWORD cEzLic_p78ou3_CheckLicNumber[] = {cEzLic_p78ou3_CheckLicNumber_ERR, cEzLic_p78ou3_CheckLicNumber_EOV1};
const WORD cEzLic_p78ou3_LicPrefixType_ELC1 = 1; //	prefix typ1 = pouze zasifrovani dat
const WORD cEzLic_p78ou3_HeaderType_10 = 10;     // hlavicka kriptovane casti verze 1.0
const WORD cEzLic_p78ou3_DataType_10 = 10;       // data licence verze 1.0
const WORD cEzLic_p78ou3_SubType_10_10 = 0x0A0A; // subtype - verze hlavicky + verze data -> cEzLic_p78ou3_HeaderType_XX * 256 + cEzLic_p78ou3_DataType_XX
// ID aplikace
const WORD cEzLic_p78ou3_IDType_DDTS = 1;   // aplikace DDTS
const WORD cEzLic_p78ou3_IDType_EOVOSV = 2; // aplikace EOV-OSV
const WORD cEzLic_p78ou3_IDType_DRT = 3;    // aplikace DRT
// std::string cEzLic_p78ou3_IDTypeStrData11[] = {"neznamo", "DDTS", "EOV-OSV", "DRT"};
const BYTE cnibblescount1 = 32;
std::string cSDMonthStr1[] = {"-", "I.", "II.", "III.", "IV.", "V.", "VI.", "VII.", "VIII.", "IX.", "X.", "XI.", "XII.", "-", "-", "-"};
BYTE cHexNibble_to_No1[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0,
                             0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                             0, 0, 0, 0, 0, 0, 0,
                             10, 11, 12, 13, 14, 15,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0,
                             10, 11, 12, 13, 14, 15,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

const string eoseovLicenceType = "EOV_OSV";

LicenceELC1::LicenceELC1() {}

LicenceELC1::~LicenceELC1() {}

LicenceELC1::LicenceELC1(LicenceIdentification &licIdentification) : lIdentification(licIdentification)
{
}

bool LicenceELC1::getSDData()
{
  for (int i = 0; i < CID_LENGTH; i++) this->sdData.CID_nibble[i] = (BYTE)cid[i];

  for (int i = 0; i < cnibblescount / 2; i++)
  {
    this->sdData.CID[i] = cHexNibble_to_No1[this->sdData.CID_nibble[2 * i]] << 4 | cHexNibble_to_No1[this->sdData.CID_nibble[2 * i + 1]];
  }

  this->sdData.manufacturerID = this->sdData.CID[0];
  this->sdData.oemID[0] = this->sdData.CID[1];
  this->sdData.oemID[1] = this->sdData.CID[2];
  this->sdData.name[0] = this->sdData.CID[3];
  this->sdData.name[1] = this->sdData.CID[4];
  this->sdData.name[2] = this->sdData.CID[5];
  this->sdData.name[3] = this->sdData.CID[6];
  this->sdData.name[4] = this->sdData.CID[7];
  this->sdData.productRevision_hw = cHexNibble_to_No1[this->sdData.CID[16]];
  this->sdData.productRevision_sw = cHexNibble_to_No1[this->sdData.CID[17]];

  if (this->sdData.productRevision_sw < 10)
    this->sdData.productRevision = (float)this->sdData.productRevision_hw + ((float)this->sdData.productRevision_sw * 0.1);
  else
    this->sdData.productRevision = (float)this->sdData.productRevision_hw + ((float)this->sdData.productRevision_sw * 0.01);

  this->sdData.serialNumber = this->sdData.CID[9] << 24 | this->sdData.CID[10] << 16 | this->sdData.CID[11] << 8 | this->sdData.CID[12];
  this->sdData.manufacturerDate_year = cHexNibble_to_No1[this->sdData.CID_nibble[27]] * 10 + cHexNibble_to_No1[this->sdData.CID_nibble[28]] + 2000;
  this->sdData.manufacturerDate_month = cHexNibble_to_No1[this->sdData.CID_nibble[29]];
  string date = cSDMonthStr1[this->sdData.manufacturerDate_month] + std::to_string(this->sdData.manufacturerDate_year);

  for (unsigned i = 0; i < date.length(); i++)
    this->sdData.manufacturerDate[i] = date[i];

  // CSD
  for (unsigned int i = 0; i < CSD_LENGTH; i++) this->sdData.CSD_nibble[i] = (BYTE)csd[i];

  for (unsigned int i = 0; i < cnibblescount / 2; i++)
  {
    this->sdData.CSD[i] = cHexNibble_to_No1[this->sdData.CSD_nibble[2 * i]] << 4 | cHexNibble_to_No1[this->sdData.CSD_nibble[2 * i + 1]];
  }

  if (this->sdData.CSD_nibble[0] == 0x34)
  {
    this->sdData.cardSize = this->sdData.CSD[7] << 16 | this->sdData.CSD[8] << 8 | this->sdData.CSD[9];
    this->sdData.cardGSize = (this->sdData.cardSize + 1) / 2048;
  }

  BYTE sdCrc = 0;
  for (int i = 0; i <= 14; i++)
  {
    BYTE sdChar = this->sdData.CID[i];
    for (int j = 0; j <= 7; j++)
    {
      sdCrc = sdCrc << 1;
      if ((sdChar ^ sdCrc) & 0x80)
        sdCrc = sdCrc ^ 0x09;
      sdChar = sdChar << 1;
    }
    sdCrc = sdCrc & 0x7F;
  }
  this->sdData.CRCOK = ((sdCrc << 1) | 1) == this->sdData.CID[15];

  return (int)this->sdData.CID[15] == (int)((sdCrc << 1) | 1);

}

void LicenceELC1::initCrypto()
{
  if (this->licType == cEzLic_p78ou3_IDType_EOVOSV)
  {
    this->cryptData.aesInitVector[0] = this->sdData.CID[10];
    this->cryptData.aesInitVector[1] = this->sdData.CID[12];
    this->cryptData.aesInitVector[2] = this->sdData.CID[11];
    this->cryptData.aesInitVector[3] = this->sdData.CID[9];
    this->cryptData.aesInitVector[4] = this->sdData.CID_nibble[22] - 15;
    this->cryptData.aesInitVector[5] = this->sdData.CID_nibble[24] - 15;
    this->cryptData.aesInitVector[6] = this->sdData.CID_nibble[25] - 15;
    this->cryptData.aesInitVector[7] = this->sdData.CID_nibble[21] - 15;
    memcpy(&this->cryptData.aesInitVector[8], &this->cryptData.aesInitVector[0], 8);

    this->cryptData.aesKey[0] = this->sdData.CID[12];
    this->cryptData.aesKey[1] = this->sdData.CID_nibble[23] - 15;
    this->cryptData.aesKey[2] = this->sdData.CID_nibble[25] - 15;
    this->cryptData.aesKey[3] = this->sdData.CID[11];
    this->cryptData.aesKey[4] = this->sdData.CID[9];
    this->cryptData.aesKey[5] = this->sdData.CID_nibble[21] - 15;
    this->cryptData.aesKey[6] = 97 + this->sdData.CID[9] % 25;
    this->cryptData.aesKey[7] = this->sdData.CID_nibble[22] - 15;
    memcpy(&this->cryptData.aesKey[8], &this->cryptData.aesKey[0], 8);
    memcpy(&this->cryptData.aesKey[16], &this->cryptData.aesKey[6], 8);
    memcpy(&this->cryptData.aesKey[24], &this->cryptData.aesKey[12], 8);
  }
}

string LicenceELC1::getLicenceName()
{
  string result = "";
  char prefixChar = 97;
  int licIndex = lIdentification.licenceIndex;

  // natvrdo, stará ELC
  result = "ezlic_eovosv" + to_string(licIndex) + "_";
  result += prefixChar + (this->sdCard.cardData.CID[12] % 25);
  result += prefixChar + (this->sdCard.cardData.CID[10] % 25);
  result += prefixChar + (this->sdCard.cardData.CID_nibble[22] % 25);
  result += prefixChar + ((this->sdCard.cardData.CID_nibble[23] * 2) % 25);
  result += prefixChar + (this->sdCard.cardData.CID_nibble[24] % 25);
  result += prefixChar + ((this->sdCard.cardData.CID_nibble[25] * 3) % 25);
  result += prefixChar + (this->sdCard.cardData.CID[9] % 25);
  result += prefixChar + (this->sdCard.cardData.CID[11] % 25);
  result += prefixChar + (this->sdCard.cardData.CID[2] % 25);
  result += prefixChar + (this->sdCard.cardData.CID[1] % 25);
  result += prefixChar + (this->sdCard.cardData.CID[3] % 25);
  result += ".lic";

  return result;
}

/// @brief get proper licencename
/// @param licPostfix
/// @return
string LicenceELC1::getLicenceName(BYTE licPostfix)
{
  string result = "";
  char prefixChar = 97;

  if (licType == PlcLicenceType::LicenceOther)
  {
    result = "ezlic_";
    result += prefixChar + (this->sdData.CID[12] % 25);
    result += prefixChar + (this->sdData.CID[10] % 25);
    result += prefixChar + (this->sdData.CID_nibble[22] % 25);
    result += prefixChar + ((this->sdData.CID_nibble[23] * 2) % 25);
    result += prefixChar + (this->sdData.CID_nibble[24] % 25);
    result += prefixChar + ((this->sdData.CID_nibble[25] * 3) % 25);
    result += prefixChar + (this->sdData.CID[9] % 25);
    result += prefixChar + (this->sdData.CID[11] % 25);
    result += prefixChar + (this->sdData.CID[2] % 25);
    result += prefixChar + (this->sdData.CID[1] % 25);
    result += prefixChar + (this->sdData.CID[3] % 25);
    result += ".lic";
  }
  else
  {
    result = "ezlic_eovosv" + to_string(licPostfix) + "_";
    result += prefixChar + (this->sdData.CID[12] % 25);
    result += prefixChar + (this->sdData.CID[10] % 25);
    result += prefixChar + (this->sdData.CID_nibble[22] % 25);
    result += prefixChar + ((this->sdData.CID_nibble[23] * 2) % 25);
    result += prefixChar + (this->sdData.CID_nibble[24] % 25);
    result += prefixChar + ((this->sdData.CID_nibble[25] * 3) % 25);
    result += prefixChar + (this->sdData.CID[9] % 25);
    result += prefixChar + (this->sdData.CID[11] % 25);
    result += prefixChar + (this->sdData.CID[2] % 25);
    result += prefixChar + (this->sdData.CID[1] % 25);
    result += prefixChar + (this->sdData.CID[3] % 25);
    result += ".lic";
  }
  return result;
}

#endif