#include <cstring>
#include <fstream>
#include <filesystem>
#include "plcLicence.h"
#include "utils.h"
#include "pugixml.hpp"

#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_IDTypeStrData[] = {"neznamo", "DDTS", "EOV-OSV", "DRT"};
const BYTE cnibblescount = 32;
std::string cSDMonthStr[] = {"-", "I.", "II.", "III.", "IV.", "V.", "VI.", "VII.", "VIII.", "IX.", "X.", "XI.", "XII.", "-", "-", "-"};
BYTE cHexNibble_to_No[] = {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};
std::string cEzLic_p78ou3_CipherAES = "aes-256-cbc"; //	konfigurace kriptovaciho algoritmu

const string eoseovLicenceType = "EOV_OSV";
//enum LicenceType { EovOsv = 1 };

PlcLicence::PlcLicence()
{
}

PlcLicence::PlcLicence(string cisArg, string csdArg, string binaryType, string dataFileName)
{

  for (int i = 0; i < cisArg.length(); i++)
    this->cid[i] = cisArg[i];
  for (int i = 0; i < csdArg.length(); i++)
    this->csd[i] = csdArg[i];

  this->stationName = "";
  this->distributor = "";
  this->licType = 2;

  processConfigFile(dataFileName);

  if (binaryType == "base64")
    this->binaryGeneration = BinaryGenerationType::Base64Cout;
  else
    this->binaryGeneration = BinaryGenerationType::File;
}

/// @brief process config file
/// @param dataFileName
void PlcLicence::processConfigFile(string &dataFileName)
{
  string fullFile = getCompletePath(dataFileName);
  const int fileNameLength = fullFile.length();
  char fileName[fileNameLength] = {};
  getCharsFromString(fullFile, fileName, fileNameLength);

  std::map<std::string, LicenceType> licTypes;
  licTypes["EOV_OSV"] = LicenceType::LicenceEov;

  pugi::xml_document doc;
  pugi::xml_parse_result result = doc.load_file(fileName);
  if (result)
  {
    this->stationName = doc.child("data").child("station").child_value();
    this->distributor = doc.child("data").child("distributor").child_value();
    this->dataLicenceType = doc.child("data").child("licenceType").child_value();
    this->dataLicenceVersion = doc.child("data").child("licenceType").attribute("version").value();
    this->dataGenerationType = this->dataLicenceVersion;
    this->dataCryptoVersion = this->dataLicenceVersion;
    // if (licType == "EOV_OSV") this->licType = cEzLic_p78ou3_IDType_EOVOSV;// 1;
    //  cout << doc.child("data").child("plcType").attribute("t").value() << endl; //přiklad pro atributy
  }
  else
  {
    std::cerr << "Unable to open the file." << std::endl;
  }
}

/// @brief reads data from CID a CSD
void PlcLicence::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_No[this->sdData.CID_nibble[2 * i]] << 4 | cHexNibble_to_No[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_No[this->sdData.CID[16]];
  this->sdData.productRevision_sw = cHexNibble_to_No[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_No[this->sdData.CID_nibble[27]] * 10 + cHexNibble_to_No[this->sdData.CID_nibble[28]] + 2000;
  this->sdData.manufacturerDate_month = cHexNibble_to_No[this->sdData.CID_nibble[29]];
  string date = cSDMonthStr[this->sdData.manufacturerDate_month] + std::to_string(this->sdData.manufacturerDate_year);

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

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

  for (int i = 0; i < cnibblescount / 2; i++)
  {
    //
    this->sdData.CSD[i] = cHexNibble_to_No[this->sdData.CSD_nibble[2 * i]] << 4 | cHexNibble_to_No[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;

    if (this->sdData.cardSize > 17200) // TODO result.cardSize
    {
    }
  }

  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];
}

/// @brief inits keys and vectors for encrypting
void PlcLicence::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);
  }
}

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

  if (licPostfix > 9) // chyba
  {
  }

  if (licType == LicenceType::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;
}

bool PlcLicence::CreateLicence()
{
  if (this->dataLicenceType == eoseovLicenceType)
  {
    return CreateEosEovLicence();
  }
  else
  {
    return false;
  }
}

