/*******************************************************************************
 * Copyright (c) 2012 - 2014 AIT, ACIN, fortiss GmbH
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Filip Andren, Alois Zoitl - initial API and implementation and/or initial documentation
 *******************************************************************************/
#include "EplXmlReader.h"

#include <string_utils.h>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;

#include <tinyxml.h>

CEplXmlReader::CEplXmlReader(CProcessImageMatrix* pa_pIn, CProcessImageMatrix* pa_pOut){
  m_pProcImageIn = pa_pIn;
  m_pProcImageOut = pa_pOut;
}

CEplXmlReader::~CEplXmlReader(){

}

void CEplXmlReader::setProcessImageIn(CProcessImageMatrix* pa_pIn){
  m_pProcImageIn = pa_pIn;
}

void CEplXmlReader::setProcessImageOut(CProcessImageMatrix* pa_pOut){
  m_pProcImageOut = pa_pOut;
}

void CEplXmlReader::readXmlFile(const char* pa_pchFileName){

  TiXmlDocument xmlDoc(pa_pchFileName);

  if(xmlDoc.LoadFile()){
    TiXmlNode *appProcess = xmlDoc.FirstChild("ApplicationProcess");
    TiXmlNode *processImageIn;
    for(TiXmlNode *processImage = appProcess->ToElement()->FirstChild("ProcessImage"); processImage != NULL; processImage = processImage->NextSibling("ProcessImage")){
      if(strcmp(processImage->ToElement()->Attribute("type"), "output") == 0){
        createProcImageOut(processImage);
      }
      else if(strcmp(processImage->ToElement()->Attribute("type"), "input") == 0){
        processImageIn = processImage;
      }
    }
    createProcImageIn(processImageIn);
  }
  else{
    cout << "ERROR: Could not open XML file" << endl;
  }
}

void CEplXmlReader::createProcImageOut(TiXmlNode* pa_pProcessImage){
  int currentCnId = -1;
  int currentModuleNr = -1;
  char currentModuleId[256];
  int currentIoNr = -1;

  TiXmlNode *channel;
  for(channel = pa_pProcessImage->FirstChild("Channel"); channel != NULL; channel = channel->NextSibling("Channel")){
    // New IO
    currentIoNr++;

    // Get "Name" attribute
    const char *ioName = channel->ToElement()->Attribute("Name");
    char *nameStr = new char[strlen(ioName) + 1];
    strcpy(nameStr, ioName);

    // Get CN ID
    char* pch = strtok(nameStr, ".CN");
    int cnId = forte::core::util::strtol(pch,0,10);

    // Get module ID
    char* modId = strtok(NULL, ".");

    // Get dataSize
    const char *temp = channel->ToElement()->Attribute("dataSize");
    int dSize = forte::core::util::strtoul(temp,0,10);

    // Get PIOffset
    temp = channel->ToElement()->Attribute("PIOffset");
    long piOffset = forte::core::util::strtol(temp, NULL, 16);

    // Get BitOffset
    long bitOffset = 0;
    temp = channel->ToElement()->Attribute("BitOffset");
    if(temp != NULL){
      bitOffset = forte::core::util::strtol(temp, NULL, 16);
    }

    // Check CN id, module number and add new IO
    if(cnId != currentCnId){ // New CN
      currentCnId = cnId;
      currentModuleNr = 0;
      strcpy(currentModuleId, modId);
      currentIoNr = 0;

      m_oModuleListOut.addEntry(ioName, piOffset, bitOffset, currentModuleNr);
      m_pProcImageOut->addEntry(cnId, currentModuleNr, currentIoNr, dSize, piOffset, bitOffset);

    }
    else if(m_oModuleListOut.moduleNameExist(ioName)){ // IO name exist => this must be a new module with same brand as another
      currentModuleNr++;
      strcpy(currentModuleId, modId);
      currentIoNr = 0;

      m_oModuleListOut.addEntry(ioName, piOffset, bitOffset, currentModuleNr);
      m_pProcImageOut->addEntry(cnId, currentModuleNr, currentIoNr, dSize, piOffset, bitOffset);

    }
    else if(strcmp(modId, currentModuleId) == 0){ // same module ID as IO before => must be same module

      m_pProcImageOut->addEntry(cnId, currentModuleNr, currentIoNr, dSize, piOffset, bitOffset);

    }
    else{ // new module ID => new module
      currentModuleNr++;
      strcpy(currentModuleId, modId);
      currentIoNr = 0;

      m_oModuleListOut.addEntry(ioName, piOffset, bitOffset, currentModuleNr);
      m_pProcImageOut->addEntry(cnId, currentModuleNr, currentIoNr, dSize, piOffset, bitOffset);

    }

    cout << "<< " << currentCnId << ", " << currentModuleNr << ", " << ioName << ", " << currentIoNr << ", " << dSize << ", " << piOffset << ", " << bitOffset << endl;

    delete[] nameStr;
  }
}

