/*******************************************************************************
 * Copyright (c) 2011 - 2013 ACIN, Profactor GmbH, fortiss GmbH, nxtControl 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:
 *    Alois Zoitl, Monika Wenger, Matthias Plasch, Martin Melik Markumians,
 *    Ingo Hegny, Stanislav Meduna
 *      - initial implementation and rework communication infrastructure
 *******************************************************************************/
#include "forte_any_elementary.h"
#ifdef FORTE_ENABLE_GENERATED_SOURCE_CPP
#include "forte_any_elementary_gen.cpp"
#endif
#include <stdlib.h>
#include <errno.h>
#include "forte_sint.h"
#include "forte_int.h"
#include "forte_dint.h"
#include "forte_usint.h"
#include "forte_uint.h"
#include "forte_udint.h"
#include "forte_lint.h"
#include "forte_ulint.h"

DEFINE_FIRMWARE_DATATYPE(ANY_ELEMENTARY, g_nStringIdANY_ELEMENTARY)

const CStringDictionary::TStringId CIEC_ANY_ELEMENTARY::scm_anTypeNameStringIds[] = { g_nStringIdANY, g_nStringIdBOOL, g_nStringIdSINT, g_nStringIdINT, g_nStringIdDINT, g_nStringIdLINT, g_nStringIdUSINT, g_nStringIdUINT, g_nStringIdUDINT, g_nStringIdULINT, g_nStringIdBYTE, g_nStringIdWORD, g_nStringIdDWORD, g_nStringIdLWORD, g_nStringIdDATE, g_nStringIdTIME_OF_DAY, g_nStringIdDATE_AND_TIME, g_nStringIdTIME, //until here simple Datatypes
    g_nStringIdREAL, g_nStringIdLREAL, g_nStringIdSTRING, //e_STRING,
    g_nStringIdWSTRING, //e_WSTRING,
    CStringDictionary::scm_nInvalidStringId, //e_DerivedData,
    CStringDictionary::scm_nInvalidStringId, //e_DirectlyDerivedData,
    CStringDictionary::scm_nInvalidStringId, //e_EnumeratedData,
    CStringDictionary::scm_nInvalidStringId, //e_SubrangeData,
    CStringDictionary::scm_nInvalidStringId, //e_ARRAY,
    CStringDictionary::scm_nInvalidStringId, //e_STRUCT,
    CStringDictionary::scm_nInvalidStringId, //e_External,
    CStringDictionary::scm_nInvalidStringId, //e_Max
    };

int CIEC_ANY_ELEMENTARY::toString(char* paValue, size_t paBufferSize) const {
  int nRetVal = 0;

  TLargestUIntValueType nDivisor = 0;
  TLargestUIntValueType nUBuffer = 0;
  TLargestIntValueType nSBuffer = 0;

  bool bSigned = true;

  switch (getDataTypeID()){
    case e_SINT:
      nSBuffer = getTINT8();
      nDivisor = 100;
      break;
    case e_USINT:
    case e_BYTE:
      nUBuffer = getTUINT8();
      nDivisor = 100;
      bSigned = false;
      break;
    case e_INT:
      nSBuffer = getTINT16();
      nDivisor = 10000;
      break;
    case e_UINT:
    case e_WORD:
      nUBuffer = getTUINT16();
      nDivisor = 10000;
      bSigned = false;
      break;
    case e_DINT:
      nSBuffer = getTINT32();
      nDivisor = 1000000000L;
      break;
    case e_UDINT:
    case e_DWORD:
      nUBuffer = getTUINT32();
      nDivisor = 1000000000L;
      bSigned = false;
      break;
#ifdef FORTE_USE_64BIT_DATATYPES
      case e_LINT:
      nSBuffer = getTINT64();
      nDivisor = 1000000000000000000LL;
      break;
    case e_ULINT:
      case e_LWORD:
      nUBuffer = getTUINT64();
      nDivisor = 10000000000000000000ULL;
      bSigned = false;
      break;
#endif
    default:
      return CIEC_ANY::toString(paValue, paBufferSize);
  }

  if(true == bSigned){
    if(nSBuffer < 0){
      if(nRetVal >= static_cast<int>(paBufferSize)) {
        return -1;
      }
      paValue[nRetVal] = '-';
      nRetVal++;
      nSBuffer *= -1;
    }
    nUBuffer = static_cast<TLargestUIntValueType>(nSBuffer);
  }

  bool bLeadingZeros = true;
  do{
    if((0 == nUBuffer / nDivisor) && (true == bLeadingZeros)){
      nDivisor /= 10;
      continue;
    }
    else{
      bLeadingZeros = false;
    }
    if(nRetVal >= static_cast<int>(paBufferSize)) {
      return -1;
    }

    paValue[nRetVal] = static_cast<char>(static_cast<char>(nUBuffer / nDivisor) + '0');
    nUBuffer = nUBuffer - (paValue[nRetVal] - '0') * nDivisor;
    nDivisor /= 10;
    nRetVal++;
  } while(nDivisor > 1);

  if(nRetVal >= static_cast<int>(paBufferSize)) {
    return -1;
  }
  paValue[nRetVal] = static_cast<char>(static_cast<char>(nUBuffer / nDivisor) + '0');
  nRetVal++;

  if(nRetVal >= static_cast<int>(paBufferSize)) {
    return -1;
  }
  paValue[nRetVal] = '\0';

  return nRetVal;
}

