/* iemnet
 *
 * receiver
 *   receives data "chunks" from a socket
 *
 *  copyright © 2010-2015 IOhannes m zmölnig, IEM
 */

/* This program 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 2               */
/* of the License, or (at your option) any later version.                       */
/*                                                                              */
/* This program 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 this program; if not, see                                         */
/*     http://www.gnu.org/licenses/                                             */
/*                                                                              */

#define DEBUGLEVEL 4

#include "iemnet.h"
#include "iemnet_data.h"

#include <stdlib.h>
#include <errno.h>

#define INBUFSIZE 65536L /* was 4096: size of receiving data buffer */

struct _iemnet_receiver {
  int sockfd; /* owned outside; you must call iemnet__receiver_destroy() before freeing socket yourself */
  void*userdata;
  t_iemnet_receivecallback callback;
};


static void pollfun(void*z, int fd)
{
  /* read data from socket and call callback */
  t_iemnet_receiver*rec = (t_iemnet_receiver*)z;

  unsigned char data[INBUFSIZE];
  unsigned int size = INBUFSIZE;
  t_iemnet_chunk*chunk = NULL;
  int result = 0;
  int local_errno = 0;

  struct sockaddr_in from;
  socklen_t fromlen = sizeof(from);

  int recv_flags = 0;
#ifdef MSG_DONTWAIT
  recv_flags |= MSG_DONTWAIT;
#endif
  errno = 0;
  if(fd != rec->sockfd)
    DEBUG("%s(%p, %d) receives from %d\n", __FUNCTION__, rec, fd, rec->sockfd);

  result = recvfrom(rec->sockfd, (void *)data, size, recv_flags,
                    (struct sockaddr *)&from, &fromlen);
  local_errno = errno;
  //fprintf(stderr, "read %d bytes...\n", result);
  DEBUG("recvfrom %d bytes: %d %p %d", result, rec->sockfd, data, size);
  DEBUG("errno = %d", local_errno);
  chunk = iemnet__chunk_create_dataaddr(result, (result>0)?data:NULL, &from);

  /* call the callback with a NULL-chunk to signal a disconnect event. */
  (rec->callback)(rec->userdata, chunk);

  iemnet__chunk_destroy(chunk);
}

t_iemnet_receiver*iemnet__receiver_create(int sock, void*userdata,
    t_iemnet_receivecallback callback, int subthread)
{
  t_iemnet_receiver*rec = (t_iemnet_receiver*)malloc(sizeof(
                          t_iemnet_receiver));

  DEBUG("create new receiver for 0x%X:%d", userdata, sock);
  //fprintf(stderr, "new receiver for %d\t%x\t%x\n", sock, userdata, callback);
  if(rec) {
    rec->sockfd = sock;
    rec->userdata = userdata;
    rec->callback = callback;

    if(subthread) {
      sys_lock();
    }
    sys_addpollfn(sock, pollfun, rec);
    if(subthread) {
      sys_unlock();
    }

  }
  //fprintf(stderr, "new receiver created\n");

  return rec;
}
void iemnet__receiver_destroy(t_iemnet_receiver*rec, int subthread)
{
  int sockfd;
  if(NULL == rec) {
    return;
  }

  sockfd = rec->sockfd;

  if(subthread) {
    sys_lock();
  }
  sys_rmpollfn(rec->sockfd);

  /* FIXXME: read any remaining bytes from the socket */

  if(subthread) {
    sys_unlock();
  }

  DEBUG("[%p] really destroying receiver %d", sockfd);
  DEBUG("[%p] closed socket %d", rec, sockfd);

  rec->sockfd = -1;
  rec->userdata = NULL;
  rec->callback = NULL;

  free(rec);
  rec = NULL;
}


/* just dummy, since we don't maintain a queue any more */
int iemnet__receiver_getsize(t_iemnet_receiver*x)
{
  if(x) {
    return 0;
  }
  return -1;
}
