#include "licenceELC21.h"

#include "utils.h"
#include "SDCard.h"
#include <unordered_map>
#include <vector>
#include <limits>
#include <fstream>

LicenceELC21::LicenceELC21()
{
}

LicenceELC21::LicenceELC21(LicenceIdentification &licIdentification, LicData &licData)
{
  lIdentification = licIdentification;
  lData = licData;
}

LicenceELC21::LicenceELC21(LicenceIdentification &licIdentification)
{
  lIdentification = licIdentification;
}

LicenceELC21::LicenceELC21(LicData &licData)
{
  lData = licData;
  processInputConfiguration();
}

bool LicenceELC21::createLicence()
{
  getLicenceItems();
  getHeader();

  sdCard = SDCard(lData.cid, lData.csd);
  if (sdCard.isLoaded == false)
    cerr << "SD card read error." << endl;
  this->licBody.licenceIdentHeader.cardSize = sdCard.cardData.cardSize;
  this->licBody.licenceIdentHeader.serialNumber = sdCard.cardData.serialNumber;
  this->licBody.licenceIdentHeader.licItemCount = this->licBody.privateContent.dataItems.size();
  this->licBody.licenceIdentHeader.publicHeaderLength = this->licBody.publicHeader.length();

  vector<unsigned char> publicContent;
  publicContent.reserve(1000);
  vector<unsigned char> privateContent;
  privateContent.reserve(1000);
  vector<unsigned char> privateContentEncrypted;
  privateContentEncrypted.reserve(1000);

  publicContent.push_back(this->licBody.licId.licIdent[0]);
  publicContent.push_back(this->licBody.licId.licIdent[1]);
  publicContent.push_back(this->licBody.licId.licIdent[2]);
  publicContent.push_back(((char)48 + (int)this->lIdentification.licElcType));
  publicContent.push_back(this->licBody.licId.licIdent[4]);
  publicContent.push_back(this->licBody.licenceIdentHeader.licenceType);
  publicContent.push_back(this->licBody.licenceIdentHeader.licenceTypeVersion);
  publicContent.push_back(this->licBody.licenceIdentHeader.licenceIndex);
  publicContent.push_back(this->licBody.licenceIdentHeader.compatibilityVersion);
  publicContent.push_back(this->licBody.privateContent.dataItems.size());
  publicContent.push_back(this->licBody.licenceIdentHeader.publicHeaderLength & 0xFF);
  publicContent.push_back((this->licBody.licenceIdentHeader.publicHeaderLength >> 8) & 0xFF);
  publicContent.push_back(this->licBody.licenceIdentHeader.cardSize & 0xFF);
  publicContent.push_back((this->licBody.licenceIdentHeader.cardSize >> 8) & 0xFF);
  publicContent.push_back(this->licBody.licenceIdentHeader.serialNumber & 0xFF);
  publicContent.push_back((this->licBody.licenceIdentHeader.serialNumber >> 8) & 0xFF);
  publicContent.push_back((this->licBody.licenceIdentHeader.serialNumber >> 16) & 0xFF);
  publicContent.push_back((this->licBody.licenceIdentHeader.serialNumber >> 24) & 0xFF);

#ifdef DEB
  cout << "publicHeaderLength: " << this->licBody.licenceIdentHeader.publicHeaderLength << endl;
  cout << "cardSize: " << this->licBody.licenceIdentHeader.cardSize << endl;
  cout << "serialNumber: " << this->licBody.licenceIdentHeader.serialNumber << endl;
  cout << "item count: " << this->licBody.privateContent.dataItems.size() << endl;
#endif

  appendStringToVector(this->licBody.publicHeader, publicContent);
  uint16_t crcPublic = calculateCRC16(publicContent);

#ifdef DEB
  cout << "CRC pubblic: " << crcPublic << endl;
#endif

  publicContent.push_back(crcPublic & 0xFF);
  publicContent.push_back((crcPublic >> 8) & 0xFF);

  privateContent.push_back(this->licBody.licenceIdentHeader.licenceType);
  privateContent.push_back(this->licBody.licenceIdentHeader.licenceTypeVersion);
  privateContent.push_back(this->licBody.licenceIdentHeader.licenceIndex);
  privateContent.push_back(this->licBody.licenceIdentHeader.compatibilityVersion);
  privateContent.push_back(this->licBody.privateContent.dataItems.size());
  privateContent.push_back(this->licBody.licenceIdentHeader.publicHeaderLength & 0xFF);
  privateContent.push_back((this->licBody.licenceIdentHeader.publicHeaderLength >> 8) & 0xFF);
  privateContent.push_back(this->licBody.licenceIdentHeader.cardSize & 0xFF);
  privateContent.push_back((this->licBody.licenceIdentHeader.cardSize >> 8) & 0xFF);
  privateContent.push_back(this->licBody.licenceIdentHeader.serialNumber & 0xFF);
  privateContent.push_back((this->licBody.licenceIdentHeader.serialNumber >> 8) & 0xFF);
  privateContent.push_back((this->licBody.licenceIdentHeader.serialNumber >> 16) & 0xFF);
  privateContent.push_back((this->licBody.licenceIdentHeader.serialNumber >> 24) & 0xFF);

  for (licDataItem dataItem : this->licBody.privateContent.dataItems)
  {
    privateContent.push_back(dataItem.protoId & 0xFF);
    privateContent.push_back((dataItem.protoId >> 8) & 0xFF);
    privateContent.push_back(dataItem.licCount & 0xFF);
    privateContent.push_back((dataItem.licCount >> 8) & 0xFF);
    for (int i = 0; i < sizeof(dataItem.dummy); i++)
      privateContent.push_back(i);
  }

  vector<unsigned char> completeVector = joinVectors(publicContent, privateContent);
  licBody.privateContent.crc = calculateCRC16(completeVector);
  uint16_t crcComplete = calculateCRC16(completeVector);

  privateContent.push_back(crcComplete & 0xFF);
  privateContent.push_back((crcComplete >> 8) & 0xFF);

  privateContentEncrypted = cryptPrivateContent(privateContent);

#ifdef DEB

  cout << endl
       << hex << "test original: " << privateContent.size() << endl;
  for (auto x : privateContent)
    cout << (int)x << "-";

  cout << endl
       << "test encrypt: " << privateContentEncrypted.size() << endl;
  for (auto x : privateContentEncrypted)
    cout << (int)x << "-";

  vector<unsigned char> privateContentDecrypted;
  privateContentDecrypted = decryptPrivateContent(privateContentEncrypted);

  cout << endl
       << "test decrypt: " << privateContentDecrypted.size() << endl;
  for (auto x : privateContentDecrypted)
    cout << (int)x << "-";
#endif

  // return;

  string licfileName = getLicenceName();
  std::ofstream outputFile(licfileName, std::ios::out | std::ios::binary);

  // Check if the file is open
  if (!outputFile.is_open())
  {
    std::cerr << "Error opening file: " << licfileName << std::endl;
    return false;
  }

  std::copy(publicContent.cbegin(), publicContent.cend(), std::ostream_iterator<unsigned char>(outputFile));
  std::copy(privateContentEncrypted.cbegin(), privateContentEncrypted.cend(), std::ostream_iterator<unsigned char>(outputFile));

  outputFile.close();

  cout << licfileName;

  return true;
}

