#include <stdio.h>
#include <filesystem>
#include <cstdlib>
#include <licenceGenerator.h>
#include "utils.h"
#include "pugixml.hpp"
#include <typeinfo>

ELCType licElcType = ELCType::ELC2;
LicenceType licLicenceType;
PlcType licPlcType;

uint8_t licenceVersion = 1; // verze licence, určitě kodování, pojmenování souborů
uint8_t licenceIndex = 0;
uint8_t revision;
uint8_t licCompatibility = 1; // identikator hlavního ELC



LicenceGenerator::LicenceGenerator()
{
}

LicenceGenerator::LicenceGenerator(string uid, string cid, string csd, string dataFileName)
{
  try
  {
    if (dataFileName.empty())
      throw std::runtime_error("Missing dataFileName par.");

    if (uid.empty() == false)
    {
      this->licData.uid = uid;
    }
    else
    {
      if (cid.empty())
        throw std::runtime_error("Missing CID par.");
      if (csd.empty())
        throw std::runtime_error("Missing CSD par.");
    }
  }
  catch (const std::exception &e)
  {
    this->argumentsCorrect = false;
    std::cerr << "error: " << e.what() << endl;
    return;
  }

  this->configFileName = dataFileName;
  this->licData.cid = cid;
  this->licData.csd = csd;

  this->argumentsCorrect = true;

  if (processInputConfiguration() == false)
  {
    cerr << "Chyba: " << error.message << endl;
  }
}

LicenceGenerator::LicenceGenerator(map<string, string> &arguments)
{
}

LicenceGenerator::LicenceGenerator(int elcType, int licenceType, int licenceVersion, int licenceIndex, int compatibility)
{
  this->licIdentification.licElcType = (ELCType)elcType;
  this->licIdentification.licLicenceType = (LicenceType)licenceType;
  this->licIdentification.licenceVersion = licenceVersion;
  this->licIdentification.licenceIndex = licenceIndex;
  this->licIdentification.licCompatibility = compatibility;
}

/// @brief inicializace generátoru a načtení licenčních bodů
/// @param elcType
/// @param licenceType
/// @param licenceVersion
/// @param licenceIndex
/// @param compatibility
/// @return
bool LicenceGenerator::initread(InitStructure &initStructure)
{
  try
  {
    elcSwitchType = initStructure.elcType * 10 + licCompatibility;

    if (init(initStructure))
    {
      switch (elcSwitchType)
      {
      case 11:
      { // old eoseov
        LicenceELC11 licence = LicenceELC11(this->licIdentification);
        licence.readLicence(&this->licenceInfo);
        break;
      }
      case 21:
      {
        LicenceELC21 licence = LicenceELC21(this->licIdentification);
        this->licence21 = &licence;
        this->licence21->readLicence(&this->licenceInfo);
        break;
      }
      case 31:
      {
        LicenceELC31 licence = LicenceELC31(this->licIdentification);
        break;
      }
      }
    }
    else
      return false;
  }
  catch (const LicenceException &ex)
  {
    error.code = ex.getErrorCode();
    error.message = ex.getErrorMessage();
    return false;
  }

  return true; // TODO testy
}

bool LicenceGenerator::initread(int elcType, int licenceType, int licenceVersion, int licenceIndex, int compatibility)
{
  try
  {
    elcSwitchType = elcType * 10 + licCompatibility;

    if (init(elcType, licenceType, licenceVersion, licenceIndex, compatibility))
    {
      switch (elcSwitchType)
      {
      case 11:
      { // old eoseov
        LicenceELC11 licence = LicenceELC11(this->licIdentification);
        licence.readLicence(&this->licenceInfo);
        break;
      }
      case 21:
      {
        LicenceELC21 licence = LicenceELC21(this->licIdentification);
        this->licence21 = &licence;
        this->licence21->readLicence(&this->licenceInfo);
        break;
      }
      case 31:
      {
        LicenceELC31 licence = LicenceELC31(this->licIdentification);
        break;
      }
      }
    }
    else
      return false;
  }
  catch (const LicenceException &ex)
  {
    error.code = ex.getErrorCode();
    error.message = ex.getErrorMessage();
    return false;
  }

  return true; // TODO testy
}

