/*******************************************************************************
 * 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, Patrick Smejkal, Alois Zoitl, Martin Melik-Merkumians - initial API and implementation and/or initial documentation
 *******************************************************************************/
#include "modbuslayer.h"
#include "commfb.h"
#include "modbusclientconnection.h"

using namespace forte::com_infra;

CModbusComLayer::CModbusComLayer(CComLayer* pa_poUpperLayer, CBaseCommFB* pa_poComFB) :
    CComLayer(pa_poUpperLayer, pa_poComFB), m_pModbusConnection(0), m_unBufFillSize(0){
  m_eConnectionState = e_Disconnected;
}

CModbusComLayer::~CModbusComLayer(){
}

EComResponse CModbusComLayer::sendData(void *pa_pvData, unsigned int pa_unSize){
  EComResponse eRetVal = e_ProcessDataOk;

  if(m_eConnectionState == e_Connected){
    switch (m_poFb->getComServiceType()){
      case e_Server:
        // todo
        break;
      case e_Client: {
        TForteUInt16 *convertedData = new TForteUInt16[pa_unSize * 4];
        unsigned int sendLength = convertDataInput(pa_pvData, pa_unSize, convertedData);
        if(sendLength > 0){
          m_pModbusConnection->writeData(convertedData, sendLength);
        }
        delete[] convertedData;
        break;
      }
      case e_Publisher:
        // todo
        break;
      case e_Subscriber:
        //do nothing as subscribers do not send data
        break;
    }
  }
  return eRetVal;
}

