/****************************************************************************
 * 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 "./loop_ipc.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./db.h"

#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/msg/msg_ping.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/nodes/ipc_nodes.h"
#include "aqhome/ipc/nodes/msg_ipc_forward.h"
#include "aqhome/ipc/nodes/msg_ipc_value.h"
#include "aqhome/ipc/nodes/msg_ipc_ping.h"
#include "aqhome/ipc/nodes/msg_ipc_setaccmsggrps.h"
#include "aqhome/ipc/nodes/msg_ipc_getdevices_rsp.h"
#include "aqhome/ipc/msg_ipc_result.h"

#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>



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

#define I18N(msg) msg
#define I18S(msg) msg



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

static void _handleIpcEndpoint(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep);
static void _handleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
void _handleIpcMsgPing(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
void _handleIpcMsgSetAccMsgGrps(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
void _handleIpcMsgForward(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
void _handleIpcMsgGetDevicesReq(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);



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


void AqHomed_ReadAndHandleIpcMessages(AQHOMED *aqh)
{
  if (aqh->ipcdEndpoint) {
    GWEN_MSG_ENDPOINT *ep;

    ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
    while(ep) {
      _handleIpcEndpoint(aqh, ep);
      ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
    }
  }
}



void _handleIpcEndpoint(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep)
{
  GWEN_MSG *msg;

  while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(ep)) ) {
    _handleIpcMsg(aqh, ep, msg);
    GWEN_Msg_free(msg);
  }
}



void _handleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
  uint16_t code;

  /* exec IPC message */
  code=GWEN_IpcMsg_GetCode(msg);
  DBG_ERROR(AQH_LOGDOMAIN, "Received IPC packet");
  switch(code) {
  case AQH_MSGTYPE_IPC_NODES_PING:           _handleIpcMsgPing(aqh, ep, msg); break;
  case AQH_MSGTYPE_IPC_NODES_SETACCMSGGRPS:  _handleIpcMsgSetAccMsgGrps(aqh, ep, msg); break;
  case AQH_MSGTYPE_IPC_NODES_FORWARD:        _handleIpcMsgForward(aqh, ep, msg); break;
  case AQH_MSGTYPE_IPC_NODES_GETDEVICES_REQ: _handleIpcMsgGetDevicesReq(aqh, ep, msg); break;
  default:                                   break;
  }
}



void _handleIpcMsgPing(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
  if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
    GWEN_MSG *msgOut;

    DBG_ERROR(AQH_LOGDOMAIN, "Received IPC PING message");
    msgOut=AQH_PingMsg_new(aqh->nodeAddress, AQH_PingIpcMsg_GetDestAddr(msg), AQH_MSG_TYPE_PING);
    GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut);
  }
}



void _handleIpcMsgSetAccMsgGrps(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
  uint32_t groups;

  DBG_ERROR(AQH_LOGDOMAIN, "Received IPC SET_ACCEPTED_MSG_GROUPS message");
  groups=AQH_SetAcceptedMsgGroupsIpcMsg_GetMsgGroups(msg);
  AQH_IpcEndpoint_SetAcceptedMsgGroups(ep, groups);
  // TODO: send response?
}



void _handleIpcMsgForward(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
  if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
    GWEN_MSG *msgOut;

    DBG_ERROR(AQH_LOGDOMAIN, "Received IPC FORWARD message");
    msgOut=AQH_ForwardIpcMsg_GetCopyOfNodeMsg(msg);
    if (msgOut)
      GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut);
  }
}



void _handleIpcMsgGetDevicesReq(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
  AQH_NODE_INFO_LIST *nodeInfoList;

  DBG_ERROR(AQH_LOGDOMAIN, "Received IPC GetDevicesRequest message");
  nodeInfoList=AQH_NodeDb_GetAllNodeInfos(aqh->nodeDb);
  if (nodeInfoList && AQH_NodeInfo_List_GetCount(nodeInfoList)) {
    const AQH_NODE_INFO *ni;

    ni=AQH_NodeInfo_List_First(nodeInfoList);
    while(ni) {
      const AQH_NODE_INFO *niNext;
      GWEN_MSG *msgOut;

      niNext=AQH_NodeInfo_List_Next(ni);
      DBG_INFO(AQH_LOGDOMAIN, "Sending response for node %02x (%08x)", AQH_NodeInfo_GetBusAddress(ni), AQH_NodeInfo_GetUid(ni));
      msgOut=AQH_GetDevicesResponseIpcMsg_new(AQH_MSGTYPE_IPC_NODES_GETDEVICES_RSP, niNext?0:AQH_MSGIPC_GETDEVICES_RSP_FLAGS_LAST, ni);
      GWEN_MsgEndpoint_AddSendMessage(ep, msgOut);
      ni=niNext;
    }
  }
  else {
    GWEN_MSG *msgOut;

    DBG_INFO(AQH_LOGDOMAIN, "No nodes");
    msgOut=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_NODES_RESULT, AQH_MSG_IPC_ERROR_NODATA);
    GWEN_MsgEndpoint_AddSendMessage(ep, msgOut);
  }
}






