/*******************************************************************************
    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@eurecom.fr

  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06410 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/
/*!
*******************************************************************************

\file     sensing_rrm_interface.c

\brief    Sensing unit sockets

\author   IACOBELLI Lorenzo, KNOPP Raymond

\date     01/04/2010


\par     Historique:


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


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/un.h>

#include "graph_sock.h"

//! \brief  Taille maximale de la charge utile
#define SIZE_MAX_PAYLOAD  2048
//! \brief PID de l'espace utilisateur (Netlink mode)
//#define PID_USERSPACE   0xAA


#define msg printf
#define RRM_FREE free

#ifndef SENSING_RRM_FIFOS_XFACE
/*!
*******************************************************************************
\brief  This function opens a unix socket for the rrm communication
\return  The return value is a socket handle
*/
int open_socket(
  sock_rrm_t *s   , ///< socket descriptor
  char *path_local,   ///< local socket path if unix socket
  char *path_dest ,   ///< host  Socket path if unix socket
  int rrm_inst        ///< instance of the rrm entity
)
{
  /* Unix socket */
  int   socket_fd ;
  int   len ;

  if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
    perror("unix socket");
    return -1 ;
  }

  memset(&(s->un_local_addr), 0, sizeof(struct  sockaddr_un));
  s->un_local_addr.sun_family = AF_UNIX;
  sprintf(s->un_local_addr.sun_path,"%s%d", path_local, rrm_inst );
  unlink(s->un_local_addr.sun_path);
  msg("local %s\n",s->un_local_addr.sun_path);
  len = strlen((s->un_local_addr).sun_path) + sizeof((s->un_local_addr).sun_family);

  if (bind(socket_fd, (struct sockaddr *)&(s->un_local_addr), len) == -1) {
    perror("bind");
    return -1 ;
  }

  memset(&(s->un_dest_addr), 0, sizeof(struct   sockaddr_un));
  s->un_dest_addr.sun_family = AF_UNIX;
  sprintf(s->un_dest_addr.sun_path,"%s%d", path_dest, rrm_inst );
  msg("Dest %s\n",s->un_dest_addr.sun_path);


  s->s = socket_fd ;
  return socket_fd ;
}
/*!
*******************************************************************************
\brief  This function closes a RRM socket
\return none
*/
void close_socket(
  sock_rrm_t *sock  ///< the socket handle
)
{
  shutdown(sock->s, SHUT_RDWR);
  close(sock->s);
}

/*!
*******************************************************************************
\brief  This function send a buffer message to the unix socket
\return if OK then "0" is returned else "-1"
*/
char BUFF[2048];
int send_msg_sock(
  sock_rrm_t *s   ,///< socket descriptor
  msg_t *smsg       ///< the message to send
)
{
  /* Unix socket */
  int         ret   = 0 ;
  //  char        *buf    = NULL;
  struct  msghdr    msghd ;
  struct  iovec     iov;
  int         taille  = sizeof(msg_head_t)  ;

  if ( smsg == NULL )
    return -1 ;

  if ( smsg->data != NULL )
    taille += smsg->head.size ;

  //buf = RRM_MALLOC(char, taille);
  //if (buf ==NULL)
  //return -1 ;

  memcpy( BUFF , &(smsg->head) , sizeof(msg_head_t) ) ;
  memcpy( BUFF+sizeof(msg_head_t), smsg->data, smsg->head.size ) ;

  iov.iov_base    = (void *)BUFF;
  iov.iov_len     = taille ;

  msghd.msg_name        = (void *)&(s->un_dest_addr);
  msghd.msg_namelen     = sizeof(s->un_dest_addr);
  msghd.msg_iov         = &iov;
  msghd.msg_iovlen      = 1;
  msghd.msg_control     = NULL ;
  msghd.msg_controllen  =   0 ;

  if ( sendmsg(s->s, &msghd, 0) < 0 ) {
    ret = -1;
    msg("socket %d, dest %s\n",s->s,s->un_dest_addr.sun_path);
    perror("sendmsg:unix socket");
  }

  //RRM_FREE(buf) ;
  //RRM_FREE(msg->data) ;
  //RRM_FREE(msg) ;

  return ret ;
}

/*!
*******************************************************************************
\brief  This function read a buffer from a unix socket
\return the function returns a message pointer. If the pointeur is NULL, a error
is happened.
*/
char *recv_msg(
  sock_rrm_t *s  ///< socket descriptor
)
{
  /* Unix socket */
  char        *buf = NULL;
  char        *smsg = NULL;
  struct  msghdr    msghd ;
  struct  iovec     iov;
  int         size_msg ;
  msg_head_t      *head  ;
  int         ret ;

  int taille =  SIZE_MAX_PAYLOAD ;

  buf         = (char *)calloc(taille,sizeof(char));

  if ( buf == NULL ) {
    fprintf(stderr,"Buf null\n");
    return NULL ;
  }

  iov.iov_base      = (void *)buf;
  iov.iov_len       = taille ;
  msghd.msg_name      = (void *)&(s->un_dest_addr);
  msghd.msg_namelen   = sizeof(s->un_dest_addr);
  msghd.msg_iov       = &iov;
  msghd.msg_iovlen    = 1;
  msghd.msg_control   = NULL ;
  msghd.msg_controllen= 0 ;

  ret = recvmsg(s->s, &msghd , 0 ) ;

  if ( ret <= 0  ) {
    // non-blocking socket
    // perror("PB recvmsg_un");
    fprintf(stderr,"PB recvmsg_un\n");
    RRM_FREE(buf);
    return NULL ;
  }

  if (msghd.msg_flags != 0 ) {
    fprintf(stderr,"error recvmsg_un: 0x%02x\n", msghd.msg_flags) ;
    RRM_FREE(buf);
    return NULL ;
  }

  head    = (msg_head_t *) buf  ;
  size_msg  = sizeof(msg_head_t) + head->size ;

  smsg    = (char *)calloc(size_msg,sizeof(char)) ;

  if ( smsg != NULL )
    memcpy( smsg , buf , size_msg ) ;

  RRM_FREE( buf ) ;

  return smsg ;
}

#else  //XFACE

int send_msg_fifo(int *s, msg_t *fmsg)
{
  int   ret   = 0, ret1;
  int  taille = sizeof(msg_head_t)  ;
  msg("write on fifos %d, msg %p\n",*s,fmsg);

  if ( fmsg == NULL )
    return -1 ;

  // envoi le header


  ret1 = rtf_put (*s,(char*) &(fmsg->head) , taille);

  if(ret1 <0) {
    msg("rtf_put H ERR %d\n",ret1);
    rtf_reset(*s);
    return ret1;
  }

  ret=ret1;

  // envoi les datas si elles sont definis
  if ( fmsg->data != NULL ) {
    ret1 += rtf_put (*s,(char*) fmsg->data, fmsg->head.size);

    if(ret1 <0) {
      msg("rtf_put D ERR %d\n",ret1);
      rtf_reset(*s);
      return ret1;
    }
  }

  ret+=ret1;
  return ret;
}

#endif //XFACE

int send_msg(void *s, msg_t *smsg)
{
#ifdef USER_MODE
  send_msg_sock((sock_rrm_t *)s, smsg);
#else
  send_msg_fifo((int *)s,smsg);
#endif

}
