/****************************************************************************
 * This file is part of the project AqHome.
 * AqHome (c) by 2025 Martin Preuss, all rights reserved.
 *
 * The license for this file can be found in the file COPYING which you
 * should have received along with this file.
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "./message_p.h"

#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endianfns.h>



GWEN_LIST_FUNCTIONS(AQH_MESSAGE, AQH_Message)
GWEN_INHERIT_FUNCTIONS(AQH_MESSAGE)



/* ------------------------------------------------------------------------------------------------
 * forward declarations
 * ------------------------------------------------------------------------------------------------
 */

static void _ensureSize(AQH_MESSAGE *msg, uint32_t neededSize);



/* ------------------------------------------------------------------------------------------------
 * implementations
 * ------------------------------------------------------------------------------------------------
 */

AQH_MESSAGE *AQH_Message_new(void)
{
  AQH_MESSAGE *msg;

  GWEN_NEW_OBJECT(AQH_MESSAGE, msg);
  GWEN_INHERIT_INIT(AQH_MESSAGE, msg);
  GWEN_LIST_INIT(AQH_MESSAGE, msg);
  msg->refCount=1;

  return msg;
}



AQH_MESSAGE *AQH_Message_dup(const AQH_MESSAGE *origMsg)
{
  if (origMsg) {
    AQH_MESSAGE *msg;

    msg=AQH_Message_new();
    if (origMsg->msgPointer && origMsg->msgSize) {
      AQH_Message_SetData(msg, origMsg->msgPointer, origMsg->msgSize);
      msg->usedSize=origMsg->usedSize;
    }
    return msg;
  }
  else
    return NULL;
}



void AQH_Message_IncRef(AQH_MESSAGE *msg)
{
  if (msg && msg->refCount)
    msg->refCount++;
}



void AQH_Message_free(AQH_MESSAGE *msg)
{
  if (msg && msg->refCount>0) {
    if (msg->refCount==1) {
      msg->refCount=0;
      GWEN_LIST_FINI(AQH_MESSAGE, msg);
      GWEN_INHERIT_FINI(AQH_MESSAGE, msg);
      free(msg->msgPointer);
      GWEN_FREE_OBJECT(msg);
    }
    else
      msg->refCount--;
  }
}



AQH_OBJECT *AQH_Message_GetObject(const AQH_MESSAGE *msg)
{
  return (msg && msg->refCount)?msg->object:NULL;
}



void AQH_Message_SetObject(AQH_MESSAGE *msg, AQH_OBJECT *o)
{
  if (msg && msg->refCount)
    msg->object=o;
}



uint8_t *AQH_Message_GetMsgPointer(const AQH_MESSAGE *msg)
{
  return (msg && msg->refCount)?msg->msgPointer:NULL;
}



uint32_t AQH_Message_GetMsgSize(const AQH_MESSAGE *msg)
{
  return (msg && msg->refCount)?msg->msgSize:0;
}



void AQH_Message_SetData(AQH_MESSAGE *msg, const uint8_t *msgPtr, uint32_t msgSize)
{
  if (msg && msg->refCount) {
    free(msg->msgPointer);
    msg->msgPointer=NULL;
    msg->msgSize=0;
    if (msgSize) {
      msg->msgPointer=(uint8_t*) malloc(msgSize);
      if (msg->msgPointer) {
        if (msgPtr)
          memmove(msg->msgPointer, msgPtr, msgSize);
        else
          memset(msg->msgPointer, 0, msgSize);
        msg->msgSize=msgSize;
      }
    }
  }
}



uint32_t AQH_Message_GetUsedSize(const AQH_MESSAGE *msg)
{
  return msg?msg->usedSize:0;
}



void AQH_Message_SetUsedSize(AQH_MESSAGE *msg, uint32_t i)
{
  if (msg) {
    _ensureSize(msg, i);
    msg->usedSize=i;
  }
}



void AQH_Message_IncUsedSize(AQH_MESSAGE *msg, uint32_t i)
{
  if (msg) {
    _ensureSize(msg, msg->usedSize+i);
    msg->usedSize+=i;
  }
}






void AQH_Message_WriteUint8At(AQH_MESSAGE *msg, uint32_t pos, uint8_t d)
{
  if (msg) {
    _ensureSize(msg, pos+1);
    msg->msgPointer[pos]=d;
  }
}



uint8_t AQH_Message_ReadUint8At(const AQH_MESSAGE *msg, uint32_t pos, uint8_t defaultValue)
{
  if (msg && msg->msgPointer && msg->msgSize>=pos+1)
    return msg->msgPointer[pos];
  return defaultValue;
}



