/*******************************************************************************
 * Copyright (c) 2005 - 2015 Profactor GmbH, ACIN, nxtControl GmbH, fortiss GmbH
 *               2018 TU Wien/ACIN
 *
 * 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:
 *    Thomas Strasser, Ingomar Müller, Rene Smodic, Alois Zoitl,
 *    Ingo Hegny, Martin Melik Merkumians, Stanislav Meduna
 *      - initial implementation and rework communication infrastructure
 *    Martin Melik Merkumians - fixes toString behavior for 0 size buffer
 *    Martin Melik Merkumians - fixes behavior for getToStringBufferSize
 *******************************************************************************/
#include "forte_string.h"
#include <devlog.h>
#include "forte_uint.h"
#include "unicode_utils.h"

DEFINE_FIRMWARE_DATATYPE(STRING, g_nStringIdSTRING);

int CIEC_STRING::fromString(const char *pa_pacValue){
  int nSrcLen = determineEscapedStringLength(pa_pacValue, '\'');
  int nSrcCappedLength = nSrcLen;

  if(0 < nSrcLen){
    if((nSrcLen >= 7) && (0 == strncmp(pa_pacValue, "STRING#", 7))){
      pa_pacValue += 7;
      nSrcCappedLength -= 7;
    }

    if(static_cast<unsigned int>(nSrcLen) > scm_unMaxStringLen) {
      nSrcCappedLength = scm_unMaxStringLen;
    }


    if (*pa_pacValue == '\'') {
      reserve(static_cast<TForteUInt16>(nSrcCappedLength));
      if(unescapeFromString(pa_pacValue, '\'') < 0) {
        return -1;
      }
    } else {
      assign(pa_pacValue, static_cast<TForteUInt16>(nSrcCappedLength));
    }
  }

  return nSrcLen;
}

int CIEC_STRING::toString(char* paValue, size_t paBufferSize) const {
  int nRetVal = -1;
  if(0 != paValue && getToStringBufferSize() <= paBufferSize){
    *paValue = '\'';
    paValue++;
    TForteUInt16 nLen = length();
    size_t nUsedBytes = 0;
    paBufferSize -= 2;
    if(0 < nLen){
      const char * acValue = getValue();
      for(unsigned int i = 0; i < nLen; ++i){
        if(nUsedBytes >= paBufferSize) {
          return -1;
        }
        nUsedBytes += dollarEscapeChar(paValue+nUsedBytes, acValue[i], 2);
      }

    } else{
      *paValue = '\0';
    }
    nRetVal = static_cast<int>(nUsedBytes);
    paValue[nRetVal] = '\'';
    paValue[nRetVal + 1] = '\0';
    nRetVal += 2;
  }
  return nRetVal;
}

size_t CIEC_STRING::getToStringBufferSize() const {
  const char * const stringValue = getValue();
  size_t neededBufferSize = 0;
  for(size_t i = 0; i < length(); ++i){
    if(isprint(static_cast<unsigned char>(stringValue[i])) && '$' != stringValue[i] && '\'' != stringValue[i]){
      ++neededBufferSize;
    }
    else{
      switch (stringValue[i]){
        case '$':
        case '\'':
        case 0x10: // line feed
        case '\n':
        case '\f':
        case '\r':
        case '\t':
          neededBufferSize += 2;
          break;
        default:
          neededBufferSize += 3;
          break;
      }
    }
  }
  return neededBufferSize + 2 + 1; // For Quotes and \0
}

