#include "licGenELC1.h"
#define CID_LENGTH 32
#define CSD_LENGTH 32

namespace Generator
{
  Licence1::Licence1()
  {
  }

  Licence1::~Licence1()
  {
  }

  Licence1::Licence1(string cisArg, string csdArg, string dataFileName)
  {
    for (unsigned int i = 0; i < cisArg.length(); i++) this->cid[i] = cisArg[i];
    for (unsigned int i = 0; i < csdArg.length(); i++) this->csd[i] = csdArg[i];

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

    processConfigFile(dataFileName);

    this->binaryGeneration = BinaryGenerationType::File;
  }

  void Licence1::processConfigFile(string &dataFileName)
  {
    string fullFile = getCompletePath(dataFileName);
    const int fileNameLength = fullFile.length();
    char fileName[fileNameLength] = {};
    getCharsFromString(fullFile, fileName, fileNameLength);

    pugi::xml_document doc;
#ifdef EZ_LIC_DEBUG
    pugi::xml_parse_result result = doc.load_file("licData.xml");
#else
    pugi::xml_parse_result result = doc.load_file(fileName);
#endif

    if (result)
    {
      const char *dataRootName = doc.child("data") ? "data" : "licence"; // kompatibilita s verzí, která měla ještě root "data"

      this->stationName = doc.child(dataRootName).child("station").child_value();
      this->distributor = doc.child(dataRootName).child("distributor").child_value();
      this->dataLicenceType = doc.child(dataRootName).child("licenceType").child_value();
      this->dataLicenceVersion = doc.child(dataRootName).child("licenceType").attribute("version").value();
      this->dataGenerationType = this->dataLicenceVersion;
      this->dataCryptoVersion = this->dataLicenceVersion;
    }
    else
    {
      throw LicenceException((int)51, "Chyba při čtení xml");
    }
  }

  bool Licence1::createLicence()
  {
    if (getSDData() == false) throw LicenceException((int)GeneralError::SDCardReadError, "Chyba při čtení SD karty, cesta: " + cid_cdsPath);
    initCrypto();

    // promenne pro praci se soubory a adresari
    LicenceDataMainELC dataMainToFileELC1{};
    LicenceData dataToFile{};

    // pomocne promenne
    string licencePostfix;     //: STRING; pomocna promenna pro kontrolu postfixu
    string mainLicDescription; //: STRING(79); globalni pojmenovani licence v nesifrovane casti licence

    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)PlcLicenceType::LicenceEov))
    {
      licSourceData.licType = 0x1AA2;
      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;

    licencePostfix = to_string(licSourceData.licPostfix);

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

    dataToFile.id.version = 10;
    dataToFile.id.cardSize = this->sdData.cardSize;

    switch (licIdent.licIDType)
    {
    case 1: // cEzLic_p78ou3_IDType_DDTS:
      mainLicDescription = "ddts ";
      break;
    case 2: // 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; // 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
    {
      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 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)
    {
#ifdef EZ_LIC_DEBUG

      char licFileNameToSave[licenseFileName.length()] = {};
      getCharsFromString(licenseFileName, licFileNameToSave, licenseFileName.length());

      FILE *fileLicence = fopen(licFileNameToSave, "wb");
      if (fileLicence)
      {
        fwrite(&totalEncryptedArray, sizeof(totalEncryptedArray), 1, fileLicence);
        // printf("License binary saved.\n");
        fclose(fileLicence);
        cout << licenseFileName << endl;
        return true;
      }

#else
      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 licence file." << std::endl;
      }
      return true;
#endif
    }
    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;
  }

}