/****************************************************************************
 * 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/mqtt/endpoint_mqtt_p.h"

#include "aqhome/mqtt/msg_mqtt_connect.h"
#include "aqhome/mqtt/msg_mqtt_publish.h"

#include <gwenhywfar/endpoint_msgio.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/inherit.h>


#define AQH_ENDPOINT_MQTT_DEFAULT_KEEPALIVE 600



GWEN_INHERIT(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_MQTT)




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

static void GWENHYWFAR_CB _freeData(void *bp, void *p);
static int _getBytesNeededForMessage(GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
static int _calcAndSetPayloadSizeAndOffset(GWEN_MSG *msg);



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


void AQH_MqttEndpoint_Extend(GWEN_MSG_ENDPOINT *ep)
{
  if (ep) {
    AQH_ENDPOINT_MQTT *xep;

    GWEN_NEW_OBJECT(AQH_ENDPOINT_MQTT, xep);
    GWEN_INHERIT_SETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_MQTT, ep, xep, _freeData);
    xep->keepAliveTime=AQH_ENDPOINT_MQTT_DEFAULT_KEEPALIVE;

    GWEN_MsgIoEndpoint_SetGetNeededBytesFn(ep, _getBytesNeededForMessage);
  }
}



void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p)
{
  AQH_ENDPOINT_MQTT *xep;

  xep=(AQH_ENDPOINT_MQTT*) p;
  free(xep->clientId);
  GWEN_FREE_OBJECT(xep);
}



const char *AQH_MqttEndpoint_GetClientId(const GWEN_MSG_ENDPOINT *ep)
{
  if (ep) {
    AQH_ENDPOINT_MQTT *xep;

    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_MQTT, ep);
    if (xep) {
      return xep->clientId;
    }
  }
  return NULL;
}



void AQH_MqttEndpoint_SetClientId(GWEN_MSG_ENDPOINT *ep, const char *s)
{
  if (ep) {
    AQH_ENDPOINT_MQTT *xep;

    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_MQTT, ep);
    if (xep) {
      free(xep->clientId);
      xep->clientId=s?strdup(s):NULL;
    }
  }
}



uint16_t AQH_MqttEndpoint_GetNextPacketId(const GWEN_MSG_ENDPOINT *ep)
{
  if (ep) {
    AQH_ENDPOINT_MQTT *xep;

    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_MQTT, ep);
    if (xep) {
      return ++(xep->lastPacketId);
    }
  }
  return 0;
}



uint16_t AQH_MqttEndpoint_GetKeepAliveTime(const GWEN_MSG_ENDPOINT *ep)
{
  if (ep) {
    AQH_ENDPOINT_MQTT *xep;

    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_MQTT, ep);
    if (xep) {
      return xep->keepAliveTime;
    }
  }
  return 0;
}



void AQH_MqttEndpoint_SetKeepAliveTime(GWEN_MSG_ENDPOINT *ep, uint16_t i)
{
  if (ep) {
    AQH_ENDPOINT_MQTT *xep;

    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_MQTT, ep);
    if (xep) {
      xep->keepAliveTime=i;
    }
  }
}



int _getBytesNeededForMessage(GWEN_UNUSED GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
  uint32_t bytesInMsg;

  bytesInMsg=GWEN_Msg_GetBytesInBuffer(msg);
  if (bytesInMsg<2) {
    DBG_DEBUG(AQH_LOGDOMAIN, "Header not yet complete");
    return (int) (2-bytesInMsg);
  }
  if (!(GWEN_Msg_GetFlags(msg) & GWEN_MSG_FLAGS_PAYLOADINFO_SET))
    _calcAndSetPayloadSizeAndOffset(msg);

  if (GWEN_Msg_GetFlags(msg) & GWEN_MSG_FLAGS_PAYLOADINFO_SET) {
    uint32_t msgLength;

    msgLength=GWEN_Msg_GetParsedPayloadOffset(msg)+GWEN_Msg_GetParsedPayloadSize(msg);
    return (int)(msgLength-bytesInMsg);
  }
  else {
    DBG_DEBUG(AQH_LOGDOMAIN, "Size field not complete, requesting another byte");
    return 1;
  }
}



int _calcAndSetPayloadSizeAndOffset(GWEN_MSG *msg)
{
  int remainingBytesInBuffer;
  uint32_t mqttRemainingLength=0;

  remainingBytesInBuffer=GWEN_Msg_GetBytesInBuffer(msg);
  if (remainingBytesInBuffer>1) {
    const uint8_t *ptr;
    int idx;
    int shift=0;

    ptr=GWEN_Msg_GetConstBuffer(msg);
    idx=1;
    remainingBytesInBuffer--;
    while(remainingBytesInBuffer) {
      uint8_t len;

      len=ptr[idx];
      mqttRemainingLength+=(len & 0x7f)<<shift;
      if (!(len & 0x80)) {
        /* last byte of size */
        GWEN_Msg_SetParsedPayloadSize(msg, mqttRemainingLength);
        GWEN_Msg_SetParsedPayloadOffset(msg, idx+1); /* payload follows at next byte */
        GWEN_Msg_AddFlags(msg, GWEN_MSG_FLAGS_PAYLOADINFO_SET);
        return 1; /* size successfully determined */
      }
      shift+=7;
      idx++;
      remainingBytesInBuffer--;
    }
  }
  return 0;
}



GWEN_MSG *AQH_MqttEndpoint_CreateMsgConnect(GWEN_MSG_ENDPOINT *ep)
{
  if (ep) {
    AQH_ENDPOINT_MQTT *xep;

    DBG_INFO(AQH_LOGDOMAIN, "Sending connect msg");
    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_MQTT, ep);
    if (xep) {
      return GWEN_ConnectMqttMsg_new("MQTT", 4, 0, xep->keepAliveTime, xep->clientId, NULL, NULL);
    }
  }
  return NULL;
}