unsigned int CModbusComLayer::convertDataInput(void *pa_poInData, unsigned int pa_nDataSize, TForteUInt16 *pa_poConvertedData){
  unsigned int outLength = 0;

  CIEC_ANY *apoSDs = static_cast<CIEC_ANY*>(pa_poInData);
  unsigned int nrSDs = pa_nDataSize;

  for(unsigned int i = 0; i < nrSDs; i++){
    CIEC_ANY *anyVal = &apoSDs[i];
    switch (apoSDs[i].getDataTypeID()){
      case CIEC_ANY::e_BOOL: // 8bit data types
      {
        TForteUInt16 out = (bool) *(CIEC_BOOL*) anyVal;
        *(TForteUInt16*) (&pa_poConvertedData[outLength]) = out;
        outLength += sizeof(TForteUInt16) / 2;
        break;
      }
      case CIEC_ANY::e_SINT: {
        TForteInt16 out = (TForteInt8) *(CIEC_SINT*) anyVal;
        *(TForteInt16*) (&pa_poConvertedData[outLength]) = out;
        outLength += sizeof(TForteInt16) / 2;
        break;
      }
      case CIEC_ANY::e_USINT: {
        TForteUInt16 out = (TForteUInt8) *(CIEC_USINT*) anyVal;
        *(TForteUInt16*) (&pa_poConvertedData[outLength]) = out;
        outLength += sizeof(TForteUInt16) / 2;
        break;
      }
      case CIEC_ANY::e_BYTE: {
        TForteUInt16 out = (TForteByte) *(CIEC_BYTE*) anyVal;
        *(TForteUInt16*) (&pa_poConvertedData[outLength]) = out;
        outLength += sizeof(TForteUInt16) / 2;
        break;
      }
      case CIEC_ANY::e_INT: // 16bit data types
      {
        TForteInt16 out = (TForteInt16) *(CIEC_INT*) anyVal;
        *(TForteInt16*) (&pa_poConvertedData[outLength]) = out;
        outLength += sizeof(TForteInt16) / 2;
        break;
      }
      case CIEC_ANY::e_UINT: {
        TForteUInt16 out = (TForteUInt16) *(CIEC_UINT*) anyVal;
        *(TForteUInt16*) (&pa_poConvertedData[outLength]) = out;
        outLength += sizeof(TForteUInt16) / 2;
        break;
      }
      case CIEC_ANY::e_WORD: {
        TForteWord out = (TForteWord) *(CIEC_WORD*) anyVal;
        *(TForteWord*) (&pa_poConvertedData[outLength]) = out;
        outLength += sizeof(TForteWord) / 2;
        break;
      }
      case CIEC_ANY::e_DINT: // 32bit data types
      {
        TForteInt32 out = (TForteInt32) *static_cast<CIEC_DINT*>(anyVal);
        *(TForteInt32*) (&pa_poConvertedData[outLength]) = convertFBOutput<TForteInt32>((TForteByte*) &out, sizeof(TForteInt32));
        outLength += sizeof(TForteInt32) / 2;
        break;
      }
      case CIEC_ANY::e_UDINT: {
        TForteUInt32 out = (TForteUInt32) *(CIEC_UDINT*) anyVal;
        *(TForteUInt32*) (&pa_poConvertedData[outLength]) = convertFBOutput<TForteUInt32>((TForteByte*) &out, sizeof(TForteUInt32));
        outLength += sizeof(TForteUInt32) / 2;
        break;
      }
      case CIEC_ANY::e_DWORD: {
        TForteDWord out = (TForteDWord) *(CIEC_DWORD*) anyVal;
        *(TForteDWord*) (&pa_poConvertedData[outLength]) = convertFBOutput<TForteDWord>((TForteByte*) &out, sizeof(TForteDWord));
        outLength += sizeof(TForteDWord) / 2;
        break;
      }
      case CIEC_ANY::e_REAL: {
        TForteFloat out = (TForteFloat) *(CIEC_REAL*) anyVal;
        *(TForteFloat*) (&pa_poConvertedData[outLength]) = convertFBOutput<TForteFloat>((TForteByte*) &out, sizeof(TForteFloat));
        outLength += sizeof(TForteFloat) / 2;
        break;
      }
      case CIEC_ANY::e_LINT: // 64bit data types
      {
        TForteInt64 out = (TForteInt64) *(CIEC_LINT*) anyVal;
        *(TForteInt64*) (&pa_poConvertedData[outLength]) = convertFBOutput<TForteInt64>((TForteByte*) &out, sizeof(TForteInt64));
        outLength += sizeof(TForteInt64) / 2;
        break;
      }
      case CIEC_ANY::e_ULINT: {
        TForteUInt64 out = (TForteUInt64) *(CIEC_ULINT*) anyVal;
        *(TForteUInt64*) (&pa_poConvertedData[outLength]) = convertFBOutput<TForteUInt64>((TForteByte*) &out, sizeof(TForteUInt64));
        outLength += sizeof(TForteUInt64) / 2;
        break;
      }
      case CIEC_ANY::e_LWORD: {
        TForteLWord out = (TForteLWord) *(CIEC_LWORD*) anyVal;
        *(TForteLWord*) (&pa_poConvertedData[outLength]) = convertFBOutput<TForteLWord>((TForteByte*) &out, sizeof(TForteLWord));
        outLength += sizeof(TForteLWord) / 2;
        break;
      }
      case CIEC_ANY::e_LREAL: {
        TForteDFloat out = (TForteDFloat) *(CIEC_LREAL*) anyVal;
        *(TForteDFloat*) (&pa_poConvertedData[outLength]) = convertFBOutput<TForteDFloat>((TForteByte*) &out, sizeof(TForteDFloat));
        outLength += sizeof(TForteDFloat) / 2;
        break;
      }
      default:
        break;
    }
  }
  return outLength;
}

