/****************************************************************************
 * This file is part of the project AqHome.
 * AqHome (c) by 2023 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 <aqhome/ipc/data/msg_data_datapoints.h>
#include <aqhome/ipc/data/ipc_data.h>

#include <gwenhywfar/msg.h>
#include <gwenhywfar/buffer.h>

#include <gwenhywfar/debug.h>
#include <gwenhywfar/msg_ipc.h>

#include <string.h>


#define AQH_MSGDATA_DATAPOINTS_OFFS_FLAGS         0   /* 4 bytes */
#define AQH_MSGDATA_DATAPOINTS_OFFS_NUMVALUES     4   /* 4 bytes */

#define AQH_MSGDATA_DATAPOINTS_OFFS_VALUEID       8   /* 8 byte */
#define AQH_MSGDATA_DATAPOINTS_OFFS_VALUENAME     16  /* 104 byte */
#  define AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME     104
#define AQH_MSGDATA_DATAPOINTS_OFFS_VALUEUNITS    120  /* 16 byte */
#  define AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS    16
#define AQH_MSGDATA_DATAPOINTS_OFFS_VALUES        136



#define AQH_MSGDATA_DATAPOINTS_MINSIZE (GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUES)


static void _writeQword(uint64_t i64, uint8_t *ptr);




GWEN_MSG *AQH_DataPointsDataIpcMsg_new(uint16_t code, uint32_t flags,
                                       uint64_t valueId, const char *valueName, const char *units,
                                       const uint64_t *i64Ptr, int numOfDataPoints)
{
  GWEN_MSG *msg;
  uint8_t *ptr;
  int payloadSize;
  int i;

  payloadSize=AQH_MSGDATA_DATAPOINTS_OFFS_VALUES+(numOfDataPoints*16);

  msg=GWEN_IpcMsg_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, code, payloadSize, NULL);
  ptr=GWEN_Msg_GetBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD;
  *(ptr++)=flags & 0xff;
  *(ptr++)=(flags>>8) & 0xff;
  *(ptr++)=(flags>>16) & 0xff;
  *(ptr++)=(flags>>24) & 0xff;

  *(ptr++)=numOfDataPoints & 0xff;
  *(ptr++)=(numOfDataPoints>>8) & 0xff;
  *(ptr++)=(numOfDataPoints>>16) & 0xff;
  *(ptr++)=(numOfDataPoints>>24) & 0xff;

  _writeQword(valueId, ptr);
  ptr+=8;


  if (valueName) {
    strncpy((char*) ptr, valueName, AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME-1);
    ptr[AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME-1]=0;
  }
  else
    memset(ptr, 0, AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME);
  ptr+=AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME;

  if (units) {
    strncpy((char*) ptr, units, AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS-1);
    ptr[AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS-1]=0;
  }
  else
    memset(ptr, 0, AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS);
  ptr+=AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS;


  for (i=0; i<numOfDataPoints; i++) {
    _writeQword(*i64Ptr, ptr);
    i64Ptr++;
    ptr+=8;

    _writeQword(*i64Ptr, ptr);
    i64Ptr++;
    ptr+=8;
  }
  return msg;
}



void _writeQword(uint64_t i64, uint8_t *ptr)
{
  *(ptr++)=i64 & 0xff;
  *(ptr++)=(i64>>8) & 0xff;
  *(ptr++)=(i64>>16) & 0xff;
  *(ptr++)=(i64>>24) & 0xff;
  *(ptr++)=(i64>>32) & 0xff;
  *(ptr++)=(i64>>40) & 0xff;
  *(ptr++)=(i64>>48) & 0xff;
  *(ptr++)=(i64>>56) & 0xff;
}



uint32_t AQH_DataPointsDataIpcMsg_GetFlags(const GWEN_MSG *msg)
{
  return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_FLAGS, 0);
}



uint32_t AQH_DataPointsDataIpcMsg_GetNumValues(const GWEN_MSG *msg)
{
  return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_NUMVALUES, 0);
}



uint64_t AQH_DataPointsDataIpcMsg_GetValueId(const GWEN_MSG *msg)
{
  return GWEN_Msg_GetUint64At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUEID, 0);
}



const char *AQH_DataPointsDataIpcMsg_GetValueName(const GWEN_MSG *msg)
{
  if (GWEN_Msg_GetBytesInBuffer(msg)>=AQH_MSGDATA_DATAPOINTS_MINSIZE)
    return (const char*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUENAME);
  return NULL;
}



const char *AQH_DataPointsDataIpcMsg_GetUnits(const GWEN_MSG *msg)
{
  if (GWEN_Msg_GetBytesInBuffer(msg)>=AQH_MSGDATA_DATAPOINTS_MINSIZE)
    return (const char*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUEUNITS);
  return NULL;
}



const uint64_t *AQH_DataPointsDataIpcMsg_GetDataPoints(const GWEN_MSG *msg)
{
  if (GWEN_Msg_GetBytesInBuffer(msg)>=AQH_MSGDATA_DATAPOINTS_MINSIZE)
    return (const uint64_t*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUES);
  return NULL;

}



int AQH_DataPointsDataIpcMsg_IsValid(const GWEN_MSG *msg)
{
  int msgLen;
  int numValues;
  const uint8_t *ptr;

  msgLen=GWEN_Msg_GetBytesInBuffer(msg);
  if (msgLen<AQH_MSGDATA_DATAPOINTS_MINSIZE) {
    DBG_ERROR(AQH_LOGDOMAIN, "Message too small (%d<%d)", GWEN_Msg_GetBytesInBuffer(msg), AQH_MSGDATA_DATAPOINTS_MINSIZE);
    return 0;
  }

  numValues=(int)AQH_DataPointsDataIpcMsg_GetNumValues(msg);
  if (msgLen<(numValues*16)+AQH_MSGDATA_DATAPOINTS_OFFS_VALUES+GWEN_MSGIPC_OFFS_PAYLOAD) {
    DBG_ERROR(AQH_LOGDOMAIN, "Message too small to contain %d values", numValues);
    return 0;
  }

  ptr=GWEN_Msg_GetConstBuffer(msg);
  if (ptr[GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUENAME+AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME-1]!=0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Name string for value is not null-terminated");
    return 0;
  }

  if (ptr[GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUEUNITS+AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS-1]!=0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Units string for value is not null-terminated");
    return 0;
  }

  return 1;
}



void AQH_DataPointsDataIpcMsg_DumpToBuffer(const GWEN_MSG *msg, GWEN_BUFFER *dbuf, const char *sText)
{
  if (GWEN_Msg_GetBytesInBuffer(msg)>=AQH_MSGDATA_DATAPOINTS_MINSIZE) {
    const char *valueName;

    valueName=AQH_DataPointsDataIpcMsg_GetValueName(msg);
    GWEN_Buffer_AppendArgs(dbuf,
                           "DATAPOINTS (code=%d, proto=%d, proto version=%d, flags=0x%08x, valueName=%s, values=%d)\n",
                           GWEN_IpcMsg_GetCode(msg),
                           GWEN_IpcMsg_GetProtoId(msg),
                           GWEN_IpcMsg_GetProtoVersion(msg),
                           (unsigned int)AQH_DataPointsDataIpcMsg_GetFlags(msg),
                           valueName?valueName:"<no name>",
                           AQH_DataPointsDataIpcMsg_GetNumValues(msg));
  }
}







