#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
#include <pthread.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <errno.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

// #include <dmalloc.h>

#include "orcd.h"
#include "SSocket.h"
#include "serial.h"
#include "ioutils.h"
#include "logutils.h"
#include "timespec.h"
#include "orcutils.h"
#include "packetbuffer.h"

#define LOGCHANNEL "ASYNC"

//////////////////// ASYNC LISTENER /////////////////////////////////////

void *asyncListenerThreadProc(void *arg)
{
  threadinfo_t *ti=(threadinfo_t*) arg;
  pthread_attr_t threadAttr;
  pthread_attr_init(&threadAttr);
  pthread_attr_setstacksize(&threadAttr, PTHREAD_STACK_MIN + 32768);

  int ot;
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&ot);

  signal(SIGPIPE, SIG_IGN);

  while(true)
    {
      threadinfo_t *newti=(threadinfo_t*) calloc(sizeof(threadinfo_t),1);
      newti->orcd=ti->orcd;
      newti->sock=ti->sock->accept();

      pthread_t clientThread;
      if (pthread_create(&clientThread, &threadAttr, asyncClientThreadProc, newti))
	{
	  logerrno(LOG_ERROR,LOGCHANNEL,"Unable to create async thread.");
	  return NULL;
	}
      pthread_detach(clientThread);

      pthread_mutex_lock(&ti->orcd->connectionCountMutex);
      newti->orcd->asyncclients++;
      pthread_mutex_unlock(&ti->orcd->connectionCountMutex);

      log(LOG_VERBOSE,LOGCHANNEL,"async connected");

    }

  return NULL;
}

//////////////////// CLIENT READER /////////////////////////////////////
void *asyncClientThreadProc(void *arg)
{
  threadinfo_t *ti;
  char         buf[ORC_MAX_PACKETSIZE];
  int          packetlen;

  ti=(threadinfo_t*) arg;

  packetbufferstate_t *pbs=packetbuffer_createstate(ti->orcd->pb);

  int ot;
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&ot);
  signal(SIGPIPE, SIG_IGN);

  while (true)
    {
      packetbuffer_getpacket(ti->orcd->pb, pbs, buf, &packetlen);

      if (ti->sock->writeAll(buf, packetlen)!=packetlen)
	break;
    }

  pthread_mutex_lock(&ti->orcd->connectionCountMutex);
  ti->orcd->asyncclients--;
  pthread_mutex_unlock(&ti->orcd->connectionCountMutex);

  log(LOG_VERBOSE,LOGCHANNEL,"client writer thread quitting");

  packetbuffer_deletestate(ti->orcd->pb, pbs);

  ti->sock->close();
  delete ti->sock;
  free(ti);

  pthread_exit(NULL);
}