/// @brief inicicialize generátoro, pouze vstupní kontrola
/// @param elcType
/// @param licenceType
/// @param licenceVersion
/// @param licenceIndex
/// @param compatibility
/// @return
bool LicenceGenerator::init(int elcType, int licenceType, int licenceVersion, int licenceIndex, int compatibility)
{
  try
  {
    if (elcType > 3)
    {
      error.code = (int)GeneralError::ELCNotImplemented;
      error.message = "ELC není implementováno.";
      throw LicenceException((int)GeneralError::ELCNotImplemented, error.message);
      return false;
    }

    this->licIdentification.licElcType = (ELCType)elcType;
    this->licIdentification.licLicenceType = (LicenceType)licenceType;
    this->licIdentification.licenceVersion = licenceVersion;
    this->licIdentification.licenceIndex = licenceIndex;
    this->licIdentification.licCompatibility = compatibility;
  }
  catch (const LicenceException &ex)
  {
    error.code = ex.getErrorCode();
    error.message = ex.getErrorMessage();
    return false;
  }

  elcSwitchType = elcType * 10 + licCompatibility;

  return true; // TODO testy
}

bool LicenceGenerator::init(InitStructure &initStructure)
{
  try
  {
    if (initStructure.elcType > 3)
    {
      error.code = (int)GeneralError::ELCNotImplemented;
      error.message = "ELC není implementováno.";
      throw LicenceException((int)GeneralError::ELCNotImplemented, error.message);
      return false;
    }

    this->licIdentification.licElcType = (ELCType)initStructure.elcType;
    this->licIdentification.licLicenceType = (LicenceType)initStructure.licenceType;
    this->licIdentification.licenceVersion = licenceVersion;
    this->licIdentification.licenceIndex = licenceIndex;
    this->licIdentification.licCompatibility = initStructure.compatibility;
  }
  catch (const LicenceException &ex)
  {
    error.code = ex.getErrorCode();
    error.message = ex.getErrorMessage();
    return false;
  }

  elcSwitchType = initStructure.elcType * 10 + licCompatibility;

  return true; // TODO testy
}

int LicenceGenerator::getDataPointsCount(int protocolId)
{
}

bool LicenceGenerator::getLicenceItemInfo(int protocolId, void *returnItemStructure)
{
  int result = 0;
  // const type_info &ti1 = typeid(LicenceELC21);

  try
  {
    switch (elcSwitchType)
    {
    case 11:
    { // old eoseov
      LicenceItem11 *resultPtr = static_cast<LicenceItem11 *>(returnItemStructure);
      resultPtr->protocolId = protocolId; // protocolId;
      if (this->licenceInfo.licences.count(protocolId))
        resultPtr->dataPointsCount = this->licenceInfo.licences.at(protocolId);
      else
        resultPtr->dataPointsCount = 0;
      // licence21->getLicenceItemInfo(protocolId, returnItemStructure); stará verze, nová má k dispozici polozky z readLicence
      break;
    }
    case 21:
    {

      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;
      // licence21->getLicenceItemInfo(protocolId, returnItemStructure); stará verze, nová má k dispozici polozky z readLicence
      break;
    }
    case 31:
    {
      LicenceELC31 licence = LicenceELC31(this->licIdentification);
      break;
    }
    default:
    {
      error.code = -1;
      error.message = "Nepodařilo se identifikovat licenci";
      return false;
    }
    }
  }
  catch (const LicenceException &ex)
  {
    error.code = ex.getErrorCode();
    error.message = ex.getErrorMessage();
    return false;
  }

  return true;
}

