#ifndef _ORCD_H
#define _ORCD_H


#include <stdio.h>
#include <pthread.h>

#include "SSocket.h"
#include "Vector.h"
#include "GetOpt.h"
#include "serial.h"
#include "configfile.h"
#include "packetbuffer.h"

typedef struct listenerinfo listenerinfo_t;
typedef struct client       client_t;
typedef struct orcd         orcd_t;
typedef struct asyncclient  asyncclient_t;
typedef struct transaction  transaction_t;
typedef struct threadinfo   threadinfo_t;

#define ORC_MAX_PACKETSIZE   255  // maximum size of a packet
#define ORC_MAX_TRANSID      64   // maximum number of transaction IDs
#define ORC_MAX_SIMULTRANS   4   // the maximum number of transactions that we'll allow simultaneously

#define ORC_PAD_STICK        1
#define ORC_PAD_MENU         2
#define ORC_PAD_STOP         4
#define ORC_PAD_UP           8
#define ORC_PAD_DOWN         16
#define ORC_PAD_LEFT         32
#define ORC_PAD_RIGHT        64
#define ORC_PAD_BUTTONS      7

struct threadinfo
{
  orcd_t         *orcd;
  SSocket        *sock;
};

struct client
{
  orcd_t         *orcd;
  int            clientid;
  listenerinfo_t *li;
  pthread_t      clientReaderThread;
  pthread_t      clientWriterThread;
  SSocket        *sock;
  FILE           *sockf;
};

#define ORC_TRANS_FREE    0
#define ORC_TRANS_WAITING 1
#define ORC_TRANS_ACKED   2

struct transaction
{
  unsigned char   status;
  void            *response;
  int             responselen;

  pthread_cond_t  cond;         // The serial reader thread will signal this condition
  pthread_mutex_t mutex;        // mutex when it receives data.
};

struct orcd
{
  GetOpt            *opt;

  packetbuffer_t    *pb;

  int               serialfd;
  int               serialfd_connected;

  int               serialtimeoutms;
  int               serialconsecutivetimeouts;

  int               cmdpipe[2];
  int               asyncpipe[2];

  // we pass commands to the exec fork thread via this pipe.
  int               execpipe[2];

  // data written to this pipe will be written to the console.
  int               consolepipe[2];
  int               consoleDisabled;


  pthread_t         listenerThread;
  pthread_t         serialReaderThread;

  int               bytecount;
  int               transactioncount;

  int               transactionsPending;

  int               quit;
  int               failure;

  int               cmdclients;
  int               asyncclients;

  // notified whenever a button (or synthetic button) is pressed on
  // the orc pad
  pthread_cond_t    padeventCond;
  pthread_mutex_t   padeventMutex;

  int               padEvent;
  int               padOldUpDown, padOldLeftRight;
  int               padJoyX, padJoyY;

  // notified whenever we receive a new ack
  pthread_cond_t    transactionCond;
  pthread_mutex_t   transactionMutex;

  transaction_t     transactionScoreboard[64];
  unsigned char     transactionNextID;

  pthread_mutex_t   serialwriteMutex;

  pthread_mutex_t   scoreboardMutex;
  pthread_cond_t    scoreboardCond;
  
  pthread_mutex_t   bannerMutex;

  pthread_mutex_t   connectionCountMutex;

  Vector<Option*>   *config;
};

/* indexes into the array of fds returned by pipe() 
#define PIPEREAD 0
#define PIPEWRITE 1
*/

#define ORC_ESUCCESS    0
#define ORC_EBADPORT    1
#define ORC_EBADPARAM   2
#define ORC_EUNKNOWNCMD 3
#define ORC_ETIMEOUT    255

int doTransaction(orcd_t *orcd, const void *cmdin, int cmdlen, void *response);

void *commandListenerThreadProc(void *arg);
void *commandClientThreadProc(void *arg);

void *asyncListenerThreadProc(void *arg);
void *asyncClientThreadProc(void *arg);

void *serialReaderThreadProc(void *arg);

void *asyncPollerThreadProc(void *arg);
void *asyncPollerThreadProc2(void *arg);

void *bannerThreadProc(void *arg);
void bannerDraw(orcd_t *orcd);

int readPacket(orcd_t *orcd, int fd, void *bufin);

void crashQuit(orcd_t *orcd);

int padGets(orcd_t *orcd);
int padPoll(orcd_t *orcd);

int reconnect(orcd_t *orcd);

#endif