int CIEC_ANY_ELEMENTARY::fromString(const char *pa_pacValue){
  int nRetVal = -1;
  const char *pacRunner = pa_pacValue;

  if((0 == pa_pacValue) || ('\0' == *pa_pacValue)){
    return -1;
  }

  if(e_ANY == getDataTypeID()){
    nRetVal = CIEC_ANY::fromString(pa_pacValue);
    //TODO think of a check if it is really an any elementary that has been created
  }
  else{
    const char *acHashPos = strchr(pa_pacValue, '#');
    int nMultiplier = 10;
    bool bSigned = true;
    if((0 != acHashPos) && (!forte::core::util::isDigit(*pa_pacValue))){
      //if we have a hash and the first character is not a digit it has to be a type identifier
      nRetVal = checkTypeSpec(pa_pacValue, acHashPos);
      if(nRetVal < 0){
        return -1;
      }
      pacRunner += (nRetVal + 1); //put the runner one after the hash
      nRetVal = -1;
    }

    TLargestUIntValueType nUUpperBound = 0;
    TLargestIntValueType nSUpperBound = 0;
    TLargestIntValueType nSLowerBound = 0;

    if('#' == pacRunner[1]){
      switch (pacRunner[0]){
        case '2':
          nMultiplier = 2;
          break;
        case '8':
          nMultiplier = 8;
          break;
        default:
          return -1;
          break;
      }
      pacRunner = pacRunner + 2;
    }
    else if('\0' != pacRunner[1] && '#' == pacRunner[2]){
      if('1' == pacRunner[0]){
        if('6' == pacRunner[1]){
          nMultiplier = 16;
        }
        else{
          if('0' != pacRunner[1]){
            return -1;
          }
        }
      } else {
        return -1;
      }
      pacRunner = pacRunner + 3;
    }

    EDataTypeID eID = getDataTypeID();
    switch (eID){
      case e_SINT:
        nSUpperBound = CIEC_SINT::scm_nMaxVal;
        nSLowerBound = CIEC_SINT::scm_nMinVal;
        break;
      case e_INT:
        nSUpperBound = CIEC_INT::scm_nMaxVal;
        nSLowerBound = CIEC_INT::scm_nMinVal;
        break;
      case e_DINT:
        nSUpperBound = CIEC_DINT::scm_nMaxVal;
        nSLowerBound = CIEC_DINT::scm_nMinVal;
        break;
#ifdef FORTE_USE_64BIT_DATATYPES
        case e_LINT:
        nSUpperBound = CIEC_LINT::scm_nMaxVal;
        nSLowerBound = CIEC_LINT::scm_nMinVal;
        break;
#endif
      case e_USINT:
      case e_BYTE:
        nUUpperBound = CIEC_USINT::scm_nMaxVal;
        bSigned = false;
        break;
      case e_UINT:
      case e_WORD:
        nUUpperBound = CIEC_UINT::scm_nMaxVal;
        bSigned = false;
        break;
      case e_UDINT:
      case e_DWORD:
        nUUpperBound = CIEC_UDINT::scm_nMaxVal;
        bSigned = false;
        break;
#ifdef FORTE_USE_64BIT_DATATYPES
      case e_ULINT:
      case e_LWORD:
        nUUpperBound = CIEC_ULINT::scm_nMaxVal;
        bSigned = false;
        break;
#endif
      default:
        return false;
    }

    if((bSigned) && (10 != nMultiplier)){
      //only decimal numbers have a sign
      bSigned = false;
      nUUpperBound = 2 * nSUpperBound + 1;
    }

    char *pacEndPtr;
    errno = 0; //erno is not cleared by the strto* functions

    if(true == bSigned){
#ifdef FORTE_USE_64BIT_DATATYPES
      TForteInt64 nValue = forte::core::util::strtoll(pacRunner, &pacEndPtr, nMultiplier);
#else
      TForteInt32 nValue = forte::core::util::strtol(pacRunner, &pacEndPtr, nMultiplier);
#endif

      if((ERANGE != errno)&& (nValue <= nSUpperBound) && (nValue
          >= nSLowerBound)){
      setLargestInt(nValue);
      nRetVal = static_cast<int>(pacEndPtr - pa_pacValue);
    }
  }
  else{
    if ('-' != *pacRunner){
      //The strtou* functions will correctly parse also negative numbers and provide their two complement as value
#ifdef FORTE_USE_64BIT_DATATYPES
          TForteUInt64 nValue = forte::core::util::strtoull(pacRunner, &pacEndPtr, nMultiplier);
#else
          TForteUInt32 nValue = forte::core::util::strtoul(pacRunner, &pacEndPtr, nMultiplier);
#endif //FORTE_USE_64BIT_DATATYPES
          if ((ERANGE != errno) && (nValue <= nUUpperBound)){
            setLargestUInt(nValue);
            nRetVal = static_cast<int>(pacEndPtr - pa_pacValue);
          }
        }
      }
    if(0 == nRetVal){
      // at this point a length of zero means broken string so return -1
      nRetVal = -1;
    }
  }
  return nRetVal;
}

int CIEC_ANY_ELEMENTARY::checkTypeSpec(const char *pa_pacValue, const char *pa_pacHashPos){
  int nRetVal = -1;

  CStringDictionary::TStringId nTypeNameId = parseTypeName(pa_pacValue, pa_pacHashPos);

  if((CStringDictionary::scm_nInvalidStringId != nTypeNameId) && (scm_anTypeNameStringIds[getDataTypeID()] == nTypeNameId)){
    nRetVal = static_cast<int>(pa_pacHashPos - pa_pacValue);
  }
  return nRetVal;
}

