/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr

  Address      : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.

 *******************************************************************************/

/*! \file proto_agent_common.c
 * \brief common primitives for all agents 
 * \author Navid Nikaein and Xenofon Foukas
 * \date 2016
 * \version 0.1
 */

#include<stdio.h>
#include <dlfcn.h>
#include <time.h>

#include "proto_agent_common.h"
#include "PHY/extern.h"
#include "log.h"

#include "RRC/LITE/extern.h"
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
#include "rrc_eNB_UE_context.h"

void * enb[NUM_MAX_ENB];
void * enb_ue[NUM_MAX_ENB];
void * enb_rrc[NUM_MAX_ENB];

/*
 * message primitives
 */

int proto_agent_serialize_message(Protocol__FlexsplitMessage *msg, void **buf, int *size) {

  *size = protocol__flexsplit_message__get_packed_size(msg);
  
  *buf = malloc(*size);
  if (buf == NULL)
    goto error;
  
  protocol__flexsplit_message__pack(msg, *buf);
  
  return 0;
  
 error:
  LOG_E(PROTO_AGENT, "an error occured\n"); 
  return -1;
}

/* We assume that the buffer size is equal to the message size.
   Should be chekced durint Tx/Rx */
int proto_agent_deserialize_message(void *data, int size, Protocol__FlexsplitMessage **msg) {
  *msg = protocol__flexsplit_message__unpack(NULL, size, data);
  if (*msg == NULL)
    goto error;  

  return 0;
  
 error:
  LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}

int fsp_create_header(xid_t xid, Protocol__FspType type,  Protocol__FspHeader **header) {
  
  *header = malloc(sizeof(Protocol__FspHeader));
  if(*header == NULL)
    goto error;
  
  protocol__fsp_header__init(*header);
  LOG_D(PROTO_AGENT, "Initialized the PROTOBUF message header\n");
  (*header)->version = FLEXSPLIT_VERSION;
  LOG_D(PROTO_AGENT, "Set the vversion to FLEXSPLIT_VERSION\n");

  (*header)->has_version = 1; 
  (*header)->type = type;
  (*header)->has_type = 1;
  (*header)->xid = xid;
  (*header)->has_xid = 1;
  return 0;

 error:
  LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}

int proto_agent_hello(mid_t mod_id, const void *params, Protocol__FlexsplitMessage **msg) {
 
  Protocol__FspHeader *header;
  /*TODO: Need to set random xid or xid from received hello message*/
  xid_t xid = 1;
  if (fsp_create_header(xid, PROTOCOL__FSP_TYPE__FSPT_HELLO, &header) != 0)
    goto error;

  Protocol__FspHello *hello_msg = NULL;
  hello_msg = malloc(sizeof(Protocol__FspHello));
  if(hello_msg == NULL)
    goto error;
  protocol__fsp_hello__init(hello_msg);
  hello_msg->header = header;

  *msg = malloc(sizeof(Protocol__FlexsplitMessage));
  if(*msg == NULL)
    goto error;
  
  protocol__flexsplit_message__init(*msg);
  (*msg)->msg_case = PROTOCOL__FLEXSPLIT_MESSAGE__MSG_HELLO_MSG;
  (*msg)->msg_dir = PROTOCOL__FLEXSPLIT_DIRECTION__SUCCESSFUL_OUTCOME;
  (*msg)->has_msg_dir = 1;
  (*msg)->hello_msg = hello_msg;
  return 0;
  
 error:
  if(header != NULL)
    free(header);
  if(hello_msg != NULL)
    free(hello_msg);
  if(*msg != NULL)
    free(*msg);
  LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}


int proto_agent_destroy_hello(Protocol__FlexsplitMessage *msg) {
  
  if(msg->msg_case != PROTOCOL__FLEXSPLIT_MESSAGE__MSG_HELLO_MSG)
    goto error;
  
  free(msg->hello_msg->header);
  free(msg->hello_msg);
  free(msg);
  return 0;

 error:
  LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}