bool LicenceELC21::readLicence(LicenceInfoGeneral * licences)
{
  try
  {
    // sdCard = SDCard("9f544930303030300000000b47015423", "400e00325b5900003a0d7f800a40008d");
    sdCard = SDCard(this->cid_cdsPath);
    if (sdCard.isLoaded == false)
      throw LicenceException((int)Error21::SDCardReadError, "Chyba při čtení SD karty, cesta: " + cid_cdsPath);

    string licFileName = getLicenceName();
    string licFilePath = this->licenceFilePath+licFileName;


    // Create an unsigned char vector to store the file data
    vector<char> content;
    if (readFile(licFilePath, content) == false)
    {
      throw LicenceException((int)GeneralError::FileOpenError, "Chyba otevření souboru licence.");
    }

    this->licBody.licId.licIdent[0] = content[0];
    this->licBody.licId.licIdent[1] = content[1];
    this->licBody.licId.licIdent[2] = content[2];
    this->licBody.licId.licIdent[3] = content[3];
    this->licBody.licId.licIdent[4] = content[4];

    this->licBody.licenceIdentHeader.licenceType = content[5];
    this->licBody.licenceIdentHeader.licenceTypeVersion = content[6];
    this->licBody.licenceIdentHeader.licenceIndex = content[7];
    this->licBody.licenceIdentHeader.compatibilityVersion = content[8];
    this->licBody.licenceIdentHeader.licItemCount = content[9];
    this->licBody.licenceIdentHeader.publicHeaderLength = bytesToWord(content[10], content[11]);
    this->licBody.licenceIdentHeader.cardSize = bytesToWord(content[12], content[13]);
    this->licBody.licenceIdentHeader.serialNumber = bytesToDword(content[14], content[15], content[16], content[17]);

    uint16_t crcPublic = bytesToWord(content[18 + licBody.licenceIdentHeader.publicHeaderLength], content[19 + licBody.licenceIdentHeader.publicHeaderLength]);

#ifdef DEB
    cout << "licenceType: " << to_string(licBody.licenceIdentHeader.licenceType) << endl;
    cout << "licenceTypeVersion: " << to_string(licBody.licenceIdentHeader.licenceTypeVersion) << endl;
    cout << "licenceIndex: " << to_string(licBody.licenceIdentHeader.licenceIndex) << endl;
    cout << "compatibilityVersion: " << to_string(licBody.licenceIdentHeader.compatibilityVersion) << endl;
    cout << "licItemCount: " << to_string(licBody.licenceIdentHeader.licItemCount) << endl;
    cout << "headerLength: " << to_string(licBody.licenceIdentHeader.publicHeaderLength) << endl;
    cout << "card: " << to_string(licBody.licenceIdentHeader.cardSize) << endl;
    cout << "serial: " << to_string(licBody.licenceIdentHeader.serialNumber) << endl;
    cout << "crcPublic: " << to_string(crcPublic) << endl;
#endif

    int elcVersion = (int)licBody.licId.licIdent[3] - 48;

    if (elcVersion != (int)this->lIdentification.licElcType)
    {
      throw LicenceException((int)GeneralError::ELCMismatch, "Nesouhlasí ELC.");
    }

    vector<unsigned char> encryptedPart(content.begin() + licBody.licenceIdentHeader.publicHeaderLength + 20, content.begin() + content.size());

#ifdef DEB
    cout << " encrypted part: " << hex << encryptedPart.size() << endl;
    for (auto x : encryptedPart)
    {
      cout << (int)x << "-";
    }
#endif

    vector<unsigned char> privateContentDecrypted;
    privateContentDecrypted = decryptPrivateContent(encryptedPart);

#ifdef DEB
    cout << dec << endl
         << " decrypted part: " << privateContentDecrypted.size() << endl;
    for (auto x : privateContentDecrypted)
    {
      cout << dec << (int)x << "-";
    }
#endif

    LicenceBody licBodyDecrypted;
    licBodyDecrypted.licenceIdentHeader.licenceType = privateContentDecrypted[0];
    licBodyDecrypted.licenceIdentHeader.licenceTypeVersion = privateContentDecrypted[1];
    licBodyDecrypted.licenceIdentHeader.licenceIndex = privateContentDecrypted[2];
    licBodyDecrypted.licenceIdentHeader.compatibilityVersion = privateContentDecrypted[3];
    licBodyDecrypted.licenceIdentHeader.licItemCount = privateContentDecrypted[4];
    licBodyDecrypted.licenceIdentHeader.publicHeaderLength = bytesToWord(privateContentDecrypted[5], privateContentDecrypted[6]);
    licBodyDecrypted.licenceIdentHeader.cardSize = bytesToWord(privateContentDecrypted[7], privateContentDecrypted[8]);
    licBodyDecrypted.licenceIdentHeader.serialNumber = bytesToDword(privateContentDecrypted[9], privateContentDecrypted[10], privateContentDecrypted[11], privateContentDecrypted[12]);

    //TODO testy na CRC

    if (licBodyDecrypted.licenceIdentHeader.licItemCount != this->licBody.licenceIdentHeader.licItemCount)
    {
      throw LicenceException((int)Error21::ItemsCountMismatch, map21Errors.at(Error21::ItemsCountMismatch));
    }

    int index = 13;
    for (int i = 0; i < this->licBody.licenceIdentHeader.licItemCount; i++)
    {
      licDataItem item;
      item.protoId = bytesToWord(privateContentDecrypted[index], privateContentDecrypted[index + 1]);
      item.licCount = bytesToWord(privateContentDecrypted[index + 2], privateContentDecrypted[index + 3]);
      index = index + sizeof(licDataItem);
      this->licBody.privateContent.dataItems.push_back(item);
      this->licenceInfo.licences.insert(pair<int, int>(item.protoId, item.licCount));
      licences->licences.insert(pair<int, int>(item.protoId, item.licCount));
    }
  }

  catch (const LicenceException &ex)
  {
    errorMessage.code = ex.getErrorCode();
    errorMessage.message = ex.getErrorMessage();
    return false;
  }

  return true;
}

