/*  circbuf.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include "debug.h"
#include "circbuf.h"
#include "cgidbg.h"
#include "yacintfc.h"

CircBuf::CircBuf(int32 Sz, int NumReaders):
	Buffer(CircBufferType,Sz) 
{
	// LogOut << "CircBuf::CircBuf(" << Sz << ", " << NumReaders << ")\n" ;
	Size = Sz ;
	Base = NEW(MachWord,Size);
	End = Base + Size;
	NoReaders = NumReaders;
	CurrentReads = new int32[NumReaders];
	TheReaderState = new BufferState[NumReaders];
	Reset();
	// LogOut << "::CircBuf exit\n" ;
}

CircBuf::~CircBuf()
{
	delete Base ;
	delete CurrentReads ;
	delete TheReaderState ;
	Base = 0;
	CurrentReads = 0;
}


ErrCode CircBuf::Reset()
{
	CurrentWrite = 0 ;
	CurrentRead = 0 ;
	for (int i = 0 ; i < NoReaders;i++) {
		CurrentReads[i] = 0 ;
		TheReaderState[i] = BufferStateOK ;
	}
	return OK ;
}

void CircBuf::CheckValidRead(int ReadChannel )
{
	if (CurrentReads[ReadChannel] == CurrentWrite)
		if (TheReaderState[ReadChannel] != BufferStateFull) {
			State.Error("buffer wrap on read");
/*
 *			LogOut << "Size = " << Size << ", rd = " << CurrentRead <<
 *				", wr = " << CurrentWrite << "\n" ;
 *			LogOut << "CR[" << ReadChannel << "] = " <<
 *				CurrentReads[ReadChannel] << ", readers = " <<
 *				NoReaders << "\n" ;
 */
	}
}

const MachWord * CircBuf::GetReadPtr(int chan)
{
	ReadIndexCheck(chan,"GetReadPtr");
	return CurrentReads[chan] + Base ;
}

void CircBuf::UpdateRead(int32 Size, int chan)
		// Add Size words to read pointer
{
	if (!Size) return ;
	ReadIndexCheck(chan, "UpdateRead");
	if (GetAvailableData(chan) < Size) {
		char size[32];
		strcpy(size,dec(Size));
		char avail[32];
		strcpy(avail,dec(GetAvailableData(chan)));
		char chn[32];
		strcpy(chn,dec(chan));
		State.Error("attempt to update read of ", size,
			" on channel ", chn, "\n on which only ",
			avail, " samples are available"); 
/*
 *		LogOut << "Size = " << Size << ", Available[" << chan <<
 *			"] = " << GetAvailableData(chan) << "\n" ;
 */
		// DbgError("CircBuf:UpdateRead", "invalid read");
	}
	int32& ReadPt = CurrentReads[chan] ;
	ReadPt += Size ;
/*
 *	LogOut << "UpdateRead(" << Size << ", " << chan <<
 *		") - CurrentReads = " <<
 *		CurrentReads[chan] << "\n" ;
 */
	ResetStateAfterRead(chan);
}

void CircBuf::UpdateWrite(int32 Size)	// Add Size words to write pointer
{
	if (GetSpace() < Size) {
		char size[32];
		strcpy(size,dec(Size));
		char avail[32];
		strcpy(avail,dec(GetSpace()));
		State.Error("attempt to update write of ", size,
			" with only ", avail, " samples available"); 
	}
	CurrentWrite += Size ;
	if (CurrentWrite >= GetSize()) CurrentWrite -= GetSize() ;
	for (int i = 0 ; i < NoReaders; i++)
		if (CurrentWrite == CurrentReads[i])
			TheReaderState[i] = BufferStateFull ;
}

void CircBuf::CheckValidWrite()
{
	for (int i = 0 ; i < NoReaders; i++ ) 
		if (CurrentReads[i] == CurrentWrite)
			if (TheReaderState[i] != BufferStateFull)
				State.Error("buffer wrap on write");
				// DbgError ("CircBuf::CheckValidWrite",
				//	"Buffer wrap on write");
}

void CircBuf::ResetStateAfterRead(int Channel)
{
	if (CurrentReads[Channel] >= GetSize())
		CurrentReads[Channel] -= GetSize() ;
	TheReaderState[Channel] = BufferStateOK;
}

void CircBuf::ResetStateAfterWrite()
{
	for (int i = 0 ; i < NoReaders;i++)
		if (CurrentReads[i] == CurrentWrite)
			TheReaderState[i] = BufferStateFull;
	CheckValidWrite() ;
}

void CircBuf::WriteCxWord (CxMachWord Wrd)
{
	MachWord Wrd1 = MachReal(Wrd) ;
	MachWord Wrd2 = MachImag(Wrd) ;
	if (CurrentWrite >= GetSize()) CurrentWrite = 0;
	*(Base+CurrentWrite++) = Wrd1 ;
	if (CurrentWrite >= GetSize()) CurrentWrite = 0;
	ResetStateAfterWrite();
	*(Base+CurrentWrite++) = Wrd2 ;
	if (CurrentWrite >= GetSize()) CurrentWrite = 0;
	ResetStateAfterWrite();
}

void CircBuf::WriteBinary (BinMachWord Wrd)
{
	*(((BinMachWord *)Base)+CurrentWrite++) = Wrd ;
	if (CurrentWrite >= GetSize()) CurrentWrite = 0;
	ResetStateAfterWrite();
}

