#include "ModemBroker.h"
#include "Modem.h"
#include "ModemFork.h"
#include "ModemSerial.h"
//#include "ModemBlob.h"
#include "MMRecipient.h"
#include <list>

//#include "ModemMessage.h"

ModemBroker* ModemBroker::_pgModemBroker = 0;

//////////////////////////////////////
// ModemBroker implementation
ModemBroker::ModemBroker()
{

    _pgModemBroker = this;
    debug = 2;
    _pSendingModem = 0;
}


ModemBroker* ModemBroker::getModemBroker()
{
    return _pgModemBroker;
}


ModemBroker::~ModemBroker()
{
    // Stop/Delete all modems.
    ModemList::iterator ipm;
    for(ipm = _pmlModems.begin(); ipm != _pmlModems.end(); ipm++) {
	ModemFork* pModem = (ModemFork *) *ipm;
	pModem->stop();
	clean( pModem );
    }
    

    // Delete all messages.
    _msPendingMessages.clear();
}

void ModemBroker::addModem(Modem* pModem)
{
    Assert(pModem);
    _pmlModems.push_back(pModem);
}

void ModemBroker::removeModem(Modem* pModem)
{
    Assert(pModem);
    _pmlModems.remove(pModem);
	_pSendingModem = NULL;
}

void ModemBroker::addMessage(ModemMessage *pMessage)
{
    _msPendingMessages.push_back(pMessage);
}

void ModemBroker::handleOutgoingData()
{
	Assert(_pSendingModem);
	Modem::Status status;

    try {
	status = _pSendingModem->getWriteStatus();
    } catch (...) {
	status = Modem::sERROR;
    }

    switch (status) {
    case Modem::sPENDING:
	// non-blocking, so we have to return...
	return;
    case Modem::sSUCCEEDED:
    case Modem::sFAILED:
    case Modem::sERROR:
    case Modem::sIDLE:
		
		MessageBufferList::iterator mli;
		mli = _msPendingMessages.begin();

		if (mli == _msPendingMessages.end()) return;

		//ModemMessage * pMessage = *mli;

		char *buffer = new char[((ModemMessage *)*mli)->nBinaryBytes()];
		((ModemMessage *)*mli)->pcUnparse(buffer);
    
    
		try {
			dwrite(4)("socketbroker: writing something");
			bool b = _pSendingModem->bBeginWrite(buffer, ((ModemMessage *)*mli)->nBinaryBytes());

			if (!b) dwrite(2)("bBeginWrite: write failed.");
		}
		catch (Exception *e) {
		    dwrite(2)("bBeginWrite: exception.");
			delete e;
		}
		delete ((ModemMessage *)*mli);
		_msPendingMessages.erase(mli);
		int zzz = 53;
		//delete pMessage;
		delete buffer;
		break;
    }

    

}

void ModemBroker::handleIncomingData()
{
    // Query each modem to see if it has data for us.
    ModemList::iterator ipm = _pmlModems.begin();
    for( ; ipm != _pmlModems.end(); ++ipm) {
		Modem* pModem = *ipm;
		if (pModem) {
			char buffer[iMAX_BLOB_SIZE];

			int nBytesRead = pModem->read(buffer, iMAX_BLOB_SIZE);

			if (nBytesRead > 0) {
			  ModemMessage * pMessage = new ModemMessage(buffer);

			  dwrite(4)("Socketbroker: nBytesRead = %d, type = %d",nBytesRead,pMessage->getType() );

			  if (pMessage->getType() != ModemMessage::tUNKNOWN) {
				MMRecipient::getMMRecipient()->receive(pMessage);
			  }
			  delete pMessage;
			}
		}
    }
}


void ModemBroker::runCycle()
{
	// cycle the modem threads
    ModemList::iterator ipm;
    for(ipm = _pmlModems.begin(); ipm != _pmlModems.end(); ipm++) {
	ModemFork* pModem = (ModemFork *) *ipm;
	pModem->cycle();
    }
    handleIncomingData();
    selectSendingModem();
    if (_pSendingModem) handleOutgoingData();
}


bool ModemBroker::hasConnection()
{
    if (!_pSendingModem) return false;
    return _pSendingModem->bHasConnection();
}


long ModemBroker::getBytesPerSecond()
{
    if (!_pSendingModem) return 0;
    if (_pSendingModem->bHasConnection())
	return _pSendingModem->getBytesPerSecond();
    else 
	return 0;
}


void ModemBroker::selectSendingModem()
{
    int iBestSpeed = 0;
	_pSendingModem = NULL;
    if (_pmlModems.begin() == _pmlModems.end()) return;
    // Iterate the modems and find the one that claims the best speed.
    ModemList::iterator ipm;
    for(ipm = _pmlModems.begin(); ipm != _pmlModems.end(); ipm++)
	{
	    Modem* pModem = *ipm;
	    Assert(pModem);

		if (pModem->bHasConnection())
		{
			int iModemSpeed = pModem->getBytesPerSecond();
			if (iModemSpeed > iBestSpeed)
			{
			    iBestSpeed = iModemSpeed;
			    _pSendingModem = pModem;
			}		
		}
	}
}