string LicenceELC21::getVersion(int middleVersion)
{
  string result = "";
  result.append(to_string(generatorVersion));
  result.append(".");
  result.append(to_string(middleVersion));
  result.append(".");
  string tempLicenceCount = "3"; // TODO
  result.append(tempLicenceCount);
  return result;
}

bool LicenceELC21::processInputConfiguration()
{
  const char *dataRootName = "licence";
  Mapping mapping;

  string licType = lData.doc->child(dataRootName).child("licenceType").child_value();
  if (!licType.empty())
  {
    this->lIdentification.licLicenceType = mapping.licMapTypes[licType]; // LicenceType::EOS_EOV;
    this->lIdentification.licTypeName = licType;
  }
  else
  {
    cerr << endl
         << " ERROR MISSING licenceType " << endl;
    return false;
  }

  this->lIdentification.licenceVersion = atoi(&lData.doc->child(dataRootName).child("licenceType").attribute("licenceVersion").value()[0]);
  this->lIdentification.revision = lData.doc->child(dataRootName).attribute("revision").value()[0];
  this->lIdentification.licenceIndex = atoi(&lData.doc->child(dataRootName).child("licenceType").attribute("licenceIndex").value()[0]);
  this->lIdentification.licElcType = (ELCType)atoi(&lData.doc->child(dataRootName).attribute("elc").value()[0]);

  this->lIdentification.licCompatibility = atoi(&lData.doc->child(dataRootName).attribute("compatibility").value()[0]);

  string plcType = lData.doc->child(dataRootName).child("plcType").child_value();
  if (!plcType.empty())
  {
    this->lIdentification.licPlcType = mapping.licMapPlcType[plcType];
  }

  this->lData.station = &lData.doc->child(dataRootName).child("station").child_value()[0];
  this->lData.distributor = &lData.doc->child(dataRootName).child("distributor").child_value()[0];
  this->lData.projectDescription = &lData.doc->child(dataRootName).child("project").child_value()[0];

  licBody.licenceIdentHeader.cardSize = 0;
  licBody.licenceIdentHeader.compatibilityVersion = this->lIdentification.licCompatibility;
  licBody.licenceIdentHeader.licenceIndex = this->lIdentification.licenceIndex;
  licBody.licenceIdentHeader.licenceType = this->lIdentification.licenceVersion;
  licBody.licenceIdentHeader.licenceTypeVersion = this->lIdentification.licenceVersion;
  licBody.licenceIdentHeader.serialNumber = 0;

  return true;
}

