#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"

#define LOGCHANNEL "COMMAND"

//////////////////// COMMAND LISTENER /////////////////////////////////////

void *commandListenerThreadProc(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, commandClientThreadProc, newti))
	{
	  logerrno(LOG_ERROR,LOGCHANNEL,"Unable to create client thread.");
	  return NULL;
	}
      pthread_detach(clientThread);

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

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

    }

  return NULL;
}

// CLIENT THREAD

void *commandClientThreadProc(void *arg)
{
  threadinfo_t *ti=(threadinfo_t*) arg;
  char buf[ORC_MAX_PACKETSIZE], response[ORC_MAX_PACKETSIZE];

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

  int sockfd=ti->sock->getFD();
  int packetlen;

  while(true)
    {
      packetlen=readPacket(ti->orcd, sockfd, buf);
      
      if (packetlen<=0)
	goto disconnect;

      int responselen=doTransaction(ti->orcd, buf, packetlen, response);
      if (responselen>0)
	{
	  if (ti->sock->writeAll(response, responselen)!=responselen)
	    goto disconnect;
	}
      else
	{
	  // generate timeout response
	  response[0]=0xed;
	  response[1]=5;
	  response[2]=buf[2];
	  response[3]=ORC_ETIMEOUT;
	  response[4]=checksum(response, 4);

	  if (ti->sock->writeAll(response,  response[1])!=response[1])
	    goto disconnect;
	}
    }
  
 disconnect:
  pthread_mutex_lock(&ti->orcd->connectionCountMutex);
  ti->orcd->cmdclients--;
  pthread_mutex_unlock(&ti->orcd->connectionCountMutex);

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

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


  pthread_exit(NULL); //return NULL;
}
