//=========================================================================
// Summary  :
// Filename : ModemFork.h
// Author   : Andrew Grant
// Project  :
// Revision : 1
// Created  : 2001/01/19
// Modified : 2001/01/19
//=========================================================================
// Description :  Generic Modem Interface
//=========================================================================
#ifndef _ModemFork_H
#define _ModemFork_H

#include "Modem.h"
#ifndef _MSC_VER // QNX
#include "Fork.h"
#include <semaphore.h>
#endif
#ifdef _MSC_VER // Windows
#include <process.h>
#endif
#include <string>

class SharedData;

///////////////////////////////////////////////////////
//  Forked Modem class- allows a blocking modem to run transparently
//   in another thread without blocking the interface calls.
//  Also a prototype of a possibly simpler framework option.

//  When the class forks, the parent process takes on the role of
//   interface, and the child is the workhorse.  The child never
//   returns from the constructor.

//
// INHERIT FROM ModemFork:
//  Implement these functions:
//    virtual int iReadFromModem(char* buffer, int iMaxLen);
//    virtual int iWriteToModem(const char* buffer, int iMaxLen);
//
//  See ModemSerial as an example.


class ModemFork : public Modem {

public:

  ModemFork(const char* pcSharedMemName);
  virtual ~ModemFork();


  ///////////////////////////////////////////////////////////////////
  // Runs runParent in the parent thread and runChild in the child thread.
  void run();
  void runChild();

  void stop();
  void cycle();

 protected:

  ///////////////////////////////////////////////////////////////////
  // NEVER CALL THIS FUNCTION except from readChild.
  // Reads data from hardware and writes it to buffer, up to iMaxLen bytes.
  //  Returns number of bytes read into buffer.  This call is allowed to block.
  virtual int iReadFromModem(char* buffer, int iMaxLen)=0;

  ///////////////////////////////////////////////////////////////////
  // NEVER CALL THIS FUNCTION except from writeChild.
  // Reads data from buffer and writes it to hardware.
  //  Returns number of bytes written from buffer.
  //  This call is allowed to block.
  //  This call may destroy the data in buffer, but will not deallocate it.
  virtual int iWriteToModem(char* buffer, int iMaxLen)=0;

  // Like getBytesPerSecond, but called by runChild to get the real answer.
  virtual int getBytesPerSecondChild() =0;

  // Like bHasConnection, but called by runChild.
  //  This gets called frequently.  If there is no connection, this call attempts
  //  to establish one.
  virtual bool bHasConnectionChild() = 0;


  ///////////////////////////////////////////////////////////////////
  // Run main processing loop.

 public:
  ///////////////////////////////////////////////////////////////////
  // Modem OVERRIDES
  virtual int read(char *buf, int maxBytes) ;
  virtual bool bBeginWrite(const char *buf, int nBytes) ;
  virtual int getBytesPerSecond();
  virtual bool bCanWrite();
  virtual bool bHasIncomingData();
  virtual bool bHasConnection();
  virtual int  iGetMaxTransitBytes() ;
  virtual Status getWriteStatus();

protected:

  /////////////////////////////////////////////////////////////////
  // Called to read data from remote modem and place it in
  //  the transit buffer.  Performs error checking.  True when
  //  we did something (fail or succeed).  Called only from child.
  bool bReadChild();

  /////////////////////////////////////////////////////////////////
  // Called to write data from transit buffer to remote modem.
  // true when we did something.  Called only from child.
  bool bWriteChild();




  ///////////////////////////////////////////////////////////////////
  // Shared memory stuff

  class TransitBuffer
  {
   public:
     TransitBuffer()
     {
       _iBufferSize = 0;  // start empty
       _bReaderInControl = false; // writer gets first crack at it
       _status = sIDLE;
     }

     // Size of written data.
    int   _iBufferSize;

    ////////////////////////////////////////////////////////////////////////
    // True when the reader has full control, else writer has full control.
    // ONLY THE CONTROLLING SIDE CAN FLIP THIS BIT!!!
    bool  _bReaderInControl;

    char  _buffer[iMAX_BLOB_SIZE];
    Modem::Status _status;
    int   iGetMaxSize() const { return iMAX_BLOB_SIZE; };

  };

  class ModemForkSharedData
  {
   public:
    ModemForkSharedData()
    {
      _bKillChild = false;    // set by parent only
      _bChildIsDead = false;  // set by child only
      _nBytesPerSecond = 0;   // set by child only
      _bHasConnection = 0;    // set by child only	
    }

    bool _bKillChild;       // true when we want the child to die
    bool _bChildIsDead;     // true when the child is dead.
    int  _nBytesPerSecond;  // approximate transit speed over the modem at this time
    bool _bHasConnection;   // true if we expect data to get through via the modem
#ifndef _MSC_VER // QNX
	sem_t _childRuns;
#endif	

    TransitBuffer _tbReadBuffer;
    TransitBuffer _tbWriteBuffer;
  };

  ModemForkSharedData*  _pCommunication;
  bool _bSuccess;   // true if the fork worked.
  bool _bRunCalled; // true if "run()" has ever been called.

  unsigned long _pidChild;   // pid of the child.
  unsigned long _pidParent;  // pid of the parent.

  string                  _sSharedDataName;
  SharedData*             _pSharedData;

#ifdef _MSC_VER // Windows
  HANDLE h_ret;
#endif
};
#endif