int proto_agent_echo_request(mid_t mod_id, const void* params, Protocol__FlexsplitMessage **msg) {
  Protocol__FspHeader *header;

  xid_t xid = 1;
  if (fsp_create_header(xid, PROTOCOL__FSP_TYPE__FSPT_ECHO_REQUEST, &header) != 0)
    goto error;
  LOG_I(PROTO_AGENT,"Created the fsp message header\n");

  Protocol__FspEchoRequest *echo_request_msg = NULL;
  echo_request_msg = malloc(sizeof(Protocol__FspEchoRequest));
  if(echo_request_msg == NULL)
    goto error;

  protocol__fsp_echo_request__init(echo_request_msg);
  echo_request_msg->header = header;

  *msg = malloc(sizeof(Protocol__FlexsplitMessage));
  if(*msg == NULL)
    goto error;
  protocol__flexsplit_message__init(*msg);

  LOG_I(PROTO_AGENT,"setting the message direction to %d\n", PROTOCOL__FLEXSPLIT_MESSAGE__MSG_ECHO_REQUEST_MSG);
  (*msg)->msg_case = PROTOCOL__FLEXSPLIT_MESSAGE__MSG_ECHO_REQUEST_MSG;
  (*msg)->msg_dir = PROTOCOL__FLEXSPLIT_DIRECTION__INITIATING_MESSAGE;
  (*msg)->has_msg_dir = 1;
  (*msg)->echo_request_msg = echo_request_msg;
  return 0;

 error:
  if(header != NULL)
    free(header);
  if(echo_request_msg != NULL)
    free(echo_request_msg);
  if(*msg != NULL)
    free(*msg);
  return -1;
}

int proto_agent_destroy_echo_request(Protocol__FlexsplitMessage *msg) {
  if(msg->msg_case != PROTOCOL__FLEXSPLIT_MESSAGE__MSG_ECHO_REQUEST_MSG)
    goto error;
  
  free(msg->echo_request_msg->header);
  free(msg->echo_request_msg);
  free(msg);
  return 0;
  
 error:
  LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}

int proto_agent_echo_reply(mid_t mod_id, const void *params, Protocol__FlexsplitMessage **msg) {
  
  xid_t xid;
  Protocol__FlexsplitMessage *input = (Protocol__FlexsplitMessage *)params;
  Protocol__FspEchoRequest *echo_req = input->echo_request_msg;
  xid = (echo_req->header)->xid;

  Protocol__FspHeader *header;
  if (fsp_create_header(xid, PROTOCOL__FSP_TYPE__FSPT_ECHO_REPLY, &header) != 0)
    goto error;

  Protocol__FspEchoReply *echo_reply_msg;
  echo_reply_msg = malloc(sizeof(Protocol__FspEchoReply));
  if(echo_reply_msg == NULL)
    goto error;
  protocol__fsp_echo_reply__init(echo_reply_msg);
  echo_reply_msg->header = header;

  *msg = malloc(sizeof(Protocol__FlexsplitMessage));
  if(*msg == NULL)
    goto error;
  protocol__flexsplit_message__init(*msg);
  (*msg)->msg_case = PROTOCOL__FLEXSPLIT_MESSAGE__MSG_ECHO_REPLY_MSG;
  (*msg)->msg_dir = PROTOCOL__FLEXSPLIT_DIRECTION__SUCCESSFUL_OUTCOME;
  (*msg)->has_msg_dir = 1;
  (*msg)->echo_reply_msg = echo_reply_msg;
  return 0;

 error:
  if(header != NULL)
    free(header);
  if(echo_reply_msg != NULL)
    free(echo_reply_msg);
  if(*msg != NULL)
    free(*msg);
  LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}

int proto_agent_destroy_echo_reply(Protocol__FlexsplitMessage *msg) {
  if(msg->msg_case != PROTOCOL__FLEXSPLIT_MESSAGE__MSG_ECHO_REPLY_MSG)
    goto error;
  
  free(msg->echo_reply_msg->header);
  free(msg->echo_reply_msg);
  free(msg);
  return 0;
  
 error:
  LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}
/*
// call this function to start a nanosecond-resolution timer
struct timespec timer_start(){
    struct timespec start_time;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_time);
    return start_time;
}

// call this function to end a timer, returning nanoseconds elapsed as a long
long timer_end(struct timespec start_time){
    struct timespec end_time;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_time);
    long diffInNanos = end_time.tv_nsec - start_time.tv_nsec;
    return diffInNanos;
}
*/

/*
 * get generic info from RAN
 */

void set_enb_vars(mid_t mod_id, ran_name_t ran){

  switch (ran){
  case RAN_LTE_OAI :
    enb[mod_id] =  (void *)&eNB_mac_inst[mod_id];
    enb_ue[mod_id] = (void *)&eNB_mac_inst[mod_id].UE_list;
    enb_rrc[mod_id] = (void *)&eNB_rrc_inst[mod_id];
    break;
  default :
    goto error;
  }

  return; 

 error:
  LOG_E(PROTO_AGENT, "unknown RAN name %d\n", ran);
}