void LicenceELC21::getLicenceItems()
{
  int nodeIndex = 0;
  int itemIndex = 0;

  for (pugi::xml_node licItemNode : lData.doc->child("licence").child("items"))
  {
    licDataItem item;
#ifdef DEB
    cout << "protoId: " << licItemNode.child("protoId").child_value() << endl;
    cout << "datapoints: " << licItemNode.child("dataPointsCount").child_value() << endl;
#endif

    item.protoId = atoi(licItemNode.child("protoId").child_value());
    item.licCount = atoi(licItemNode.child("dataPointsCount").child_value());

    this->licBody.privateContent.dataItems.push_back(item);

    nodeIndex = 0;
    itemIndex++;
    for (pugi::xml_node child : licItemNode.children())
    {
      nodeIndex++;
      if (nodeIndex == 1)
      {
        licBody.publicHeader.append("\"name\":\"");
        licBody.publicHeader.append(child.child_value());
        licBody.publicHeader.append("\",");
        continue;
      }
      if (nodeIndex == 3)
      {
        licBody.publicHeader.append("\"dataPointsCount\":\"");
        licBody.publicHeader.append(child.child_value());
        licBody.publicHeader.append("\"");
      }
    }
    if (itemIndex != lIdentification.licItemsCount)
    {
      licBody.publicHeader.append("},");
    }
    else
    {
      licBody.publicHeader.append("}");
    }
  }
}

