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

#include "./server_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"

#include <gwenhywfar/debug.h>



/* ------------------------------------------------------------------------------------------------
 * defines
 * ------------------------------------------------------------------------------------------------
 */

#define R_SETDATA_REQUEST_EXPIRE_SECS    20
#define R_SETDATA_SUBREQUEST_EXPIRE_SECS 10



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

static void _storeDatapoint(AQHOME_SERVER *xo, const AQH_VALUE *v, double valueData);
static AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
                                           AQH_OBJECT *epSrc, uint32_t requestMsgId,
                                           AQH_OBJECT *epDriver,
                                           const AQH_VALUE *v, double data);
static void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason);
static void _rqAbort(AQH_MSG_REQUEST *rq, int reason);

static AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, double data);
static int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg);
static void _subRqAbort(AQH_MSG_REQUEST *rq, int reason);



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

void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList)
{
  if (tagList) {
    AQHOME_SERVER *xo;
  
    xo=AqHomeDataServer_GetServerData(o);
    if (xo) {
      uint32_t msgId;
      AQH_VALUE *recvdValue;
  
      msgId=AQH_IpcMessage_GetMsgId(recvdMsg);
      DBG_INFO(NULL, "Received IPC SetDataRequest message (msgId=%d)", msgId);
  
      recvdValue=AQH_IpcdMessageSetData_ReadValue(tagList);
      if (recvdValue) {
        const char *valueName;
        double valueData;
        AQH_VALUE *systemValue;
  
        valueName=AQH_Value_GetNameForSystem(recvdValue);
        valueData=AQH_IpcdMessageSetData_ReadData(tagList);
  
        systemValue=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
        if (systemValue) {
          if (AQH_Value_GetValueType(systemValue)==AQH_ValueType_Actor) {
            const char *driverName;
  
            driverName=AQH_Value_GetDriver(systemValue);
            if (driverName && *driverName) {
              AQH_OBJECT *epDriver;
  
              epDriver=AqHomeDataServer_GetIpcEndpointByServiceName(o, driverName);
              if (epDriver) {
                AQH_MSG_REQUEST *rq;
  
                DBG_ERROR(NULL, "Creating SETDATA request for driver endpoint (%s)", AQH_Endpoint_GetServiceName(epDriver));
                rq=_mkRequest_SetData(o, epSrc, msgId, epDriver, systemValue, valueData);
                AqHomeDataServer_AddRequestToTree(o, rq);

                _storeDatapoint(xo, systemValue, valueData);
              }
              else {
                DBG_ERROR(NULL, "Driver \"%s\" not available", driverName);
                AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
              }
            }
            else {
              DBG_ERROR(NULL, "No driver name");
              AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
            }
          } /* if actor */
          else {
            DBG_ERROR(NULL, "Value \"%s\" is not an actor", valueName);
            AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
          }
        }
        else {
          DBG_ERROR(NULL, "Unknown value \"%s\"", valueName);
          AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_NOTFOUND);
        }
        AQH_Value_free(recvdValue);
      } /* if recvdValue */
      else {
        DBG_ERROR(NULL, "No value in message");
        AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
      }
    }
  }
}



void _storeDatapoint(AQHOME_SERVER *xo, const AQH_VALUE *v, double valueData)
{
  uint64_t timestamp;
  int rv;
  
  timestamp=(uint64_t) time(NULL);
  rv=AQH_Storage_AddDatapoint(xo->storage, AQH_Value_GetId(v), timestamp, valueData);
  if (rv<0) {
    DBG_INFO(NULL, "here (%d)", rv);
  }
  else {
    DBG_INFO(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v));
  }
}



/* ------------------------------------------------------------------------------------------------
 * IPC Request SETDATA
 */

AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
                                    AQH_OBJECT *epSrc, uint32_t requestMsgId,
                                    AQH_OBJECT *epDriver,
                                    const AQH_VALUE *v, double data)
{
  AQH_MSG_REQUEST *rq;
  AQH_MSG_REQUEST *subRq;

  rq=AQH_MsgRequest_new();
  AQH_MsgRequest_SetPrivateData(rq, o);
  AQH_MsgRequest_SetEndpoint(rq, epSrc);
  AQH_MsgRequest_SetRequestMsgId(rq, requestMsgId);
  AQH_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished);
  AQH_MsgRequest_SetAbortFn(rq, _rqAbort);
  AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS);

  subRq=_mkSubRequest_SetData(o, epDriver, v, data);
  AQH_MsgRequest_Tree2_AddChild(rq, subRq);

  return rq;
}



void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason)
{
  AQH_OBJECT *ep;
  uint32_t refMsgId;
  int result;

  DBG_DEBUG(NULL, "SubRequest finished (reason: %d)", reason);
  refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
  ep=AQH_MsgRequest_GetEndpoint(rq);
  result=AQH_MsgRequest_GetResult(subRq);

  if (reason==AQH_MSG_REQUEST_REASON_ABORTED)
    AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
  else
    AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, result);

  AQH_MsgRequest_SetResult(rq, result);
  AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
}



void _rqAbort(AQH_MSG_REQUEST *rq, int reason)
{
  AQH_OBJECT *ep;
  uint32_t refMsgId;
  AQH_MSG_REQUEST *rqParent;

  DBG_INFO(NULL, "Aborting request");
  refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
  ep=AQH_MsgRequest_GetEndpoint(rq);
  AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
  AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);

  rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
  if (rqParent)
    AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}



/* ------------------------------------------------------------------------------------------------
 * Driver Request SETDATA
 */


AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, double data)
{
  AQH_MSG_REQUEST *rq;
  uint16_t msgId;
  AQH_MESSAGE *driverMsg;

  rq=AQH_MsgRequest_new();
  AQH_MsgRequest_SetPrivateData(rq, o);
  AQH_MsgRequest_SetEndpoint(rq, epDriver);

  AQH_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse);
  AQH_MsgRequest_SetAbortFn(rq, _subRqAbort);

  msgId=AQH_Endpoint_GetNextMessageId(epDriver);
  AQH_MsgRequest_SetRequestMsgId(rq, msgId);
  AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);

  driverMsg=AQH_IpcdMessageSetData_new(AQH_MSGTYPE_IPC_DATA_SETDATA, msgId, 0, v, data);
  AQH_Endpoint_AddMsgOut(epDriver, driverMsg);

  return rq;
}



int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg)
{
  DBG_DEBUG(NULL, "Checking message from driver");
  if (AQH_IpcMessage_GetCode(msg)==AQH_MSGTYPE_IPC_DATA_RESULT) {
    GWEN_TAG16_LIST *tagList;

    tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0);
    if (tagList) {
      uint32_t result;
      AQH_MSG_REQUEST *rqParent;

      result=AQH_IpcMessageResult_GetResult(tagList);
      DBG_INFO(NULL, "Received result for request: %d", result);
      AQH_MsgRequest_SetResult(rq, result);
      AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
      rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
      if (rqParent)
        AQH_MsgRequest_SubRequestFinished(rqParent, rq, AQH_MSG_REQUEST_REASON_DONE);

      GWEN_Tag16_List_free(tagList);
      return AQH_MSG_REQUEST_RESULT_HANDLED;
    }
    else {
      DBG_ERROR(NULL, "Bad message %d (no TAG16 data)", AQH_IpcMessage_GetCode(msg));
    }
  }
  else {
    DBG_ERROR(NULL, "Unexpected response message %d", AQH_IpcMessage_GetCode(msg));
  }

  return AQH_MSG_REQUEST_RESULT_NOT_HANDLED;
}



void _subRqAbort(AQH_MSG_REQUEST *rq, int reason)
{
  AQH_MSG_REQUEST *rqParent;

  DBG_INFO(NULL, "Aborting request");

  AQH_MsgRequest_SetResult(rq, AQH_MSGDATA_RESULT_ERROR_GENERIC);
  AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);

  rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
  if (rqParent)
    AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}