bool PlcLicence::CreateEosEovLicence()
{
  getSDData();
  initCrypto();

  // promenne pro praci se soubory a adresari
  LicenceDataMainELC dataMainToFileELC1; //: tEz_LicenceDataMainELC1;	hlavicka urcena pro zapis do souboru - typ ELC1
  LicenceData dataToFile;                // tEz_LicenceData_10_10;	// data urcena pro sifrovani a zapis do soubotu - verze => hlavicka: 1.0, data: 1.0

  // pomocne promenne
  BYTE dataWriteToFile[maxDataToFile]; // OF BYTE; data zapisovana do souboru
  string licencePostfix;               //: STRING; pomocna promenna pro kontrolu postfixu
  string mainLicDescription;           //: STRING(79); globalni pojmenovani licence v nesifrovane casti licence

  UINT freeDataSize;  //: UINT;	velikost volne citelnych dat v souboru licenci
  UINT cryptDataSize; //: UINT;	velikost volne cryptovanych dat v souboru licenci
  UINT fullDataSize;  //: UINT;	celkova velikost souboru licenci

  USINT generation = 2; //: USINT; verze SW pro licence

  PlcData plcData;
  plcData.licenceName = "Licence";
  plcData.licenceType = "1";
  plcData.station = stationName;
  plcData.distributor = distributor;

  LicenceSourceData licSourceData;

  if (plcData.licenceType == "EOV" || plcData.licenceType == to_string((int)LicenceType::LicenceEov))
  {
    licSourceData.licType = 0x1AA2; // 6818 ... proč ?
    licSourceData.licPostfix = 0;
    getCharsFromString(plcData.station, licSourceData.licDescription1);
    getCharsFromString(plcData.station, dataToFile.header.licDescription1);
    getCharsFromString(plcData.distributor, licSourceData.licDescription2);
    getCharsFromString(plcData.distributor, dataToFile.header.licDescription2);
  }

  LicenceIdent licIdent;
  licIdent.licPrefixType = licSourceData.licType >> 12;
  licIdent.licHeaderType = licSourceData.licType >> 8 & 0x000F;
  licIdent.licDataType = licSourceData.licType >> 4 & 0x000F;
  licIdent.licSubType = licIdent.licHeaderType << 8 | licIdent.licHeaderType;
  licIdent.licIDType = licSourceData.licType & 0x000F;

  if (licSourceData.licPostfix > 9)
    licSourceData.licPostfix = 0;
  licencePostfix = to_string(licSourceData.licPostfix);

  string licenseFileName = getLicenceName(0);
  licSourceData.licPostfixIdent = right(licencePostfix, 1);

  int sizeOfData = sizeof(dataToFile);

  if (licIdent.licHeaderType == cEzLic_p78ou3_HeaderType_10)
  {
    if (licIdent.licDataType == cEzLic_p78ou3_DataType_10)
    {
      dataToFile.id.version = 10;
      dataToFile.id.cardSize = this->sdData.cardSize;

      switch (licIdent.licIDType)
      {
      case cEzLic_p78ou3_IDType_DDTS:
        mainLicDescription = "ddts ";
        break;
      case cEzLic_p78ou3_IDType_EOVOSV:
        mainLicDescription = "eov, osv ";
        break;
      default:
        mainLicDescription = "neznama app ";
      }

      mainLicDescription += licSourceData.licPostfixIdent;
      mainLicDescription += " => ";

      dataToFile.header.licVersion = 10;
      dataToFile.header.licType = licIdent.licIDType;
      dataToFile.header.licDate = getLicDate(); // 1692230400; //getLicDate(); //1692144000;// getLicDate();
      mainLicDescription += dataToFile.header.licDescription1;
      mainLicDescription += " [";
      mainLicDescription += dataToFile.header.licDescription2;
      mainLicDescription += "]";

      if (licIdent.licIDType == cEzLic_p78ou3_IDType_EOVOSV)
      {
        if (generation == 1) // stará verze
        {
          dataToFile.header.licCount = 1;
          dataToFile.items[0].licCount = 65535;
          dataToFile.items[0].protoId = cEzLic_p78ou3_CheckLicNumberId_EOV1;
          dataToFile.items[0].data1 = cEzLic_p78ou3_CheckLicNumber_EOV1;
        }
        else
        {
          dataToFile.header.licCount = 1;
          dataToFile.items[1].licCount = 65535;
          dataToFile.items[1].protoId = cEzLic_p78ou3_CheckLicNumberId_EOV1;
          dataToFile.items[1].data1 = cEzLic_p78ou3_CheckLicNumber_EOV1;
        }
      }

      unsigned char encrypted[10000] = {};
      const int s = sizeof(dataToFile);
      unsigned char byteArray[s] = {};
      memcpy(byteArray, &dataToFile, sizeof(dataToFile));

      dataToFile.crc = calculateCRC16(byteArray, s - sizeof(dataToFile.crc)); // 47535 | 884:38382

      memcpy(byteArray, &dataToFile, sizeof(dataToFile));

      int finalEncryptedLength = encrypt(byteArray, sizeof(dataToFile), this->cryptData.aesKey, this->cryptData.aesInitVector, encrypted);

      if (licIdent.licPrefixType == cEzLic_p78ou3_LicPrefixType_ELC1) // typ1 = pouze zasifrovani dat
      {
        freeDataSize = sizeof(dataMainToFileELC1); // melo by byt 92

        if (1) // EzLic_p78ou3_CryptoData.aesRetVal > 0 crypto
        {
          dataMainToFileELC1.prefix = 0x31434C45;
          getCharsFromString(mainLicDescription, dataMainToFileELC1.licHeader.description);
          dataMainToFileELC1.licHeader.sizeData = finalEncryptedLength; // sizeof(dataToFile); // 884;
          dataMainToFileELC1.licHeader.licType = licIdent.licIDType;
          dataMainToFileELC1.licHeader.licSubType = licIdent.licHeaderType << 8 | licIdent.licDataType;
        }
      }

      const int dataMainLength = sizeof(dataMainToFileELC1);
      const int dataToFileLength = sizeof(dataToFile);
      const int dataEncryptedLength = finalEncryptedLength;
      const int totalLength = dataMainLength + dataToFileLength;
      const int totalEncryptedLength = dataMainLength + finalEncryptedLength;

      unsigned char bdataMainToFileELC1[dataMainLength] = {};
      memcpy(bdataMainToFileELC1, &dataMainToFileELC1, dataMainLength);

      unsigned char bdataToFile[dataToFileLength] = {};
      memcpy(bdataToFile, &dataToFile, dataToFileLength);
      unsigned char totalEncryptedArray[totalEncryptedLength] = {};

      for (int i = 0; i < dataMainLength; i++)
        totalEncryptedArray[i] = bdataMainToFileELC1[i];
      for (int i = 0; i < finalEncryptedLength; i++)
        totalEncryptedArray[i + dataMainLength] = encrypted[i];

      string fullFile = getCompletePath(licenseFileName);

      if (binaryGeneration == BinaryGenerationType::File)
      {
        std::ofstream outputFile(fullFile);

        if (outputFile.is_open())
        {
          outputFile.write(reinterpret_cast<const char *>(totalEncryptedArray), totalEncryptedLength);
          outputFile.close();
          std::cout << licenseFileName;
        }
        else
        {
          std::cerr << "Unable to open the file." << std::endl;
        }
        return true;
      }
      else
      {
        int totalFileSize = sizeof(totalEncryptedArray);
        // cout << "data:text/plain;base64,";
        cout << "data:application/octet-stream;base64,";

        char encryptedChars[totalFileSize];

        for (int i = 0; i < totalFileSize; i++)
          encryptedChars[i] = static_cast<signed char>(totalEncryptedArray[i]);

        string strToBase = convertToString(encryptedChars, totalFileSize);
        string base64Coded = base64_encode_ai(strToBase);
        cout << base64Coded << endl;

        return true;
      }
    }
  }

  return false;
}