bool LicenceGenerator::getLicenceInfo(int protocolId, void *returnStructure)
{
  int result = 0;
  const type_info &ti1 = typeid(LicenceELC21);
  // const type_info& ti1 = typeid(returnStructure);
  // cout << "TTTYP: " << ti1.name() << endl;
  switch (elcSwitchType)
  {
  case 11:
  { // old eoseov
    break;
  }
  case 21:
  {
    LicenceELC21 licence = LicenceELC21(this->licIdentification);
    try
    {
      licence.readLicence(&this->licenceInfo);
      return licence.getLicenceInfo(protocolId, returnStructure);
    }
    catch (const LicenceException &ex)
    {
      error.code = ex.getErrorCode();
      error.message = ex.getErrorMessage();
      return false;
    }
    break;
  }
  case 31:
  {
    LicenceELC31 licence = LicenceELC31(this->licIdentification);
    try
    {
      licence.readLicence();
      return licence.getLicenceInfo(protocolId, returnStructure);
    }
    catch (const LicenceException &ex)
    {
      error.code = ex.getErrorCode();
      error.message = ex.getErrorMessage();
      return false;
    }
    break;
  }
  }

  error.code = -1;
  error.message = "Nepodařilo se identifikovat licenci";
  return false;
}

bool LicenceGenerator::processInputConfiguration()
{
  string fullFile = getCompletePath(this->configFileName);
  const int fileNameLength = fullFile.length();
  char fileName[fileNameLength] = {};
  getCharsFromString(fullFile, fileName, fileNameLength);

#ifdef WINDOWS
  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"
    Mapping mapping;

    if (atoi(&doc.child(dataRootName).attribute("xmlVersion").value()[0]) != XML_VERSION)
    {
      error.code = -1;
      error.message = "Invalid XML VERSION";
      return false;
    }

    this->licData.doc = &doc;

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

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

    licElcType = (ELCType)atoi(&doc.child(dataRootName).attribute("elc").value()[0]);
    licCompatibility = atoi(&doc.child(dataRootName).attribute("compatibility").value()[0]);

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

    string plcType = doc.child(dataRootName).child("plcType").child_value();
    if (!plcType.empty())
    {
      this->licIdentification.licPlcType = mapping.licMapPlcType[plcType];
    }
  }
  else
  {
    std::cerr << "Unable to open the config file." << std::endl;
  }

  return true;
}

void LicenceGenerator::createLicenceFile()
{
  switch (licElcType)
  {
  case ELCType::ELC1:
    createLicenceELC1();
    break;

  case ELCType::ELC2:
    createLicenceELC2();
    break;

  case ELCType::ELC3:
    createLicenceELC3();
    break;
  }
}

void LicenceGenerator::readLicenceFile()
{
  switch (this->licIdentification.licElcType)
  {
  case ELCType::ELC1:
    readLicenceELC1();
    break;

  case ELCType::ELC2:
    readLicenceELC2();
    break;

  case ELCType::ELC3:
    readLicenceELC3();
    break;
  }
}

void LicenceGenerator::createLicenceELC1() // přejmenvat na ELC1
{
  LicenceELC11 plcWriteLicence = LicenceELC11(this->licData.cid, this->licData.csd, "file", this->configFileName);
  plcWriteLicence.createLicence();
}

void LicenceGenerator::createLicenceELC2()
{
  switch (licCompatibility)
  {
  case 1:
    LicenceELC21 licence = LicenceELC21(this->licData);
    licence.createLicence();
    break;
  }
}

void LicenceGenerator::createLicenceELC3()
{
  switch (licCompatibility)
  {
  case 1:
    LicenceELC31 licence = LicenceELC31(this->licData);
    licence.createLicence();
    break;
  }
}

void LicenceGenerator::readLicenceELC1()
{
  cout << "reading ELC1" << endl;
}

void LicenceGenerator::readLicenceELC2()
{
  LicenceELC21 licence = LicenceELC21(this->licIdentification);
}

void LicenceGenerator::readLicenceELC3()
{
  cout << "reading ELC3" << endl;
}

LicenceGenerator::~LicenceGenerator()
{
}