void AQH_Message_WriteUint16At(AQH_MESSAGE *msg, uint32_t pos, uint16_t d)
{
  if (msg) {
    _ensureSize(msg, pos+2);
    *( (uint16_t*) (msg->msgPointer+pos) )=GWEN_ENDIAN_HTOLE16(d);
  }
}



uint16_t AQH_Message_ReadUint16At(const AQH_MESSAGE *msg, uint32_t pos, uint16_t defaultValue)
{
  if (msg && msg->msgPointer && msg->msgSize>=pos+2)
    return GWEN_ENDIAN_LE16TOH(*( (uint16_t*) (msg->msgPointer+pos) ));
  return defaultValue;
}



void AQH_Message_WriteUint32At(AQH_MESSAGE *msg, uint32_t pos, uint32_t d)
{
  if (msg) {
    _ensureSize(msg, pos+4);
    *( (uint32_t*) (msg->msgPointer+pos) )=GWEN_ENDIAN_HTOLE32(d);
  }
}



uint32_t AQH_Message_ReadUint32At(const AQH_MESSAGE *msg, uint32_t pos, uint32_t defaultValue)
{
  if (msg && msg->msgPointer && msg->msgSize>=pos+4)
    return GWEN_ENDIAN_LE32TOH(*( (uint32_t*) (msg->msgPointer+pos) ));
  return defaultValue;
}



void AQH_Message_WriteUint64At(AQH_MESSAGE *msg, uint32_t pos, uint64_t d)
{
  if (msg) {
    _ensureSize(msg, pos+8);
    *( (uint64_t*) (msg->msgPointer+pos) )=GWEN_ENDIAN_HTOLE64(d);
  }
}



uint64_t AQH_Message_ReadUint64At(const AQH_MESSAGE *msg, uint32_t pos, uint64_t defaultValue)
{
  if (msg && msg->msgPointer && msg->msgSize>=pos+8)
    return GWEN_ENDIAN_LE64TOH(*( (uint64_t*) (msg->msgPointer+pos) ));
  return defaultValue;
}



void AQH_Message_WriteBytesAt(AQH_MESSAGE *msg, uint32_t pos, const uint8_t *bufferPtr, uint32_t bufferLen)
{
  if (msg && bufferPtr && bufferLen) {
    _ensureSize(msg, pos+bufferLen);
    memmove(msg->msgPointer+pos, bufferPtr, bufferLen);
  }
}



int AQH_Message_WriteStringAt(AQH_MESSAGE *msg, uint32_t pos, uint32_t maxSize, int filler, const char *s)
{
  if (msg && maxSize) {
    uint32_t slen=0;

    if (s) {
      slen=strlen(s);
      if (slen>maxSize) {
        DBG_ERROR(AQH_LOGDOMAIN, "String too long (%d > %d)", slen, maxSize);
        return GWEN_ERROR_INVALID;
      }
    }
    _ensureSize(msg, pos+maxSize);
    if (s)
      memmove(msg->msgPointer+pos, s, slen);
    if (slen<maxSize)
      memset(msg->msgPointer+pos+slen, filler, maxSize-slen);
  }
  return 0;
}



int AQH_Message_WriteStringWithTrailingNullAt(AQH_MESSAGE *msg, uint32_t pos, uint32_t maxSize, int filler, const char *s)
{
  if (msg && maxSize) {
    uint32_t slen=1;

    if (s) {
      slen=strlen(s)+1;
      if (slen>maxSize) {
        DBG_ERROR(AQH_LOGDOMAIN, "String too long (%d > %d)", slen, maxSize);
        return GWEN_ERROR_INVALID;
      }
    }
    _ensureSize(msg, pos+maxSize);
    if (s)
      memmove(msg->msgPointer+pos, s, slen);
    if (slen<maxSize)
      memset(msg->msgPointer+pos+slen, filler, maxSize-slen);
  }
  return 0;
}



void _ensureSize(AQH_MESSAGE *msg, uint32_t neededSize)
{
  if (msg && neededSize) {
    if (msg->msgPointer==0 || msg->msgSize==0) {
      msg->msgPointer=(uint8_t*) malloc(neededSize);
      assert(msg->msgPointer);
    }
    else {
      if (neededSize>msg->msgSize) {
        uint8_t *ptr;

        ptr=realloc(msg->msgPointer, neededSize);
        assert(ptr);
        if (ptr) {
          msg->msgPointer=ptr;
          msg->msgSize=neededSize;
        }
      }
    }
  }
}