bool PlcLicence::ReadEosEovLicence(string dataFileName)
{
  BYTE licPostfix = 0;
  FILE *licenceFile;
  char ch;
  long lSize;
  size_t result;

  const int fileNameLength = dataFileName.length();
  char fileNameCh[fileNameLength] = {};
  for (int i = 0; i < dataFileName.length(); i++)
    fileNameCh[i] = dataFileName[i];

  cout << dataFileName << endl;

  licenceFile = fopen(fileNameCh, "rb"); // read mode
  fseek(licenceFile, 0, SEEK_END);         // seek to end of file
  const int size = ftell(licenceFile);     // get current file pointer
  fseek(licenceFile, 0, SEEK_SET);

  if (licenceFile == NULL)
  {
    perror("Error while opening the file.\n");
    operationErrors = "Error while opening the file.\n";
    return false;
  }

  int count = 0;

  unsigned char licenceContent[size];

  for (int i = 0; i < size; i++)
  {
    ch = fgetc(licenceFile);
    licenceContent[i] = ch;
    count++;
  }

  fclose(licenceFile);

  // this->cid = cid;
  // this->csd = csd;

  getSDData();

  string licFileName = getLicenceName(licPostfix);

  LicenceDataMainELC licenceHeader;
  LicenceIdent licIdent;
  LicenceData licEncryptedData;

  memset(&licenceHeader, 0, sizeof(LicenceDataMainELC));
  memcpy(&licenceHeader, licenceContent, sizeof(LicenceDataMainELC));

  const int sizeOfEncryptedData = size - sizeof(LicenceDataMainELC);
  unsigned char encryptedData[sizeOfEncryptedData] = {};
  for (int i = 0; i < sizeOfEncryptedData; i++)
    encryptedData[i] = licenceContent[i + sizeof(LicenceDataMainELC)];

  // original jsou testy na velikost přečteného file, zatím TODO

  BYTE prefixType = (int)licenceContent[3] - 0x30;
  if (prefixType == PrefixType::ELC1)
  {
    if (licenceHeader.licHeader.sizeData > 0)
    {
      licIdent.licIDType = licenceHeader.licHeader.licType;
      licIdent.licSubType = licenceHeader.licHeader.licSubType;

      if (licenceHeader.licHeader.licSubType == cEzLic_p78ou3_SubType_10_10) // zatim natvrdo
      {
        initCrypto();
        // CryptData cryptData = initCrypto(sdData, licIdent.licIDType);
        unsigned char decrypted[2000] = {};
        int decrypted_len = decrypt(encryptedData, sizeof(encryptedData), cryptData.aesKey, cryptData.aesInitVector, decrypted);

        if (sizeof(licEncryptedData) != decrypted_len)
        {
          operationErrors = "License size mismatch";
          return false;
        }
        else
        {
          memset(&licEncryptedData, 0, sizeof(licEncryptedData));
          memcpy(&licEncryptedData, decrypted, sizeof(licEncryptedData));

          if (licEncryptedData.id.version == cEzLic_p78ou3_HeaderType_10 && licEncryptedData.header.licVersion == cEzLic_p78ou3_HeaderType_10)
          {
            if (licEncryptedData.header.licType == cEzLic_p78ou3_IDType_EOVOSV)
            {
              if (licEncryptedData.id.cardSize != sdData.cardSize)
              {
                operationErrors = "Size card mismatch";
                return false;
              }

              if (licEncryptedData.header.licCount > 0)
              {

                if (licType == LicenceType::LicenceOther)
                {
                  // TODO stará
                }
                else
                {
                  // maxLic := MIN(EzLlic_5rq4_DataFromFile_10_10.header.licCount, cEzLic_p78ou3_licMaxCount); ?
                  int maxLic = min(licEncryptedData.header.licCount, licMaxCount);
                  for (int i = 1; i < maxLic; i++)
                  {
                    if (licEncryptedData.items[i].protoId > 0 || licEncryptedData.items[i].licCount > 0 || licEncryptedData.items[i].data1 > 0)
                    {
                      // EzLlic_5rq4_CheckData.LicId[EzLlic_5rq4_DataFromFile_10_10.items[licId].protoId] := licId;
                    }
                    else
                    {
                      operationErrors = "Licence items mismatch";
                      return false;
                    }
                  }
                }
              }
              else
              {
                operationErrors = "Size card info mismatch";
                return false;
              }
            }
          }
          else
          {
            operationErrors = "Licence mismatch";
            return false;
          }
        }
      }
    }
    else
    {
      operationErrors = "Licence error";
      return false;
    }
  }
  else
  {
    operationErrors = "Licence error";
    return false;
  }

  cout << "Licence readed: " << size << endl << ", data: " << licenceHeader.licHeader.description << endl;
  return true;
}

bool PlcLicence::ReadLicence(string dataFileName, string licenceType, string licenceVersion, string cidArg, string csdArg)
{

  for (int i = 0; i < cidArg.length(); i++) this->cid[i] = cidArg[i];
  for (int i = 0; i < csdArg.length(); i++) this->csd[i] = csdArg[i];
  this->dataLicenceType = licenceType;
  this->dataLicenceVersion = licenceVersion;

  if (this->dataLicenceType == eoseovLicenceType)
  {
    return ReadEosEovLicence(dataFileName);
  }

  return true;
}

