/*----------------------------------------------------------------------------*
 *                                                                            *
 *            M I N I M A L I S T I C     U L P     E N T I T Y               *
 *                                                                            *
 *                    Copyright (C) 2010 Amit Chawre.                         *
 *                                                                            *
 *----------------------------------------------------------------------------*/

/**
 * @file NwMiniUlpEntity.c
 * @brief This file contains example of a minimalistic ULP entity.
*/

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "NwEvt.h"
#include "NwGtpv2c.h"
#include "NwGtpv2cIe.h"
#include "NwGtpv2cMsg.h"
#include "NwGtpv2cMsgParser.h"
#include "NwMiniLogMgrEntity.h"
#include "NwMiniUlpEntity.h"

#ifndef NW_ASSERT
#define NW_ASSERT assert
#endif

#ifdef __cplusplus
extern "C" {
#endif

static
NwCharT* gLogLevelStr[] = {"EMER", "ALER", "CRIT",  "ERRO", "WARN", "NOTI", "INFO", "DEBG"};

static NwRcT
nwGtpv2cCreateSessionRequestIeIndication(NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T* ieValue, void* arg)
{
  NW_LOG(NW_LOG_LEVEL_DEBG, "Received IE Parse Indication for of type %u, length %u, instance %u!", ieType, ieLength, ieInstance);
  return NW_OK;
}

/*---------------------------------------------------------------------------
 * Public Functions
 *--------------------------------------------------------------------------*/

NwRcT
nwGtpv2cUlpInit(NwGtpv2cNodeUlpT* thiz, NwGtpv2cStackHandleT hGtpv2cStack, char* peerIpStr)
{
  NwRcT rc;
  thiz->hGtpv2cStack = hGtpv2cStack;
  strcpy(thiz->peerIpStr, peerIpStr);
  return NW_OK;
}

NwRcT
nwGtpv2cUlpDestroy(NwGtpv2cNodeUlpT* thiz)
{
  NW_ASSERT(thiz);
  memset(thiz, 0, sizeof(NwGtpv2cNodeUlpT));
  return NW_OK;
}

NwRcT
nwGtpv2cUlpSenEchoRequestToPeer(NwGtpv2cNodeUlpT* thiz, NwU32T peerIp)
{
  NwRcT rc;
  NwGtpv2cUlpApiT           ulpReq;
  /*
   *  Send Message Request to Gtpv2c Stack Instance
   */

  ulpReq.apiType = NW_GTPV2C_ULP_API_INITIAL_REQ;

  ulpReq.apiInfo.initialReqInfo.hTunnel         = 0;
  ulpReq.apiInfo.initialReqInfo.hUlpTrxn        = (NwGtpv2cUlpTrxnHandleT)thiz;
  ulpReq.apiInfo.initialReqInfo.hUlpTunnel      = (NwGtpv2cUlpTrxnHandleT)thiz;
  ulpReq.apiInfo.initialReqInfo.teidLocal       = (NwGtpv2cUlpTrxnHandleT)0;
  ulpReq.apiInfo.initialReqInfo.peerIp          = htonl(peerIp);

  rc = nwGtpv2cMsgNew( thiz->hGtpv2cStack,
                       NW_FALSE,
                       NW_GTP_ECHO_REQ,
                       0,
                       0,
                       &(ulpReq.hMsg));

  NW_ASSERT(NW_OK == rc);

  rc = nwGtpv2cMsgAddIeTV1((ulpReq.hMsg), NW_GTPV2C_IE_RECOVERY, 0, thiz->restartCounter);
  NW_ASSERT(NW_OK == rc);

  rc = nwGtpv2cProcessUlpReq(thiz->hGtpv2cStack, &ulpReq);
  NW_ASSERT(NW_OK == rc);

  return NW_OK;
}

NwRcT
nwGtpv2cUlpCreateSessionRequestToPeer(NwGtpv2cNodeUlpT* thiz)
{
  NwRcT rc;
  NwGtpv2cUlpApiT           ulpReq;

  /*
   *  Send Message Request to Gtpv2c Stack Instance
   */

  ulpReq.apiType = (NW_GTPV2C_ULP_API_INITIAL_REQ | NW_GTPV2C_ULP_API_FLAG_CREATE_LOCAL_TUNNEL);

  ulpReq.apiInfo.initialReqInfo.hUlpTrxn        = (NwGtpv2cUlpTrxnHandleT)thiz;
  ulpReq.apiInfo.initialReqInfo.teidLocal       = (NwGtpv2cUlpTrxnHandleT)thiz;
  ulpReq.apiInfo.initialReqInfo.peerIp          = ntohl(inet_addr(thiz->peerIpStr));

  rc = nwGtpv2cMsgNew( thiz->hGtpv2cStack,
                       NW_TRUE,
                       NW_GTP_CREATE_SESSION_REQ,
                       0,
                       0,
                       &(ulpReq.hMsg));

  NW_ASSERT(NW_OK == rc);

  rc = nwGtpv2cMsgAddIeTV1((ulpReq.hMsg), NW_GTPV2C_IE_RECOVERY, 0, thiz->restartCounter);
  NW_ASSERT(NW_OK == rc);

  rc = nwGtpv2cProcessUlpReq(thiz->hGtpv2cStack, &ulpReq);
  NW_ASSERT(NW_OK == rc);

  return NW_OK;
}

NwRcT
nwGtpv2cUlpProcessStackReqCallback (NwGtpv2cUlpHandleT hUlp,
                                    NwGtpv2cUlpApiT *pUlpApi)
{
  NwRcT rc;
  NwGtpv2cNodeUlpT* thiz;
  NW_ASSERT(pUlpApi != NULL);

  thiz = (NwGtpv2cNodeUlpT*) hUlp;

  switch(pUlpApi->apiType) {

  case NW_GTPV2C_ULP_API_INITIAL_REQ_IND: {
    NwGtpv2cUlpApiT           ulpReq;
    NwGtpv2cMsgParserT        *pMsgParser;
    NW_LOG(NW_LOG_LEVEL_DEBG, "Received NW_GTPV2C_ULP_API_INITIAL_REQ_IND from gtpv2c stack! %X:%u", pUlpApi->apiInfo.initialReqIndInfo.peerIp, pUlpApi->apiInfo.initialReqIndInfo.peerPort);

    rc = nwGtpv2cMsgParserNew(thiz->hGtpv2cStack, NW_GTP_CREATE_SESSION_REQ, nwGtpv2cCreateSessionRequestIeIndication, NULL, &pMsgParser);
    NW_ASSERT(NW_OK == rc);

    if(pUlpApi->apiInfo.initialReqIndInfo.msgType == NW_GTP_CREATE_SESSION_REQ) {

      struct {
        NwU8T causeValue;
        NwU8T spare:5;
        NwU8T pce:1;
        NwU8T bce:1;
        NwU8T cs:1;
      } cause;


      rc = nwGtpv2cMsgParserAddIe(pMsgParser, NW_GTPV2C_IE_RECOVERY, NW_GTPV2C_IE_INSTANCE_ZERO, NW_GTPV2C_IE_PRESENCE_MANDATORY, nwGtpv2cCreateSessionRequestIeIndication, NULL);
      NW_ASSERT(NW_OK == rc);

      NwU8T offendingIeType, OffendingIeLength;
      rc = nwGtpv2cMsgParserRun(pMsgParser, (pUlpApi->hMsg), &offendingIeType, &OffendingIeLength);

      if( rc != NW_OK ) {
        switch(rc) {
        case NW_GTPV2C_MANDATORY_IE_MISSING:
          NW_LOG(NW_LOG_LEVEL_ERRO, "Mandatory IE type '%u' of instance '%u' missing!", offendingIeType, OffendingIeLength);
          cause.causeValue = NW_GTPV2C_CAUSE_MANDATORY_IE_MISSING;
          break;

        default:
          NW_LOG(NW_LOG_LEVEL_ERRO, "Unknown message parse error!");
          cause.causeValue = 0;
          break;
        }

      } else {
        cause.causeValue = NW_GTPV2C_CAUSE_REQUEST_ACCEPTED;
      }

      /*
       * Send Message Request to Gtpv2c Stack Instance
       */
      ulpReq.apiType = NW_GTPV2C_ULP_API_TRIGGERED_RSP;

      ulpReq.apiInfo.triggeredRspInfo.hTrxn         = pUlpApi->apiInfo.initialReqIndInfo.hTrxn;

      rc = nwGtpv2cMsgNew( thiz->hGtpv2cStack,
                           NW_TRUE,
                           NW_GTP_CREATE_SESSION_RSP,
                           0,
                           nwGtpv2cMsgGetSeqNumber(pUlpApi->hMsg),
                           &(ulpReq.hMsg));

      rc = nwGtpv2cMsgAddIe((ulpReq.hMsg), NW_GTPV2C_IE_CAUSE, 2, 0, (NwU8T*)&cause);
      NW_ASSERT(NW_OK == rc);

      NW_LOG(NW_LOG_LEVEL_NOTI, "Received NW_GTP_CREATE_SESSION_REQ, Sending NW_GTP_CREATE_SESSION_RSP!");
      rc = nwGtpv2cProcessUlpReq(thiz->hGtpv2cStack, &ulpReq);
      NW_ASSERT(NW_OK == rc);
    }

    rc = nwGtpv2cMsgParserDelete(thiz->hGtpv2cStack, pMsgParser);
    NW_ASSERT(NW_OK == rc);

    rc = nwGtpv2cMsgDelete(thiz->hGtpv2cStack, (pUlpApi->hMsg));
    NW_ASSERT(NW_OK == rc);

  }
  break;

  case NW_GTPV2C_ULP_API_TRIGGERED_RSP_IND: {
    NwGtpv2cUlpApiT           ulpReq;
    NwGtpv2cMsgParserT        *pMsgParser;
    NW_LOG(NW_LOG_LEVEL_DEBG, "Received NW_GTPV2C_ULP_API_TRIGGERED_RSP_IND from gtpv2c stack!", pUlpApi->apiInfo.triggeredRspIndInfo, pUlpApi->apiInfo.triggeredRspIndInfo);

    rc = nwGtpv2cMsgParserNew(thiz->hGtpv2cStack, NW_GTP_CREATE_SESSION_REQ, nwGtpv2cCreateSessionRequestIeIndication, NULL, &pMsgParser);
    NW_ASSERT(NW_OK == rc);

    if(pUlpApi->apiInfo.triggeredRspIndInfo.msgType == NW_GTP_CREATE_SESSION_RSP) {
      rc = nwGtpv2cMsgParserAddIe(pMsgParser, NW_GTPV2C_IE_CAUSE, NW_GTPV2C_IE_INSTANCE_ZERO, NW_GTPV2C_IE_PRESENCE_MANDATORY, nwGtpv2cCreateSessionRequestIeIndication, NULL);
      NW_ASSERT(NW_OK == rc);

      NwU8T offendingIeType, OffendingIeLength;
      rc = nwGtpv2cMsgParserRun(pMsgParser, (pUlpApi->hMsg), &offendingIeType, &OffendingIeLength);

      if( rc != NW_OK ) {
        switch(rc) {
        case NW_GTPV2C_MANDATORY_IE_MISSING:
          NW_LOG(NW_LOG_LEVEL_ERRO, "Mandatory IE Missing!");
          break;

        default:
          NW_LOG(NW_LOG_LEVEL_ERRO, "Unknown message parse error!");
          break;

        }

      }

      /*
       * Send Message Request to Gtpv2c Stack Instance
       */
      NW_LOG(NW_LOG_LEVEL_NOTI, "Received NW_GTP_CREATE_SESSION_RSP, Sending NW_GTP_CREATE_SESSION_REQ!");
      nwGtpv2cUlpCreateSessionRequestToPeer(thiz);
    }

    rc = nwGtpv2cMsgParserDelete(thiz->hGtpv2cStack, pMsgParser);
    NW_ASSERT(NW_OK == rc);

    rc = nwGtpv2cMsgDelete(thiz->hGtpv2cStack, (pUlpApi->hMsg));
    NW_ASSERT(NW_OK == rc);

  }
  break;

  case NW_GTPV2C_ULP_API_RSP_FAILURE_IND: {
    NW_LOG(NW_LOG_LEVEL_ERRO, "Received NW_GTPV2C_ULP_API_RSP_FAILURE from gtpv2c stack for transaction '%x'!", thiz);
  }
  break;

  default:
    NW_LOG(NW_LOG_LEVEL_WARN, "Received undefined UlpApi from gtpv2c stack!");
  }

  return NW_OK;
}

#ifdef __cplusplus
}
#endif

