#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 <errno.h>

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

#define LOGCHANNEL "ORCD"

//////////////////// SERIAL READER /////////////////////////////////////

void *serialReaderThreadProc(void *arg)
{
  orcd_t *orcd=(orcd_t*) arg;
  unsigned char buf[ORC_MAX_PACKETSIZE];

  int ot;
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&ot);

  struct timespec timenow;

  while (true)
    {
      int packetlen;

      if (orcd->serialfd_connected)
	{
	  packetlen=readPacket(orcd, orcd->serialfd, buf);

	  if (packetlen<=0)
	    {
	      log(LOG_ERROR,LOGCHANNEL,"Error while reading from serial port: %s",strerror(errno));
	      orcd->serialfd_connected = 0;
	    }
	}
      if (!orcd->serialfd_connected)
	{
		sleep(1);
		continue;
	}

      orcd->bytecount+=packetlen;

      int transID=buf[2] & 0x003f;
      if (transID>0)
	{
	  pthread_mutex_lock(&orcd->transactionScoreboard[transID].mutex);

	  if (orcd->transactionScoreboard[transID].status==ORC_TRANS_WAITING)
	    {
	      orcd->transactionScoreboard[transID].status=ORC_TRANS_ACKED;
	      orcd->transactionScoreboard[transID].responselen=packetlen;
	      //	      printf("%02X",transID); fflush(NULL);
	      //	      printf("acking %i\n", transID);

	      if (orcd->transactionScoreboard[transID].response!=NULL)
		memcpy(orcd->transactionScoreboard[transID].response, buf, packetlen);
	      
	      pthread_cond_signal(&orcd->transactionScoreboard[transID].cond);
	    }
	  else
	    {
	      timespec_now(&timenow);

	      if (orcd->opt->getOptionBool("verbose"))
		log(LOG_WARN, LOGCHANNEL,"Unsolicited packet, transid=%02x ", 
		    transID);
	    }

	  pthread_mutex_unlock(&orcd->transactionScoreboard[transID].mutex);
	}

      // if it's a state packet, give a copy to everyone
      if (buf[3]=='*')
	{
	  packetbuffer_addpacket(orcd->pb, buf, packetlen);

	  // is this a packet from lcd?
	  if ((buf[2]&0xc0)==0x80)
	    {
	      pthread_mutex_lock(&orcd->padeventMutex);

	      int newUpDown=buf[7];
	      int newLeftRight=buf[8];

	      orcd->padEvent=orcd->padEvent&0xff8;
	      orcd->padEvent|=buf[6]; // OR in the button codes.

	      if ((newUpDown&0xf0)!=(orcd->padOldUpDown&0xf0))
		orcd->padEvent|=ORC_PAD_UP;
	      if ((newUpDown&0x0f)!=(orcd->padOldUpDown&0x0f))
		orcd->padEvent|=ORC_PAD_DOWN;
	      if ((newLeftRight&0xf0)!=(orcd->padOldLeftRight&0xf0))
		orcd->padEvent|=ORC_PAD_LEFT;
	      if ((newLeftRight&0x0f)!=(orcd->padOldLeftRight&0x0f))
		orcd->padEvent|=ORC_PAD_RIGHT;

	      orcd->padJoyX=buf[4];
	      orcd->padJoyY=buf[5];

	      // if this is our first time through, don't register any events.
	      if (orcd->padOldUpDown==-1)
		orcd->padEvent=0;

	      //	      printf("%02X:%02X:%02X\n",buf[6],buf[7],buf[8]);

	      orcd->padOldUpDown=newUpDown;
	      orcd->padOldLeftRight=newLeftRight;

	      pthread_cond_broadcast(&orcd->padeventCond);
	      pthread_mutex_unlock(&orcd->padeventMutex);
	    }
	}
    }

  return NULL;
}