void LicenceELC21::getHeader()
{
  PublicHeader publicHeader;
  publicHeader.version = getVersion(7);
  publicHeader.projectDescription = lData.projectDescription;
  publicHeader.date = getDate();
  publicHeader.licenceType = lIdentification.licTypeName;
  publicHeader.licenceType += to_string(lIdentification.licenceIndex);

  licBody.publicHeader = "";

  licBody.publicHeader.append("{\"version\":");
  licBody.publicHeader.append(publicHeader.version);
  licBody.publicHeader.append("\",");
  // \"1.2.3\",");
  licBody.publicHeader.append("\"project\":\"");
  licBody.publicHeader.append(publicHeader.projectDescription);
  licBody.publicHeader.append("\",");

  licBody.publicHeader.append("\"date\":\"");
  licBody.publicHeader.append(publicHeader.date);
  licBody.publicHeader.append("\",");

  licBody.publicHeader.append("\"note\":\"poznámka\",");
  licBody.publicHeader.append("\"licenceType\":\"");
  licBody.publicHeader.append(publicHeader.licenceType);
  licBody.publicHeader.append("\",");

  licBody.publicHeader.append("\"items\":[");

  for (pugi::xml_node tool : lData.doc->child("licence").child("items"))
  {
    lIdentification.licItemsCount++;
  }

  int nodeIndex = 0;
  int itemIndex = 0;

  for (pugi::xml_node licItemNode : lData.doc->child("licence").child("items"))
  {
    licBody.publicHeader.append("{");
    nodeIndex = 0;
    itemIndex++;
    for (pugi::xml_node child : licItemNode.children())
    {
      nodeIndex++;
      if (nodeIndex == 1)
      {
        licBody.publicHeader.append("\"name\":\"");
        licBody.publicHeader.append(child.child_value());
        licBody.publicHeader.append("\",");
        continue;
      }
      if (nodeIndex == 3)
      {
        licBody.publicHeader.append("\"dataPointsCount\":\"");
        licBody.publicHeader.append(child.child_value());
        licBody.publicHeader.append("\"");
      }
    }
    if (itemIndex != lIdentification.licItemsCount)
    {
      licBody.publicHeader.append("},");
    }
    else
    {
      licBody.publicHeader.append("}");
    }
  }

  licBody.publicHeader.append("]");
  licBody.publicHeader.append("}");
}

vector<unsigned char> LicenceELC21::cryptPrivateContent(const std::vector<unsigned char> &content)
{

  BYTE initVector[15] = {0};
  BYTE aesKey[32] = {0};

  LicenceELC21::initVector(initVector, aesKey);

  unsigned char encrypted[10000] = {};
  const unsigned char *plainTextArray = content.data();
  int finalEncryptedLength = encrypt(plainTextArray, content.size(), aesKey, initVector, encrypted);

  std::size_t charArraySize = sizeof(encrypted) / sizeof(unsigned char);
  std::vector<unsigned char> result(encrypted, encrypted + finalEncryptedLength);

  return result;
}

vector<unsigned char> LicenceELC21::decryptPrivateContent(const std::vector<unsigned char> &content)
{

  BYTE initVector[15] = {0};
  BYTE aesKey[32] = {0};

  LicenceELC21::initVector(initVector, aesKey);

  const unsigned char *encryptedData = content.data();
  unsigned char decrypted[10000] = {};

  int decrypted_len = decrypt(encryptedData, content.size(), aesKey, initVector, decrypted);

  std::size_t charArraySize = sizeof(decrypted) / sizeof(unsigned char);
  std::vector<unsigned char> result(decrypted, decrypted + decrypted_len);

  return result;
}