void CEplXmlReader::createProcImageIn(TiXmlNode *pa_pProcessImage){
  int currentCnId = -1;
  int currentModuleNr = -1;
  char currentModuleId[256];
  int currentIoNr = -1;

  TiXmlNode *channel;
  for(channel = pa_pProcessImage->FirstChild("Channel"); channel != NULL; channel = channel->NextSibling("Channel")){
    // New IO
    currentIoNr++;

    // Get "Name" attribute
    const char *ioName = channel->ToElement()->Attribute("Name");
    char *nameStr = new char[strlen(ioName) + 1];
    strcpy(nameStr, ioName);

    // Get CN ID
    char* pch = strtok(nameStr, ".CN");
    int cnId = forte::core::util::strtoul(pch,0,10);

    // Get module ID
    char* modId = strtok(NULL, ".");

    // Get dataSize
    const char *temp = channel->ToElement()->Attribute("dataSize");
    int dSize = forte::core::util::strtoul(temp,0,10);

    // Get PIOffset
    temp = channel->ToElement()->Attribute("PIOffset");
    long piOffset = forte::core::util::strtol(temp, NULL, 16);

    // Get BitOffset
    long bitOffset = 0;
    temp = channel->ToElement()->Attribute("BitOffset");
    if(temp != NULL){
      bitOffset = forte::core::util::strtol(temp, NULL, 16);
    }

    // Check CN id, module number and add new IO
    if(cnId != currentCnId){ // New CN
      currentCnId = cnId;

      currentModuleNr = getModuleNr(ioName);

      strcpy(currentModuleId, modId);
      currentIoNr = 0;

      m_oModuleListIn.addEntry(ioName, piOffset, bitOffset, currentModuleNr);
      m_pProcImageIn->addEntry(cnId, currentModuleNr, currentIoNr, dSize, piOffset, bitOffset);

    }
    else if(m_oModuleListIn.moduleNameExist(ioName)){ // IO name exist => this must be a new module with same brand as another

      currentModuleNr = getModuleNr(ioName);

      strcpy(currentModuleId, modId);
      currentIoNr = 0;

      m_oModuleListIn.addEntry(ioName, piOffset, bitOffset, currentModuleNr);
      m_pProcImageIn->addEntry(cnId, currentModuleNr, currentIoNr, dSize, piOffset, bitOffset);

    }
    else if(strcmp(modId, currentModuleId) == 0){ // same module ID as IO before => must be same module

      m_pProcImageIn->addEntry(cnId, currentModuleNr, currentIoNr, dSize, piOffset, bitOffset);

    }
    else{ // new module ID => new module

      currentModuleNr = getModuleNr(ioName);

      strcpy(currentModuleId, modId);
      currentIoNr = 0;

      m_oModuleListIn.addEntry(ioName, piOffset, bitOffset, currentModuleNr);
      m_pProcImageIn->addEntry(cnId, currentModuleNr, currentIoNr, dSize, piOffset, bitOffset);

    }

    cout << ">> " << currentCnId << ", " << currentModuleNr << ", " << ioName << ", " << currentIoNr << ", " << dSize << ", " << piOffset << ", " << bitOffset << endl;

    delete[] nameStr;
  }
}

int CEplXmlReader::getModuleNr(const char* pa_pchIoId){
  char localCopy[256];
  strcpy(localCopy, pa_pchIoId);

  char dest[256];
  char* pch = strtok(localCopy, ".");
  strcpy(dest, pch);
  strcat(dest, ".");
  pch = strtok(NULL, ".");
  strcat(dest, pch);

  int occurences = m_oModuleListIn.getNrOfModules(dest);
  int modNr = m_oModuleListOut.getModuleNr(dest, occurences + 1);
  if(modNr == -1) {
    cout << "ShouldNotHappenError" << endl;
  }

  return modNr;
}