EComResponse CModbusComLayer::processInterrupt(){
  if(e_ProcessDataOk == m_eInterruptResp){
    switch (m_eConnectionState){
      case e_Connected: {
        CIEC_ANY *apoRDs = m_poFb->getRDs();
        unsigned int nrRDs = m_poFb->getNumRD();

        unsigned int dataIndex = 0;

        for(unsigned int i = 0; i < nrRDs; i++){
          switch (apoRDs[i].getDataTypeID()){
            case CIEC_ANY::e_BOOL:
              apoRDs[i].setValue(CIEC_BOOL(convertFBOutput<bool>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(bool);
              break;
            case CIEC_ANY::e_SINT:
              apoRDs[i].setValue(CIEC_SINT(convertFBOutput<TForteInt8>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteInt8);
              break;
            case CIEC_ANY::e_INT:
              apoRDs[i].setValue(CIEC_INT(convertFBOutput<TForteInt16>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteInt16);
              break;
            case CIEC_ANY::e_DINT:
              apoRDs[i].setValue(CIEC_DINT(convertFBOutput<TForteInt32>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteInt32);
              break;
            case CIEC_ANY::e_LINT:
              apoRDs[i].setValue(CIEC_LINT(convertFBOutput<TForteInt64>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteInt64);
              break;
            case CIEC_ANY::e_USINT:
              apoRDs[i].setValue(CIEC_USINT(convertFBOutput<TForteUInt8>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteUInt8);
              break;
            case CIEC_ANY::e_UINT:
              apoRDs[i].setValue(CIEC_UINT(convertFBOutput<TForteUInt16>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteUInt16);
              break;
            case CIEC_ANY::e_UDINT:
              apoRDs[i].setValue(CIEC_UDINT(convertFBOutput<TForteUInt32>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteUInt32);
              break;
            case CIEC_ANY::e_ULINT:
              apoRDs[i].setValue(CIEC_ULINT(convertFBOutput<TForteUInt64>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteUInt64);
              break;
            case CIEC_ANY::e_BYTE:
              apoRDs[i].setValue(CIEC_BYTE(convertFBOutput<TForteByte>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteByte);
              break;
            case CIEC_ANY::e_WORD:
              apoRDs[i].setValue(CIEC_WORD(convertFBOutput<TForteWord>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteWord);
              break;
            case CIEC_ANY::e_DWORD:
              apoRDs[i].setValue(CIEC_DWORD(convertFBOutput<TForteDWord>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteDWord);
              break;
            case CIEC_ANY::e_LWORD:
              apoRDs[i].setValue(CIEC_LWORD(convertFBOutput<TForteLWord>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteLWord);
              break;
            case CIEC_ANY::e_REAL:
              apoRDs[i].setValue(CIEC_REAL(convertFBOutput<TForteFloat>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteFloat);
              break;
            case CIEC_ANY::e_LREAL:
              apoRDs[i].setValue(CIEC_LREAL(convertFBOutput<TForteDFloat>(&m_acRecvBuffer[dataIndex], m_unBufFillSize - dataIndex)));
              dataIndex += sizeof(TForteDFloat);
              break;
            default:
              // TODO
              break;
          }
        }
        break;
      }
      case e_Disconnected:
      case e_Listening:
      case e_ConnectedAndListening:
      default:
        break;
    }
  }
  else{
    if(e_InitTerminated == m_eInterruptResp){
      // todo: Move server into listening mode again, etc.
    }
  }
  return m_eInterruptResp;
}

EComResponse CModbusComLayer::recvData(const void *, unsigned int){
  m_eInterruptResp = e_Nothing;

  switch (m_eConnectionState){
    case e_Listening:

      //TODO accept incoming connection

      break;
    case e_Connected: {
      int nRetVal = 0;
      switch (m_poFb->getComServiceType()){
        case e_Server:
          //TODO
          break;
        case e_Client:
          //TODO check if errors occured during polling in ModbusConnection
          nRetVal = m_pModbusConnection->readData(&m_acRecvBuffer[0]);
          break;
        case e_Publisher:
          //do nothing as publisher cannot receive data
          break;
        case e_Subscriber:
          //do nothing since Modbus protocol cannot act as subscriber
          break;
      }
      switch (nRetVal){
        case 0:
          //TODO
          break;
        default:
          //we successfully received data
          m_unBufFillSize = nRetVal;
          m_eInterruptResp = e_ProcessDataOk;
          break;
      }
      m_poFb->interruptCommFB(this);
    }
      break;
    case e_ConnectedAndListening:
    case e_Disconnected:
    default:
      break;
  }
  return m_eInterruptResp;
}

template<typename T>
T CModbusComLayer::convertFBOutput(TForteByte *pa_acDataArray, unsigned int pa_nDataSize){
  T retVal;
  unsigned int currentDataSize = sizeof(T);

  if(currentDataSize <= pa_nDataSize){
    if(currentDataSize > 2){
      // A data type with size greater than 16 bits is requested =>
      // we need to swap order of each 16 bit data package

      unsigned int nrUint16s = currentDataSize / 2;
      TForteUInt16 *destAr = new TForteUInt16[nrUint16s];
      TForteUInt16 *sourceAr = (TForteUInt16*) pa_acDataArray;

      for(unsigned int i = 0; i < nrUint16s; i++) {
        destAr[i] = sourceAr[nrUint16s - 1 - i];
      }

      retVal = *((T*) destAr);

      delete[] destAr;
    }
    else{
      TForteByte *tempAr = new TForteByte[currentDataSize];
      for(unsigned int j = 0; j < currentDataSize; j++) {
        tempAr[j] = pa_acDataArray[j];
      }

      retVal = *((T*) tempAr);
      delete[] tempAr;
    }
  }
  else {
    retVal = 0;
  }

  return retVal;
}

EComResponse CModbusComLayer::openConnection(char *pa_acLayerParameter){
  EComResponse eRetVal = e_InitInvalidId;
  switch (m_poFb->getComServiceType()){
    case e_Server:
      //TODO open server connection
      m_eConnectionState = e_Listening;
      break;
    case e_Client: {
      STcpParams tcpParams;
      SRtuParams rtuParams;
      SCommonParams commonParams;
      memset(&tcpParams, 0, sizeof(tcpParams));
      memset(&rtuParams, 0, sizeof(rtuParams));
      memset(&commonParams, 0, sizeof(commonParams));

      int errCode = processClientParams(pa_acLayerParameter, &tcpParams, &rtuParams, &commonParams);
      if(errCode != 0){
        DEVLOG_ERROR("CModbusComLayer:: Invalid input parameters\n");
      }
      else{
        m_pModbusConnection = new CModbusClientConnection(getExtEvHandler<CModbusHandler>());
        if(strlen(tcpParams.m_acIp) > 0){
          m_pModbusConnection->setIPAddress(tcpParams.m_acIp);
          m_pModbusConnection->setPort(tcpParams.m_nPort);
        }
        else if(strlen(rtuParams.m_acDevice) > 0){
          m_pModbusConnection->setDevice(rtuParams.m_acDevice);
          m_pModbusConnection->setBaud(rtuParams.m_nBaud);
          m_pModbusConnection->setParity(rtuParams.m_cParity);
          m_pModbusConnection->setDataBit(rtuParams.m_nDataBit);
          m_pModbusConnection->setStopBit(rtuParams.m_nStopBit);
        }
        m_pModbusConnection->setComCallback(this);
        m_pModbusConnection->setResponseTimeout(commonParams.m_nResponseTimeout);
        m_pModbusConnection->setByteTimeout(commonParams.m_nByteTimeout);

        static_cast<CModbusClientConnection*>(m_pModbusConnection)->setSlaveId(commonParams.m_nSlaveId);

        for(unsigned int i = 0; i < commonParams.m_nNrPolls; i++){
          static_cast<CModbusClientConnection*>(m_pModbusConnection)->addNewPoll(commonParams.m_nPollFrequency, commonParams.m_nFuncCode, commonParams.m_nReadStartAddress[i], commonParams.m_nReadNrAddresses[i]);
        }
        for(unsigned int i = 0; i < commonParams.m_nNrSends; i++){
          static_cast<CModbusClientConnection*>(m_pModbusConnection)->addNewSend(commonParams.m_nSendStartAddress[i], commonParams.m_nSendNrAddresses[i]);
        }

        if(m_pModbusConnection->connect() < 0){
          return eRetVal;
        }
        m_eConnectionState = e_Connected;
        eRetVal = e_InitOk;
      }
    }
      break;
    case e_Publisher:
      //do nothing as modbus cannot be publisher
      break;
    case e_Subscriber:
      //do nothing as modbus cannot be subscriber
      break;
  }

  return eRetVal;
}

void CModbusComLayer::closeConnection(){
  //TODO
  DEVLOG_INFO("CModbusLayer::closeConnection()\n");

  if(m_pModbusConnection != NULL){
    m_pModbusConnection->disconnect();
    delete m_pModbusConnection;
  }
}

int CModbusComLayer::processClientParams(char* pa_acLayerParams, STcpParams* pa_pTcpParams, SRtuParams* pa_pRtuParams, SCommonParams* pa_pCommonParams){
  char *params = new char[strlen(pa_acLayerParams) + 1];
  char *paramsAddress =  params;
  strcpy(params, pa_acLayerParams);
  char *chrStorage;

  pa_pTcpParams->m_acIp[0] = '\0';
  pa_pRtuParams->m_acDevice[0] = '\0';

  chrStorage = strchr(params, ':');
  if(chrStorage == 0){
    delete[] paramsAddress;
    return -1;
  }
  *chrStorage = '\0';
  ++chrStorage;

  if(strcmp(params, "rtu") == 0 || strcmp(params, "RTU") == 0){
    // get rtu params
    params = chrStorage;
    chrStorage = strchr(chrStorage, ':');
    if(chrStorage == 0){
      delete[] paramsAddress;
      return -1;
    }
    *chrStorage = '\0';
    ++chrStorage;

    strcpy(pa_pRtuParams->m_acDevice, params);
    pa_pRtuParams->m_nBaud = (int) forte::core::util::strtol(chrStorage, 0, 10);

    chrStorage = strchr(chrStorage, ':');
    if(chrStorage == 0){
      delete[] paramsAddress;
      return -1;
    }
    *chrStorage = '\0';
    ++chrStorage;

    pa_pRtuParams->m_cParity = chrStorage[0];

    chrStorage = strchr(chrStorage, ':');
    if(chrStorage == 0){
      delete[] paramsAddress;
      return -1;
    }
    *chrStorage = '\0';
    ++chrStorage;

    pa_pRtuParams->m_nDataBit = (int) forte::core::util::strtol(chrStorage, 0, 10);

    chrStorage = strchr(chrStorage, ':');
    if(chrStorage == 0){
      delete[] paramsAddress;
      return -1;
    }
    *chrStorage = '\0';
    ++chrStorage;

    pa_pRtuParams->m_nStopBit = (int) forte::core::util::strtol(chrStorage, 0, 10);

    chrStorage = strchr(chrStorage, ':');
    if(chrStorage == 0){
      delete[] paramsAddress;
      return -1;
    }
    *chrStorage = '\0';
    ++chrStorage;
  }
  else{
    if(strcmp(params, "tcp") == 0 || strcmp(params, "TCP") == 0){
      params = chrStorage;

      chrStorage = strchr(chrStorage, ':');
      if(chrStorage == 0){
        delete[] paramsAddress;
        return -1;
      }
      *chrStorage = '\0';
      ++chrStorage;
    }
    if(isIp(params)){
      // TCP connection
      strcpy(pa_pTcpParams->m_acIp, params);
      pa_pTcpParams->m_nPort = (unsigned int) forte::core::util::strtoul(chrStorage, 0, 10);

      chrStorage = strchr(chrStorage, ':');
      if(chrStorage == 0){
        delete[] paramsAddress;
        return -1;
      }
      *chrStorage = '\0';
      ++chrStorage;
    }
    else{
      delete[] paramsAddress;
      return -1;
    }
  }
  // Get common parameters

  pa_pCommonParams->m_nPollFrequency = atol(chrStorage);

  chrStorage = strchr(chrStorage, ':');
  if(chrStorage == 0){
    delete[] paramsAddress;
    return -1;
  }
  *chrStorage = '\0';
  ++chrStorage;

  pa_pCommonParams->m_nFuncCode = (unsigned int) forte::core::util::strtoul(chrStorage, 0, 10);

  chrStorage = strchr(chrStorage, ':');
  if(chrStorage == 0){
    delete[] paramsAddress;
    return -1;
  }
  *chrStorage = '\0';
  ++chrStorage;

  // Search for optional parameter slave id
  char *chrSlave = strchr(chrStorage, ':');
  if(chrSlave != NULL){
    chrSlave++;
    if(strchr(chrSlave, ':') != NULL){
      pa_pCommonParams->m_nSlaveId = (unsigned int) forte::core::util::strtoul(chrStorage, 0, 10);

      chrStorage = chrSlave;
    }
    else{
      pa_pCommonParams->m_nSlaveId = 0xFF;
    }
  }
  else{
    delete[] paramsAddress;
    return -1;
  }

  char *readAddresses = chrStorage;
  chrStorage = strchr(chrStorage, ':');
  if(chrStorage == 0){
    delete[] paramsAddress;
    return -1;
  }
  *chrStorage = '\0';
  ++chrStorage;

  // Find read addresses
  int paramLen = strlen(readAddresses);
  int nrPolls = 0;
  int strIndex = -1;
  while(strIndex < paramLen - 1){
    strIndex = findNextStartAddress(readAddresses, ++strIndex);
    if(strIndex < 0){
      break;
    }
    pa_pCommonParams->m_nReadStartAddress[nrPolls] = (unsigned int) forte::core::util::strtoul(const_cast<char*>(&readAddresses[strIndex]), 0, 10);
    strIndex = findNextStopAddress(readAddresses, strIndex);
    pa_pCommonParams->m_nReadNrAddresses[nrPolls] = (unsigned int) forte::core::util::strtoul(const_cast<char*>(&readAddresses[strIndex]), 0, 10) - pa_pCommonParams->m_nReadStartAddress[nrPolls] + 1;
    nrPolls++;
  }
  pa_pCommonParams->m_nNrPolls = nrPolls;

  char *writeAddresses = chrStorage;
  chrStorage = strchr(chrStorage, ':');
  if(chrStorage != 0){
    *chrStorage = '\0';
    ++chrStorage;
  }

  // Find send addresses
  paramLen = strlen(writeAddresses);
  int nrSends = 0;
  strIndex = -1;
  while(strIndex < paramLen - 1){
    strIndex = findNextStartAddress(writeAddresses, ++strIndex);
    if(strIndex < 0){
      break;
    }
    pa_pCommonParams->m_nSendStartAddress[nrSends] = (unsigned int) forte::core::util::strtoul(const_cast<char*>(&writeAddresses[strIndex]), 0, 10);
    strIndex = findNextStopAddress(writeAddresses, strIndex);
    pa_pCommonParams->m_nSendNrAddresses[nrSends] = (unsigned int) forte::core::util::strtoul(const_cast<char*>(&writeAddresses[strIndex]), 0, 10) - pa_pCommonParams->m_nSendStartAddress[nrSends] + 1;
    nrSends++;
  }
  pa_pCommonParams->m_nNrSends = nrSends;

  // Find responseTimeout and byteTimeout
  do{
    if(chrStorage == 0){
      break;
    }
    pa_pCommonParams->m_nResponseTimeout = (unsigned int) forte::core::util::strtoul(chrStorage, 0, 10);

    chrStorage = strchr(chrStorage, ':');
    if(chrStorage == 0){
      break;
    }
    *chrStorage = '\0';
    ++chrStorage;

    pa_pCommonParams->m_nByteTimeout = (unsigned int) forte::core::util::strtoul(chrStorage, 0, 10);
  } while(0);

  if(nrPolls == 0 && nrSends == 0){
    delete[] paramsAddress;
    return -1;
  }

  delete[] paramsAddress;
  return 0;
}

int CModbusComLayer::findNextStartAddress(const char* pa_acParam, int pa_nStartIndex){
  if(pa_nStartIndex == 0){
    switch (pa_acParam[pa_nStartIndex]){
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        return pa_nStartIndex;
    }
  }

  int strLength = strlen(&pa_acParam[pa_nStartIndex]);
  const char *pch = strchr(&pa_acParam[pa_nStartIndex], ',');

  if(pch != NULL){
    if(pch - &pa_acParam[pa_nStartIndex] < strLength - 1) {
      return pch - &pa_acParam[0] + 1;
    }

  }

  return -1;
}

int CModbusComLayer::findNextStopAddress(const char* pa_acParam, int pa_nStartIndex){
  int strLength = strlen(&pa_acParam[pa_nStartIndex]);
  const char *pchComma = strchr(&pa_acParam[pa_nStartIndex], ',');
  const char *pchDot = strchr(&pa_acParam[pa_nStartIndex], '.');

  if(pchComma != NULL && pchDot != NULL) {
    if(pchDot < pchComma && (pchDot - &pa_acParam[pa_nStartIndex] < strLength - 2)) {
      return pchDot - &pa_acParam[0] + 2;
    }
  } else if(pchDot != NULL) {
    if(pchDot - &pa_acParam[pa_nStartIndex] < strLength - 2) {
      return pchDot - &pa_acParam[0] + 2;
    }
  }

  return pa_nStartIndex;
}

bool CModbusComLayer::isIp(const char* pa_acIp){
  char* str = new char[strlen(pa_acIp) + 1];
  strcpy(str, pa_acIp);
  char* pch;
  int nrPeriods = 0;

  pch = strtok(str, ".");
  while(pch != NULL){
    nrPeriods++;
    if(strlen(pch) > 3){
      delete[] str;
      return false;
    }
    for(unsigned int i = 0; i < strlen(pch); i++){
      if(!forte::core::util::isDigit(pch[i])){
        delete[] str;
        return false;
      }
    }

    pch = strtok(NULL, ".");
  }
  if(nrPeriods != 4){
    delete[] str;
    return false;
  }

  delete[] str;
  return true;
}