#ifdef FORTE_UNICODE_SUPPORT
int CIEC_STRING::fromUTF8(const char *pa_pacValue, int pa_nLen, bool pa_bUnescape) {

  int nSrcLen = pa_nLen >= 0 ? pa_nLen : (pa_bUnescape ? determineEscapedStringLength(pa_pacValue, '\'') : static_cast<int>(strlen(pa_pacValue)));
  int nSrcCappedLength = nSrcLen;

  if(0 <= nSrcLen){
    if (nSrcLen == 0) {
      assign("", 0);
      return 0;
    }

    if (nSrcLen > static_cast<int>(scm_unMaxStringLen)) {
      // If we get a to large string we will truncate it
      // This is a conservative guess
      nSrcCappedLength = scm_unMaxStringLen;
      DEVLOG_WARNING("Too large string, destination will be truncated!\n");
    }

    unsigned int nMaxWidth;
    int nLength = CUnicodeUtilities::checkUTF8(pa_pacValue, nSrcCappedLength, nMaxWidth);
    if (nLength < 0) {
      DEVLOG_WARNING("Invalid UTF-8 string given to fromString!\n");
      *this = "***INVALID UTF-8***";
      return -1;
    } else if (nMaxWidth > 8) {
      DEVLOG_WARNING("UTF-8 string with non-representable characters given to fromString!\n");
    }

    reserve(static_cast<TForteUInt16>(nLength));
    if (0 == getGenData()) {
      return -1;
    }

    TForteUInt32 nCodepoint;
    const char *pRunner = pa_pacValue;
    TForteByte *pEncBuffer = (TForteByte *) getValue();
    TForteByte *pEncRunner = pEncBuffer;

    while (*pRunner && (pRunner-pa_pacValue) < nSrcCappedLength && (pEncRunner-pEncBuffer) < nLength) {
      int nRes;
      nRes = CUnicodeUtilities::parseUTF8Codepoint((const TForteByte *) pRunner, nCodepoint);
      pRunner += nRes;
      if (nCodepoint == CUnicodeUtilities::scm_unBOMMarker) {
        continue;
      }
      if (nCodepoint >= 0x100) {
        nCodepoint = '?';
      }
      *pEncRunner++ = (TForteByte) nCodepoint;
    }

    *pEncRunner = '\0';
    setLength(static_cast<TForteUInt16>(pEncRunner - pEncBuffer));

    if (pa_bUnescape) {
      nLength = unescapeFromString(getValue(), '\'');
      if (nLength < 0) {
        return -1;
      }
    }
  }
  return nSrcLen;
}

int CIEC_STRING::toUTF8(char* paBuffer, size_t paBufferSize, bool paEscape) const {
  // Count the needed space
  size_t nNeededLength = paEscape ? 2 : 0; // Leading and trailing delimiter
  int nRes;

  const unsigned char *pRunner = (const unsigned char *) getValue();
  while (*pRunner) {
    nRes = CUnicodeUtilities::encodeUTF8Codepoint(0, 0, (TForteUInt32) *pRunner);
    if (nRes < 0) {
      return -1;
    }
    nNeededLength += nRes;
    if (nRes == 1 && paEscape && dollarEscapeChar(0, *pRunner, 0) == 2) {
      nNeededLength++;
    }
    ++pRunner;
  }

  if(0 == paBuffer) {
    return static_cast<int>(nNeededLength);
  }

  if(nNeededLength + 1 > paBufferSize) {
    return -1;
  }

  char *pEncRunner = paBuffer;
  char *pDataEnd = paBuffer + nNeededLength;

  if(paEscape) {
    *pEncRunner++ = '\'';
  }

  pRunner = (const unsigned char *) getValue();
  while (*pRunner) {
    nRes = CUnicodeUtilities::encodeUTF8Codepoint((TForteByte *) pEncRunner, static_cast<unsigned int>(pDataEnd - pEncRunner), (TForteUInt32) *pRunner);
    if(nRes == 1 && paEscape) {
      nRes = dollarEscapeChar(pEncRunner, *pRunner, static_cast<unsigned int>(pDataEnd - pEncRunner));
    }
    if(nRes < 0) {
      return -1;
    }

    pEncRunner += nRes;
    ++pRunner;
  }

  if(paEscape) {
    *pEncRunner++ = '\'';
  }
  *pEncRunner = '\0';

  return static_cast<int>(pEncRunner - paBuffer);
}

#endif