void LicenceELC21::initVector(BYTE (&iVector)[], BYTE (&key)[])
{

  struct Vector15
  {
    int vec[15];
  };

  Vector15 vec1 = {this->sdCard.cardData.CID[10],
                   this->sdCard.cardData.CID[12],
                   this->sdCard.cardData.CID[11],
                   this->sdCard.cardData.CID[9],
                   this->sdCard.cardData.CID_nibble[22] - 15,
                   this->sdCard.cardData.CID_nibble[24] - 15,
                   this->sdCard.cardData.CID_nibble[25] - 15,
                   this->sdCard.cardData.CID_nibble[21] - 15,
                   9, 10, 11, 12, 13, 14, 15};
  Vector15 vec2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; //TODO přidat smysluplnější indexy
  Vector15 vec3 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

  std::unordered_map<int, Vector15> vectors;
  vectors.insert(std::pair<int, Vector15>(1, vec1));
  vectors.insert(std::pair<int, Vector15>(2, vec2));
  vectors.insert(std::pair<int, Vector15>(3, vec3));

  struct Key32
  {
    int key[32];
  };

  Key32 key1 = {this->sdCard.cardData.CID[12],
                this->sdCard.cardData.CID[23] - 15,
                this->sdCard.cardData.CID[25] - 15,
                this->sdCard.cardData.CID[11],
                this->sdCard.cardData.CID[9],
                this->sdCard.cardData.CID_nibble[21],
                this->sdCard.cardData.CID[9] % 25,
                this->sdCard.cardData.CID_nibble[22] - 15,
                4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
  Key32 key2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
  Key32 key3 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};

  std::unordered_map<int, Key32> keys;
  keys.insert(std::pair<int, Key32>(1, key1));
  keys.insert(std::pair<int, Key32>(2, key2));
  keys.insert(std::pair<int, Key32>(3, key3));

  int lVersion = lIdentification.licenceVersion;

  iVector[0] = vectors.at(lVersion).vec[0];
  iVector[1] = vectors.at(lVersion).vec[1];
  iVector[2] = vectors.at(lVersion).vec[2];
  iVector[3] = vectors.at(lVersion).vec[3];
  iVector[4] = vectors.at(lVersion).vec[4];
  iVector[5] = vectors.at(lVersion).vec[5];
  iVector[6] = vectors.at(lVersion).vec[6];
  iVector[7] = vectors.at(lVersion).vec[7];

  memcpy(&iVector[8], &iVector[0], 8);

  key[0] = keys.at(lVersion).key[0];
  key[1] = keys.at(lVersion).key[1];
  key[2] = keys.at(lVersion).key[2];
  key[3] = keys.at(lVersion).key[3];
  key[4] = keys.at(lVersion).key[4];
  key[5] = keys.at(lVersion).key[5];
  key[6] = keys.at(lVersion).key[6];
  key[7] = keys.at(lVersion).key[7];
  memcpy(&key[8], &key[0], 8);
  memcpy(&key[16], &key[6], 8);
  memcpy(&key[24], &key[12], 8);
}

