/**
*  callback associated with the socket controller for receiving a
   message on a AF_UNIX socket operating of datagram mode

  @param socket_pointer: pointer to the socket context
  @param socketId: reference of the socket--> not used

*/
uint32_t af_unix_recv_generic_cbk(void * socket_pointer,int socketId)
{

  uint8_t                buffer_header[ROZOFS_MAX_HEADER_SIZE];
  af_unix_ctx_generic_t  *sock_p = (af_unix_ctx_generic_t*)socket_pointer;
  int                    bytesRcvd;
  struct sockaddr_un     sockAddr;
  int                    sockAddrLen;
  com_recv_template_t    *recv_p;
  uint16_t               recv_credit;
  int                    eintr_count;
  void                  *buf_recv_p;
  int                    full_msg_len;
  uint32_t               payloadLen;
  char                  *sockname_p;
  uint8_t               *payload_p;

  recv_p = &sock_p->recv;
  recv_credit = recv_p->recv_credit_conf;


  sockAddrLen = sizeof(sockAddr);
  /*
  ** Get the header of the message without removing the message from the
  ** socket queue
  */
  eintr_count = 0;
  while(recv_credit != 0)
  {
    bytesRcvd = recvfrom(sock_p->socketRef,
		        buffer_header,
		        recv_p->headerSize,
		        MSG_PEEK,
		        (struct sockaddr *)&sockAddr,
		        ( socklen_t *)&sockAddrLen);
   if (bytesRcvd == -1)
   {
     switch (errno)
     {
       case EAGAIN:
        /*
        ** the socket is empty
        */
        return TRUE;

       case EINTR:
         /*
         ** re-attempt to read the socket
         */
         eintr_count++;
         if (eintr_count < 3) continue;
         /*
         ** here we consider it as a error
         */
         RUC_WARNING(eintr_count);
         sock_p->stats.totalRecvError++;
         return TRUE;

       case EBADF:
       case EFAULT:
       case EINVAL:
       default:
         /*
         ** We might need to double checl if the socket must be killed
         */
         RUC_WARNING(errno);
         sock_p->stats.totalRecvError++;
         return TRUE;
     }
   }
   /*
   ** check we have the right length
   */
   if (bytesRcvd != recv_p->headerSize)
   {
     /*
     ** unable to read the full header, so drop the current message. Notice that for
     ** the AF_UNIX case, the message will be truncated to the max size of buffer_header
     ** Here we will indicate an error an attempt to read the next inflight message if any
     */
     sock_p->stats.totalRecvBadHeader++;
     RUC_WARNING(bytesRcvd);

     recvfrom(sock_p->socketRef,
		      buffer_header,
		      recv_p->headerSize,
		      0,
		      (struct sockaddr *)&sockAddr,
		      ( socklen_t *)&sockAddrLen);
      sock_p->stats.totalRecvBadHeader++;
      recv_credit--;
      continue;
   }
   /*
   ** get the length of the payload of the message
   */
   payloadLen = com_sock_extract_length_from_header_host_format((char*)buffer_header,recv_p->msgLenOffset,recv_p->msgLenSize);
   if (payloadLen == 0)
   {
     /*
     ** the length information is wrong, skip that message and attempt to read the next one
     ** in sequence
     */
     RUC_WARNING(payloadLen);

     recvfrom(sock_p->socketRef,
		      buffer_header,
		      recv_p->headerSize,
		      0,
		      (struct sockaddr *)&sockAddr,
		      ( socklen_t *)&sockAddrLen);
      sock_p->stats.totalRecvBadHeader++;
      recv_credit--;
      continue;
   }
   /*
   ** check if the message does not exceed the max buffer size
   */
   full_msg_len = payloadLen + recv_p->headerSize;
   if (full_msg_len > recv_p->bufSize)
   {
     /*
     ** message exceeds the capacity of the receiver-> drop it
     */
     RUC_WARNING(payloadLen);

     recvfrom(sock_p->socketRef,
		      buffer_header,
		      recv_p->headerSize,
		      0,
		      (struct sockaddr *)&sockAddr,
		      ( socklen_t *)&sockAddrLen);
      sock_p->stats.totalRecvBadLength++;
      recv_credit--;
      continue;
   }
   /*
   ** Ok now call the application for receive buffer allocation
   */
   if (sock_p->userRcvAllocBufCallBack != NULL)
   {
      buf_recv_p = (sock_p->userRcvAllocBufCallBack)(sock_p->userRef,sock_p->index,full_msg_len);
   }
   else
   {
     buf_recv_p = af_unix_alloc_recv_buf();
   }
   if (buf_recv_p == NULL)
   {
     /*
     ** the receiver is out of buffer-> leave the message in the receiver queue and exit
     */
     sock_p->stats.totalRecvOutoFBuf++;
     return TRUE;
   }
   payload_p = (uint8_t*)ruc_buf_getPayload(buf_recv_p);
   /*
   ** OK, we have a receive buffer, so receive the full message and give to the application
   ** but before read it from the socket
   */
  eintr_count = 0;
retry:
   bytesRcvd = recvfrom(sock_p->socketRef,
		       payload_p,
		       full_msg_len,
		       0,
		       (struct sockaddr *)&sockAddr,
		       ( socklen_t *)&sockAddrLen);
    if (bytesRcvd == -1)
    {
      switch (errno)
      {
        case EAGAIN:
         /*
         ** the socket is empty--> that situation MUST not occur:
         */
         ruc_buf_freeBuffer(buf_recv_p);
         return TRUE;
        case EINTR:
          /*
          ** re-attempt to read the socket
          */
          eintr_count++;
          if (eintr_count < 3) goto retry;
          /*
          ** here we consider it as a error
          */
          RUC_WARNING(eintr_count);
          /*
          ** release the receive buffer
          */
          ruc_buf_freeBuffer(buf_recv_p);
          sock_p->stats.totalRecvError++;
          return TRUE;

        case EBADF:
        case EFAULT:
        case EINVAL:
        default:
          /*
          ** We might need to double checl if the socket must be killed
          */
          ruc_buf_freeBuffer(buf_recv_p);
          RUC_WARNING(errno);
          sock_p->stats.totalRecvError++;
          return TRUE;
      }
    }
    /*
    ** check we have the right length
    */
    if (bytesRcvd != full_msg_len)
    {
      /*
      ** that situation must not occur with datagram socket
      */
      RUC_WARNING(errno);
      ruc_buf_freeBuffer(buf_recv_p);
      sock_p->stats.totalRecvError++;
      recv_credit--;
      continue;
    }
    /*
    ** OK, copy the reference of the source in the packet buffer
    */
    sockname_p = ruc_buf_get_usrSrcInfo(buf_recv_p);
    memcpy(sockname_p,&sockAddr.sun_path,sockAddrLen);
    /*
    ** OK, call the receive process of the application
    */
    sock_p->stats.totalRecvBytes += full_msg_len;
    sock_p->stats.totalRecvSuccess++;
    (sock_p->userRcvCallBack)(sock_p->userRef,sock_p->index,buf_recv_p);
    recv_credit--;
  }
  return TRUE;
}
/**
*  callback associated with the socket controller for receiving a
   message on a AF_UNIX socket operating of datagram mode

  @param socket_pointer: pointer to the socket context
  @param socketId: reference of the socket--> not used

*/
uint32_t af_unix_recv_stream_generic_cbk(void * socket_pointer,int socketId)
{

  af_unix_ctx_generic_t  *sock_p = (af_unix_ctx_generic_t*)socket_pointer;
  com_recv_template_t    *recv_p;
  uint16_t               recv_credit;
  void                  *buf_recv_p = NULL;
  int                    full_msg_len;
  uint32_t               payloadLen;
  uint8_t               *payload_p;
  uint32_t               status;
  int                    len_read;
  void *bufref = NULL;
  /*
  ** set the credit, notice that the credit is decremented upon reception
  ** of a full message
  */
  recv_p = &sock_p->recv;
  recv_credit = recv_p->recv_credit_conf;

  while(recv_credit != 0)
  {
    switch (recv_p->state)
    {
      /*
      ** There is no recepition in progress
      */
      case RECV_IDLE:
        recv_p->nbread = 0;
        recv_p->nb2read = recv_p->headerSize;
        recv_p->bufRefCurrent = NULL;
        recv_p->state = RECV_WAIT_HDR;
        break;
      /*
      **_________________________________________________________________
      ** Waiting for the header before allocating the receive buffer
      **_________________________________________________________________
      */
      case RECV_WAIT_HDR:
        /*
        ** attempt to receive the full header to figure out what kind of receive buffer
        ** Must be allocated
        */
        status = af_unix_recv_stream_sock_recv(sock_p,recv_p->buffer_header+recv_p->nbread,
                                               recv_p->nb2read- recv_p->nbread ,0,&len_read);
        switch(status)
        {
          case RUC_OK:
           /*
           ** that's fine : go to the next step to figure out what kind of buffer can be allocated
           */
           sock_p->stats.totalRecvBytes += len_read;
           recv_p->state = RECV_ALLOC_BUF;
           break;

          case RUC_WOULDBLOCK:
           /*
           ** we don't get the full header so no change, wait for the next receiver event
           */
	   sock_p->stats.emptyRecv++;
           return TRUE;

          case RUC_PARTIAL:
          /*
          ** update the count and re-attempt until getting a EAGAIN or a full header
          */
	  sock_p->stats.partialRecv++;
          sock_p->stats.totalRecvBytes += len_read;
          recv_p->nbread += len_read;
          break;

          default:
          case RUC_DISC:
          /*
          ** general disconnection
          */
          af_unix_sock_stream_disconnect_internal(sock_p);
          /*
          ** socket is dead call the user callback
          */
          recv_p->state = RECV_DEAD;
//          warning("af_unix_recv_stream_generic_cbk: %s",strerror(errno));
          (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno);

          return TRUE;
        }
      break;

      /*
      **_________________________________________________________________
      ** allocate a receive buffer according to the length of the message
      **_________________________________________________________________
      */
      case RECV_ALLOC_BUF:
        if (sock_p->userHdrAnalyzerCallBack != NULL)
        {
           /*
           ** The applicaton has provide a callback for parsing its header and to extract the
           ** length of the payload-> Typically, it is mandatory for the case of the RPC since
           ** the field that contains the length has the bit 31 asserted
           */
           payloadLen = (sock_p->userHdrAnalyzerCallBack)((char*)recv_p->buffer_header);         

        }
        else
        {
          payloadLen = com_sock_extract_length_from_header_host_format((char*)recv_p->buffer_header,
                                                                       recv_p->msgLenOffset,
                                                                       recv_p->msgLenSize);
        }
        if (payloadLen == 0)
        {
           /*
           ** general disconnection
           */
           af_unix_sock_stream_disconnect_internal(sock_p);
           recv_p->state = RECV_DEAD;
           sock_p->stats.totalRecvBadHeader++;
           /*
           ** the length is wrong, we have no choice we need to close the connection
           */
           (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno);


           return TRUE;
        }
        /*
        ** check if the message does not exceed the max buffer size
        */
        full_msg_len = payloadLen + recv_p->headerSize;
        if (full_msg_len > recv_p->bufSize)
        {
          /*
           ** general disconnection
           */
           af_unix_sock_stream_disconnect_internal(sock_p);
           recv_p->state = RECV_DEAD;
           sock_p->stats.totalRecvBadHeader++;           /*
           ** the length is wrong, we have no choice we need to close the connection
           */
           (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno);
 
 
           return TRUE;
        }
        /*
        ** Ok now call the application for receive buffer allocation
        */
        if (sock_p->userRcvAllocBufCallBack != NULL)
        {
           buf_recv_p = (sock_p->userRcvAllocBufCallBack)(sock_p->userRef,sock_p->index,full_msg_len);
        }
        else
        {
          buf_recv_p = af_unix_alloc_recv_buf();
        }
        if (buf_recv_p == NULL)
        {
          /*
          ** the receiver is out of buffer-> leave the message in the receiver queue and exit
          */
          sock_p->stats.totalRecvOutoFBuf++;
          recv_p->state = RECV_ALLOC_BUF;
          return TRUE;
        }
        /*
        ** set the payload length in the buffer
        */
        ruc_buf_setPayloadLen(buf_recv_p,(uint32_t)(payloadLen + recv_p->headerSize));
        /*
        ** Ok now start receiving the payload
        */
        recv_p->nbread = recv_p->headerSize;
        recv_p->nb2read = recv_p->headerSize+payloadLen;
        recv_p->bufRefCurrent = buf_recv_p;
        payload_p = (uint8_t*)ruc_buf_getPayload(recv_p->bufRefCurrent);
        /*
        ** copy the already received bytes in the allocated received buffer
        */
        memcpy(payload_p,recv_p->buffer_header,recv_p->headerSize);
        recv_p->state = RECV_PAYLOAD;
        break;


      /*
      **_________________________________________________________________
      ** reception of the payload of the message
      **_________________________________________________________________
      */
      case RECV_PAYLOAD:
        /*
        ** attempt to receive the full header to figure out what kind of receive buffer
        ** Must be allocated
        */
        payload_p = (uint8_t*)ruc_buf_getPayload(recv_p->bufRefCurrent);
        status = af_unix_recv_stream_sock_recv(sock_p,payload_p+recv_p->nbread,
                                               recv_p->nb2read- recv_p->nbread ,0,&len_read);
        switch(status)
        {
          case RUC_OK:
	   /*
	   ** update the speculative scheduler
	   */
	   ruc_sockCtrl_speculative_scheduler_decrement(sock_p->connectionId);
           /*
           ** that fine's call the application with that received message
           */
           sock_p->stats.totalRecvBytes += len_read;
           sock_p->stats.totalRecvSuccess++;
           /*
           ** clear the reference of the buffer to avoid a double release that may occur
           ** if the application delete the context
           */
           bufref = recv_p->bufRefCurrent;
           recv_p->bufRefCurrent = NULL;
           (sock_p->userRcvCallBack)(sock_p->userRef,sock_p->index,bufref);
           recv_p->state = RECV_IDLE;
           recv_credit--;
           break;

          case RUC_WOULDBLOCK:
           /*
           ** we don't get the full message so no change, wait for the next receiver event
           */
	   sock_p->stats.emptyRecv++;
           return TRUE;

          case RUC_PARTIAL:
          /*
          ** update the count and re-attempt until getting a EAGAIN or a full header
          */
	  sock_p->stats.partialRecv++;
          sock_p->stats.totalRecvBytes += len_read;
          recv_p->nbread += len_read;
          break;

          case RUC_DISC:
          default:
          /*
          ** socket is dead call the user callback
          */
          bufref = recv_p->bufRefCurrent;
          recv_p->bufRefCurrent = NULL;
          ruc_buf_freeBuffer(bufref);
           /*
          ** general disconnection
          */
          af_unix_sock_stream_disconnect_internal(sock_p);

          /*
          ** it is up to the application to release the buffer if the error is fatal
          ** but for the case of the receiver, no buffer reference is provided
          */
          recv_p->state = RECV_DEAD;
          (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno);

          return TRUE;
        }
        break;
      /*
      **_________________________________________________________________
      ** Dead state of the receiver
      **_________________________________________________________________
      */
      case RECV_DEAD:
        return TRUE;
    }
  }
  return TRUE;
}