void CircBuf::WriteWord (MachWord Wrd)
{
	*(Base+CurrentWrite++) = Wrd ;
	if (CurrentWrite >= GetSize()) CurrentWrite = 0;
	ResetStateAfterWrite();
}

void CircBuf::ReadIndexCheck(int ReadIndex, char * Routine)
{
	if (ReadIndex >= NoReaders || ReadIndex < 0)  {
		char ri[32];
		strcpy(ri,dec(ReadIndex));
		State.Error("invalid read index ", ri);
/*
 *		LogOut << "Invalid read index in routine Circbuf::"
 *			<< Routine << "\n" ;
 */
		// DbgError(Routine,"invalid read index","CircBuf") ;
	}
}

CxMachWord CircBuf::ReadCxWord ( int ReadIndex)
{
	ReadIndexCheck(ReadIndex, "ReadCxWord");
	int32& ReadPt = CurrentReads[ReadIndex] ;
	CheckValidRead(ReadIndex) ;
	if (State.IsError()) return 0 ;
	MachWord Word1 = *(Base + ReadPt++) ;
	ResetStateAfterRead(ReadIndex);
	CheckValidRead(ReadIndex) ;
	if (State.IsError()) return 0 ;
	MachWord Word2 = *(Base + ReadPt++) ;
	ResetStateAfterRead(ReadIndex);
	CxMachWord Value(Word1,Word2);
	// cout << "CircBuf::ReadCxWord(" << ReadIndex << ") - ReadPt = " <<
	// 	ReadPt << ", Value = " << Value << "\n" ;
	return Value ;
}

BinMachWord CircBuf::ReadBinary ( int ReadIndex)
{
	ReadIndexCheck(ReadIndex, "ReadBinary") ;
	int32& ReadPt = CurrentReads[ReadIndex] ;
	CheckValidRead(ReadIndex);
	if (State.IsError()) return 0 ;
	BinMachWord Value = *(((BinMachWord *)Base) + ReadPt++) ;
	ResetStateAfterRead(ReadIndex);
	// cout << "CircBuf::ReadWord(" << ReadIndex << ") - ReadPt = " <<
	// 	ReadPt << ", Value = " << Value << "\n" ;
	return Value ;
}

MachWord CircBuf::ReadWord ( int ReadIndex)
{
	ReadIndexCheck(ReadIndex, "ReadWord") ;
	int32& ReadPt = CurrentReads[ReadIndex] ;
	CheckValidRead(ReadIndex);
	if (State.IsError()) return 0 ;
	MachWord Value = *(Base + ReadPt++) ;
	ResetStateAfterRead(ReadIndex);
	// cout << "CircBuf::ReadWord(" << ReadIndex << ") - ReadPt = " <<
	// 	ReadPt << ", Value = " << Value << "\n" ;
	return Value ;
}

int32 CircBuf::GetSpace ()
{
	int32 MaxSpace = GetSize() ;
	// LogOut << "GetSpace - Size = " << GetSize() << "\n" ;
	int32 AvaSpace = MaxSpace ;
	for (int i = 0; i < NoReaders ; i++ ) {
		AvaSpace = CurrentReads[i] - CurrentWrite ;
		if (!AvaSpace) if (!IsFull(i)) AvaSpace = GetSize();
		if (AvaSpace < 0) AvaSpace += GetSize();
		if (MaxSpace > AvaSpace) MaxSpace = AvaSpace ;
	}
/*
 *	LogOut << "CircBuf::GetSpace - MaxSpace = " << MaxSpace <<
 *		", NoReaders = " << NoReaders  << "\n" ;
 */
	return MaxSpace ;
}

int32 CircBuf::GetAvailableData (int channel)
{
	int32 AvaData = CurrentWrite - CurrentReads[channel] ;
	if (!AvaData) if (IsFull(channel)) return GetSize();
	if (AvaData < 0) AvaData += GetSize();
	return AvaData ;
}

int32 CircBuf::GetReadIndexOfFullestBuffer()
{
	int Fullest = 0 ;
	int Space = GetAvailableData(0) ;
	int LocalSpace ;
	for (int i = 1 ; i < NoReaders; i++) {
		if ((LocalSpace=GetAvailableData(i)) > Space) {
			Space = LocalSpace ;
			Fullest = i ;
		}
	}
	return Fullest ;
}

int32 CircBuf::GetContiguousSpace ()
{
	int FullestIndex = GetReadIndexOfFullestBuffer();
	if (IsFull(FullestIndex)) return 0 ;
	int AvaSpace = CurrentReads[FullestIndex] - CurrentWrite ;
	if (AvaSpace <= 0) AvaSpace = GetSize() - CurrentWrite ;
	return AvaSpace ;
}

int32 CircBuf::GetContiguousAvailableData (int channel)
{
	// Must leave one word unread
	int32 AvaData = CurrentWrite - CurrentReads[channel] ;
	if (!AvaData) if (!IsFull(channel)) return 0 ; // Buffer is empty
	if (AvaData <= 0)  AvaData = Size - CurrentReads[channel] ;
	
/*
 *	LogOut << "GetContiguousAvailableData: Write = " << CurrentWrite <<
 *		", Read = " << CurrentReads[channel] << "\n" ;
 *	LogOut << "AvaData = " << AvaData << "\n" ;
 */
	return AvaData ;
}
		