/// @brief get proper licencename
/// @param licPostfix
/// @return
string LicenceELC21::getLicenceName()
{
  // note - bude stačit pouze měnit indexy nebo bude potřeba udělat novej rozcestnik? nyni necháno na indexech
  string result = "";
  char prefixChar = 97;
  int licType = (int)lIdentification.licLicenceType;
  int lVersion = lIdentification.licenceVersion;

  unordered_map<int, string> baseString;
  baseString.insert(std::pair<int, string>((int)LicenceType::EOS_EOV, "ezlic_eovosv"));
  baseString.insert(std::pair<int, string>((int)LicenceType::DDTS, "ezlic_ddts"));
  baseString.insert(std::pair<int, string>((int)LicenceType::DRT, "ezlic_drt"));

  struct Index
  {
    int index[11];
  };

  std::unordered_map<int, Index> indexes;
  Index indexes1 = {7, 16, 20, 23, 18, 4, 9, 11, 6, 9, 13};
  Index indexes2 = {12, 10, 22, 23, 24, 25, 9, 11, 2, 1, 3}; // puvodní indexy
  Index indexes3 = {7, 16, 20, 23, 18, 4, 9, 11, 6, 9, 13};

  indexes.insert(std::pair<int, Index>(1, indexes1));
  indexes.insert(std::pair<int, Index>(2, indexes2));
  indexes.insert(std::pair<int, Index>(3, indexes3));

  result = baseString.at(licType) + to_string(lIdentification.licenceIndex) + "_";

  result += prefixChar + ((this->sdCard.cardData.CID[indexes.at(lVersion).index[0]] + (lIdentification.licenceIndex * 11)) % 25);
  result += prefixChar + ((this->sdCard.cardData.CID[indexes.at(lVersion).index[1]] + (lIdentification.licenceIndex * 39)) % 25);

  result += prefixChar + ((this->sdCard.cardData.CID_nibble[indexes.at(lVersion).index[2]] + (lIdentification.licenceIndex * 1)) % 25);
  result += prefixChar + ((this->sdCard.cardData.CID_nibble[indexes.at(lVersion).index[3]] * 2) % 25);
  result += prefixChar + ((this->sdCard.cardData.CID_nibble[indexes.at(lVersion).index[4]] + (lIdentification.licenceIndex * 5)) % 25);
  result += prefixChar + ((this->sdCard.cardData.CID_nibble[indexes.at(lVersion).index[5]] * 3) % 25);

  result += prefixChar + ((this->sdCard.cardData.CID[indexes.at(lVersion).index[6]] + (lIdentification.licenceIndex * 52)) % 25);
  result += prefixChar + ((this->sdCard.cardData.CID[indexes.at(lVersion).index[7]] + (lIdentification.licenceIndex * 34)) % 25);
  result += prefixChar + ((this->sdCard.cardData.CID[indexes.at(lVersion).index[8]] + (lIdentification.licenceIndex * 21)) % 25);
  result += prefixChar + ((this->sdCard.cardData.CID[indexes.at(lVersion).index[9]] + (lIdentification.licenceIndex * 47)) % 25);
  result += prefixChar + ((this->sdCard.cardData.CID[indexes.at(lVersion).index[10]] + (lIdentification.licenceIndex * 7)) % 25);

  result += ".lic";
  return result;
}

int LicenceELC21::getDataPointsCount(int protocolId)
{
  if (this->readLicence(nullptr) == false)
  {
  }

  for (auto item : this->licBody.privateContent.dataItems)
  {
    if (item.protoId == protocolId)
      return item.licCount;
  }
  return 0;
}

bool LicenceELC21::getLicenceItemInfo(int protocolId, void *returnItemStructure)
{
  if (returnItemStructure != nullptr)
  {
    LicenceItem21 *resultPtr = static_cast<LicenceItem21 *>(returnItemStructure);
    resultPtr->protocolId = protocolId; // protocolId;
    if (this->licenceInfo.licences.count(protocolId)) resultPtr->dataPointsCount=this->licenceInfo.licences.at(protocolId);
    else resultPtr->dataPointsCount = 0;
  }
  else
  {
    errorMessage.code = 1;
    errorMessage.message = "Error: Null pointer!";
    return false;
  }

  return true;
}

bool LicenceELC21::getLicenceInfo(int protocolId, void *returnStructure)
{
  if (returnStructure != nullptr)
  {
    LicenceInfo21 *resultPtr = static_cast<LicenceInfo21 *>(returnStructure);
    for (auto item : this->licBody.privateContent.dataItems)
    {
      resultPtr->licences.insert(pair<int, int>(item.protoId, item.licCount));
      if (item.protoId == protocolId) resultPtr->reqDataPointsCount = item.licCount;
    }
  }
  else
  {
    errorMessage.code = 1;
    errorMessage.message = "Error: Null pointer!";
    return false;
  }

  return true;
}

LicenceELC21::~LicenceELC21() {}
