//=========================================================================
// Summary  :
// Filename : Modem.cc
// Author   : haydn... fairfield
// Project  :
// Revision : 1
// Created  : 2000/12/08
// Modified : 2000/12/08
//=========================================================================
// Description :
//=========================================================================



#include "ModemFork.h"

#ifndef _MSC_VER // QNX
#include <signal.h>
#include <sys/types.h>
#include "framework/SharedData.h"
#include "utils/System.h"
#include <process.h>
#endif

#ifdef _MSC_VER // Windows
#include <process.h>
#endif

//////////////////////////////////////
// ModemFork implementation
ModemFork::ModemFork(const char* pcSharedMemName)
{
  _bSuccess = false;   
  _bRunCalled = false;
  _pidParent = 0;
  _pidChild = 0;
  
  _pCommunication = new ModemForkSharedData();
#ifndef _MSC_VER // QNX
  sem_init(&(_pCommunication->_childRuns), 1, 1);
#else _MSC_VER // Windows
	h_ret = NULL;
#endif
}


ModemFork::~ModemFork()
{
}

#ifdef _MSC_VER // Windows
DWORD WINAPI ChildBegin(LPVOID pThis)
{
	((ModemFork*)pThis)->runChild();

	return 1;
}
#endif

#ifndef _MSC_VER // QNX
void ChildBegin(void * pThis)
{
    ((ModemFork*)pThis)->runChild();
    
    return;
}
#endif

//////////////////////////////////////////////////////////////
// parent functions

void ModemFork::run()
{
	_bRunCalled = true;
	_bSuccess = false;
#ifndef _MSC_VER // QNX
		
	_pidChild = _beginthread(&ChildBegin,NULL,16384, this);	
    if (_pidChild != -1) {
	    _bSuccess = true;
	}
#endif
	
#ifdef _MSC_VER // Windows
	h_ret = CreateThread(NULL,
			     0,
			     ChildBegin,
			     this,
			     0,
			     NULL);
	
	if (h_ret) {
	    _pidParent = _getpid();
	    _bSuccess = true;
	}
	
#endif
}

void ModemFork::stop()
{
	_pCommunication->_bKillChild = true;
	while (!_pCommunication->_bChildIsDead) {
	    cycle();
	    Sleep(100);
	}
}

void ModemFork::cycle()
{
#ifndef _MSC_VER //QNX
	sem_post(&(_pCommunication->_childRuns));
#else // Windows
#endif
}

int ModemFork::read(char* buffer, int maxBytes)
{
    if (!_bRunCalled) run();
    if (!_bSuccess) return 0;

    // All timeouts currently zero.

    // Do we have command of the read transit buffer?
    if (_pCommunication->_tbReadBuffer._bReaderInControl)
	{
	    // Yes!  We are ready.  Read it.
	    int nBytes = min(maxBytes, _pCommunication->_tbReadBuffer._iBufferSize);
	    Assert(nBytes > 0);

	    // Must copy the data because it may well get overwritten soon.
	    memcpy(buffer, _pCommunication->_tbReadBuffer._buffer, nBytes);

	    // Let the child know that we are done with the read buffer.
	    _pCommunication->_tbReadBuffer._bReaderInControl = false;

	    return nBytes;
	}
    else
	{
	    return 0;
	}
}

bool ModemFork::bBeginWrite(const char* buffer, int nBytes)
{
    if (!_bRunCalled) run();
    if (!_bSuccess) return false;

    if (nBytes <= 0) return false;
    if (!buffer) return false;
    
    // Can we write?
    if (!_pCommunication->_tbWriteBuffer._bReaderInControl)
	{
	    // Yes, we can.  Go ahead.
	    TransitBuffer* pWriteBuffer = &_pCommunication->_tbWriteBuffer;
	    
	    nBytes = min(nBytes, pWriteBuffer->iGetMaxSize());
	    memcpy(pWriteBuffer->_buffer, buffer, nBytes);
	    pWriteBuffer->_iBufferSize = nBytes;

	    // Update local status.
	    _status = sPENDING;
	    pWriteBuffer->_status = sPENDING;

	    // And then pass control to the child.
	    pWriteBuffer->_bReaderInControl = true;

	    return true;
	}
    else
	{
	    return false;
	}
}



int ModemFork::getBytesPerSecond()
{
    Assert(_pCommunication);
    return _pCommunication->_nBytesPerSecond;
}

bool ModemFork::bCanWrite()
{
    Assert(_pCommunication);
    return !_pCommunication->_tbWriteBuffer._bReaderInControl;
}

bool ModemFork::bHasIncomingData()
{
    Assert(_pCommunication);
    return _pCommunication->_tbReadBuffer._bReaderInControl;
}

bool ModemFork::bHasConnection()
{
    Assert(_pCommunication);
    return _pCommunication->_bHasConnection;
}

int ModemFork::iGetMaxTransitBytes()
{
    return iMAX_BLOB_SIZE;
}



//////////////////////////////////////////////////////////////
//  Child implementation


void ModemFork::runChild()
{
    while(!_pCommunication->_bKillChild) {
#ifndef _MSC_VER //QNX
		sem_wait(&(_pCommunication->_childRuns));
#else // Windows
		Sleep(10);
#endif

		bReadChild();
		
		bWriteChild();
			
		// Update the expected modem speed.
		_pCommunication->_bHasConnection = bHasConnectionChild();
		if (_pCommunication->_bHasConnection)
			_pCommunication->_nBytesPerSecond = getBytesPerSecondChild();
		else
			_pCommunication->_nBytesPerSecond = 0;
    }
	_pCommunication->_bChildIsDead = true;
}

bool ModemFork::bReadChild()
{
  // Do we have control of read transit block?
  if (!_pCommunication->_tbReadBuffer._bReaderInControl) {
      // "Write" is in control of the read block.  For read actions, we are the writer.
      int nBytes = iReadFromModem(_pCommunication->_tbReadBuffer._buffer,
				  _pCommunication->_tbReadBuffer.iGetMaxSize());
      
      // Did we get anything?
      if (nBytes > 0) {
	  // Yes!  Save length data.
	  _pCommunication->_tbReadBuffer._iBufferSize = nBytes;
	  
	  // Hand off control to reader.
	  _pCommunication->_tbReadBuffer._bReaderInControl = true;
	  
	  return true;
      }
  }
  return false;
}


bool ModemFork::bWriteChild()
{
    // Do we have control of either transit block?
    if (_pCommunication->_tbWriteBuffer._bReaderInControl) {
	// Read is in control.  For write actions, we are the reader.
	int nBytes = iWriteToModem(_pCommunication->_tbWriteBuffer._buffer,
				   _pCommunication->_tbWriteBuffer._iBufferSize);
	
	if (nBytes == _pCommunication->_tbWriteBuffer._iBufferSize) {
	    _pCommunication->_tbWriteBuffer._status = sSUCCEEDED;
	} else {
	    _pCommunication->_tbWriteBuffer._status = sFAILED;
	}
	
	_pCommunication->_tbWriteBuffer._bReaderInControl = false;
	return true;
    }   
    return false;
}


Modem::Status ModemFork::getWriteStatus()
{
    /*
  // Is the child in control?
  if (_pCommunication->_tbWriteBuffer._bReaderInControl)
  {
    // Yes!  It must be trying to write.
    return sPENDING;
  }
  else
  {
    */
    // No!  It must have returned.
    return _pCommunication->_tbWriteBuffer._status;
    // }
}
