#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/io.h>
#include <exec/tasks.h>
#include <exec/errors.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <devices/scsidisk.h>
#include <devices/serial.h>
#include <proto/exec.h>

#include <instrument.h>
#include <DriverIo.h>
#include <scanner.h>

#include <stdlib.h>

static ULONG  sigMask;
static struct MsgPort*  driverPort;    /* main driver port                   */
static struct IOStdReq* scannerIO;     /* a standard IORequest structure     */
static struct SCSICmd   Cmd;           /* where the actual SCSI command goes */
static BOOL   SCSI;
static BOOL   scannerIoOut;

static void forkIo(struct process* prc,int hd,short cmd,void* data,int* data_len,short* status);

int openDriverIo(const char* dname,const int unit,struct MsgPort* port)
{
  if( scannerIO = CreateIORequest(port,sizeof(struct IOExtSer)) )
  {
    if( OpenDevice(dname,unit,(struct IORequest *)scannerIO,0) )
    {
      DeleteIORequest(scannerIO);
      scannerIO = NULL;
    }
    else
    {
      UBYTE READY[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

      scannerIO->io_Length  = sizeof(struct SCSICmd);
      scannerIO->io_Data    = (APTR)&Cmd;
      scannerIO->io_Command = HD_SCSICMD;    /* the command we are sending   */

      Cmd.scsi_Data = NULL;                  /* No data                      */
      Cmd.scsi_Length = 0;                   /* how much we will accept      */
      Cmd.scsi_Flags = SCSIF_NOSENSE;

      Cmd.scsi_SenseData = NULL;             /* where sense data will go     */
      Cmd.scsi_SenseLength = 0;              /* how much we will accept      */
      Cmd.scsi_SenseActual = 0;              /* how much has been received   */
      Cmd.scsi_Status = 0;

      Cmd.scsi_Command = READY;              /* issuing a READY command      */
      Cmd.scsi_CmdLength = 6;                /* length of the command        */
      DoIO( (struct IORequest *)scannerIO ); /* send it to the device driver */
      
      if( scannerIO->io_Error == IOERR_NOCMD )
        SCSI = FALSE;                        /* It was'nt a scsi device      */
      else
        SCSI = TRUE;
      
      sigMask = (0x01<<port->mp_SigBit) | (SIGBREAKF_CTRL_C);
      driverPort = port;
    }
  }
  
  return( scannerIO != NULL);
}

void closeDriverIo(void)
{
  if( scannerIO )
  {
    CloseDevice( (struct IORequest *)scannerIO );
    DeleteIORequest(scannerIO);
    scannerIO = NULL;
    driverPort = NULL;
    sigMask = 0;
    SCSI = FALSE;
  }
}

struct IOStdReq* waitForIOReq(void)
{
  BOOL aborted = FALSE;

  while( driverPort )
  {
    struct IOStdReq* ior;

    if( Wait(sigMask) & SIGBREAKF_CTRL_C )
    {
      // Abort signal
      if( scannerIoOut )
      {
        aborted = TRUE;
        AbortIO((struct IORequest*)scannerIO);
        WaitPort(driverPort);
      }
    }

    if( ior = (struct IOStdReq*)GetMsg(driverPort) )
    {
      // A message is on the port
      if( ior == scannerIO )
      {
        // Reply message from scanner.
        scannerIoOut = FALSE;
        if( aborted )
          ior->io_Error = IOERR_ABORTED;
        return ior;
      }

      // It must be a request from BetaScan
      if( !scannerIoOut )
      {
        // No request is out. Return and evaluate the request
        return ior;
      }
      else
      {
        // We are waiting for reply from scanner.
        // Can't do anything except aborting the scanner IO.
        ior->io_Error = SCAN_ERR_BUSY;
        ReplyMsg((struct Message*)ior);      
      }
    }
  }    
  return NULL;
}

int isSCSI(void)
{
  return SCSI;
}

//
// Common utilities for all ports
//
int obtainIoHandle(void)
{
  if( scannerIO )
    return -1;
  else
    return 0;
}

void releaseIoHandle(int hd)
{
}

void doScannerIo(int hd,short cmd,void* data,int* data_len,short* status)
{
  if( scannerIO )
  {
    struct Task* tp;

    tp = FindTask(0);
  
    if( tp->tc_UserData )
      forkIo((struct process*)tp->tc_UserData,hd,cmd,data,data_len,status);
    else
    {
      scannerIO->io_Length  = *data_len;
      scannerIO->io_Data  = data;
      scannerIO->io_Command = cmd;              /* the command we are sending   */
    
      SendIO( (struct IORequest *)scannerIO );  /* send it to the device driver */
      scannerIoOut = TRUE;                      /* mark io request out          */
      waitForIOReq();                           /* go and wait for return       */

      *status = scannerIO->io_Error;
    }
  }
  else
    *status = 1;

  if( *status )
    *data_len = 0;
  else
    *data_len = scannerIO->io_Actual;
}

/////////////////////////////////////////////////////////////////////////////
//
// Unix specific part
//
/////////////////////////////////////////////////////////////////////////////

struct FilesTab
{
  struct fileinfo **files;
  int max_files;
};


struct process
{
    struct MinNode node;
    struct Task *process;
    int pid;
    BPTR input;
    enum { alive, exited } status;
    int rc;

    void*  a7;
    struct FilesTab files;
    
    // Extension
    struct IOExtSer scannerIO;
};


//  Allocate memory for extended structure process
//
void* _allocProcess(void)
{
  return calloc(1,sizeof(struct process));
}

void _fork_prolog(void)
{
  struct Process* dosPrc;
  struct process* unxPrc;

  dosPrc = (struct Process*)FindTask(0);
  unxPrc = (struct process*)dosPrc->pr_Task.tc_UserData;
  
  unxPrc->scannerIO = *((struct IOExtSer*)scannerIO);
  unxPrc->scannerIO.IOSer.io_Message.mn_ReplyPort = &dosPrc->pr_MsgPort;
}

static void forkIo(struct process* prc,int hd,short cmd,void* data,int* data_len,short* status)
{
  prc->scannerIO.IOSer.io_Length  = *data_len;
  prc->scannerIO.IOSer.io_Data  = data;
  prc->scannerIO.IOSer.io_Command = cmd;        /* the command we are sending   */
    
  DoIO( (struct IORequest*)(&prc->scannerIO) ); /* send it to the device driver */
  *status = prc->scannerIO.IOSer.io_Error;
}
