#include <classes/Exec/devices.h>

#include <exec/memory.h>
#include <String.h>

#include <pragma/exec_lib.h>

/*

Der Kopierkonstruktor und der Zuweisungsoperator benutzen das Attribut opened
um festzustellen, ob das Device schon geöffnet ist (!opened.only()).

Ist das Device schon geöffnet, werden weitere Öffnungsversuche unterbunden,
und das Schließen wird nur von der Instanz erlaubt, die das Device auch
geöffnet hat.

Ist das Device beim Kopieren noch nicht geöffnet, kann durch die neue Kopie
und durch die alte das Device geöffnet und geschlossen werden, die Kontrolle
findet dann unabhängig voneinander statt.

*/

IORequestC::IORequestC(ULONG size)
	: PortC(), copied(FALSE), opened()
{
	requestsize = size;
	iorequest = (struct IORequest *) CreateIORequest(port(),requestsize);
	if (!iorequest)
		throw MemoryX(requestsize,MEMF_PUBLIC);
	send = FALSE;
	closed = TRUE;
}

IORequestC::IORequestC(const IORequestC &s)
	: PortC(s), copied(TRUE)
{
	requestsize = s.requestsize;
	iorequest = (struct IORequest *) AllocMem(requestsize,MEMF_PUBLIC);
	if (!iorequest)
		throw MemoryX(requestsize,MEMF_PUBLIC);
	memcpy(iorequest,s.iorequest,requestsize);
	// Für das Attribut opened hat only() die Bedeutung von Device geschlossen
	if (s.opened.only())
		// Falls dem so ist, lege ich eine neue Instanz davon an,
		opened = ShareManualC()
	else {
		// ansonsten wird nur eine weitere Referenz darauf genommen
		opened = s.opened;
		opened.reference();
	};
	send = FALSE;
	closed = TRUE;
}

IORequestC::~IORequestC()
{
	close();
	if (iorequest)
	{
		if (!copied)
			DeleteIORequest(iorequest);
		else
			FreeMem(iorequest,requestsize);
	};
}

IORequestC &IORequestC::operator = (const IORequestC &s)
{
	if (this != &s)
	{
		close();
		if (iorequest)
		{
			if (!copied)
				DeleteIORequest(iorequest);
			else
				FreeMem(iorequest,requestsize);
		};
		copied = TRUE;
		PortC::operator=(s);
		requestsize = s.requestsize;
		iorequest = (struct IORequest *) AllocMem(requestsize,MEMF_PUBLIC);
		if (!iorequest)
			throw MemoryX(requestsize,MEMF_PUBLIC);
		memcpy(iorequest,s.iorequest,requestsize);
		// Für das Attribut opened hat only() die Bedeutung von Device geschlossen
		if (s.opened.only())
			// Falls dem so ist, lege ich eine neue Instanz davon an,
			opened = ShareManualC()
		else {
			// ansonsten wird nur eine weitere Referenz darauf genommen
			opened = s.opened;
			opened.reference();
		};
		send = FALSE;
		closed = TRUE;
	};
	return *this;
}

BYTE IORequestC::error() const
{
	return iorequest->io_Error
}

BYTE IORequestC::open(STRPTR name, ULONG unit, ULONG flags)
{
	if (opened.only())
	{
		BYTE r = OpenDevice(name,unit,iorequest,flags);
		if (r == 0)
		{
			opened.reference();
			closed = FALSE;
		};
		return r;
	};
	return IOERR_OPENFAIL;
}

VOID IORequestC::close()
{
	if (!closed)
	{
		abortIO();
		waitIO();
		opened.dereference();
		closed = TRUE;
		if (opened.only())
			CloseDevice(iorequest);
	};
}

BYTE IORequestC::doIO()
{
	if (isOpen() && !send)
		return DoIO(iorequest);
	return IOERR_OPENFAIL;
}

BYTE IORequestC::doIO(UWORD command)
{
	if (isOpen() && !send)
	{
		iorequest->io_Command = command;
		return DoIO(iorequest);
	};
	return IOERR_OPENFAIL;
}

VOID IORequestC::sendIO()
{
	if (isOpen() && !send)
	{
		send = TRUE;
		SendIO(iorequest);
	};
}

VOID IORequestC::sendIO(UWORD command)
{
	if (isOpen() && !send)
	{
		iorequest->io_Command = command;
		send = TRUE;
		SendIO(iorequest);
	};
}

BOOL IORequestC::checkIO()
{
	if (send)
	{
		BOOL b = CheckIO(iorequest) != NULL;
		if (b)
			send = FALSE;
		return b;
	};
	return TRUE;
}

BYTE IORequestC::waitIO()
{
	if (send)
	{
		send = FALSE;
		return WaitIO(iorequest);
	};
	return IOERR_OPENFAIL;
}

VOID IORequestC::abortIO()
{
	if (send)
		AbortIO(iorequest);
}

// *************************************************************

IOStdRequestC::IOStdRequestC(ULONG size)
	: IORequestC(size)
{
}

IOStdRequestC::IOStdRequestC(const IOStdRequestC &s)
{
}

IOStdRequestC::~IOStdRequestC()
{
}

IOStdRequestC &IOStdRequestC::operator= (const IOStdRequestC &s)
{
	if (this != &s)
	{
		IORequestC::operator=(s);
	};
	return *this;
}

ULONG IOStdRequestC::actual() const
{
	return request()->io_Actual;
}

BYTE IOStdRequestC::doIO(UWORD command, ULONG length, APTR data)
{
	IOStdReq *r = request();
	if (!isSend())
	{
		r->io_Command = command;
		r->io_Length = length;
		r->io_Data = data;
		return IORequestC::doIO();
	};
	return IOERR_OPENFAIL;
}

BYTE IOStdRequestC::doIO(UWORD command, ULONG length,
	APTR data, ULONG offset)
{
	IOStdReq *r = request();
	if (!isSend())
	{
		r->io_Command = command;
		r->io_Length = length;
		r->io_Data = data;
		r->io_Offset = offset;
		return IORequestC::doIO();
	};
	return IOERR_OPENFAIL;
}

VOID IOStdRequestC::sendIO(UWORD command, ULONG length, APTR data)
{
	IOStdReq *r = request();
	if (!isSend())
	{
		r->io_Command = command;
		r->io_Length = length;
		r->io_Data = data;
		IORequestC::sendIO();
	};
}

VOID IOStdRequestC::sendIO(UWORD command, ULONG length,
	APTR data, ULONG offset)
{
	IOStdReq *r = request();
	if (!isSend())
	{
		r->io_Command = command;
		r->io_Length = length;
		r->io_Data = data;
		r->io_Offset = offset;
		IORequestC::sendIO();
	};
}

// ***************************************************************
