/*  buffer.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved					*/

#include "portable.h"
#include "baseio.h"
#include "outtok.h"

#include "basic.h"
#include "network.h"
#include "intfc.h"
#include "user.h"
#include "netcnt.h"
#include "travparm.h"
#include "cgidbg.h"
#include "bufstat.h"
#include "shared.h"

#include "buffer.h"
#include "interinit.h"
#include "shrbuf.h"
#include "yacintfc.h"
#include "editnet.h"
#include "shared.h"

static EntityList * BufferNodeList =0 ;

static InteractiveEntity * IntEnt ;

class NetControlList:public SingleList {
public:
	ErrCode Insert(NetControl *nt) {return SingleList::Insert(nt);}
	ErrCode Append(NetControl *nt) {return SingleList::Append(nt);}
	NetControl * Get()   {return (NetControl *) SingleList::Get();}
	NetControl * Pop() {return (NetControl *) SingleList::Pop();}
	NetControl * GetNFromTop(int N) ;
	NetControl * GetNthEntry(int N) ;
	NetControlList(){;}
	int Size(){return SingleList::Size();}
} ;

class NetControlListIterator:public SingleListIterator {
public:
	NetControlListIterator(NetControlList& df):
		SingleListIterator((SingleList&) df){}
	NetControl * operator()()
		{return (NetControl *) Next();}
};

BufferDescript::~BufferDescript()
{
	NetControl * Con ;
	while (Con = WhereAssigned->Get()) Con->ClearBuffers();
	delete WhereAssigned;
}

int BufferDescript::clear_all_references()
{
	if (!WhereAssigned) return 1;
	NetControl * con ;
	while (con = WhereAssigned->Get())
		if (!con->clear_buffer_descriptor(this)) {
			WhereAssigned->Append(con);
			return 0;
		}
	return 1 ;
}

int BufferDescript::check_safe_delete()
{
	int Size = WhereAssigned->Size();
	if (Size) {
		if (State.IsInOverwrite()) return clear_all_references();
		OutTokens Out(OutputCppHelp);
		Out.NextFillOut("BufferDescript");
		Out.NextQuoteOut(GetName()) ;
		Out.NextFillOut(
			"is currently in use by the following controller") ;
		if (Size > 1) Out.NextConcat("s") ;
		Out.NextConcat(":") ;
		Out.NewLine();
		NetControlListIterator Next(*WhereAssigned);
		NetControl * Con ;
		int FirstTime = 1 ;
		while (Con = Next()) {
			if (!FirstTime) Out.NextConcat(",");
			Out.NextFillOut(Con->GetName()) ;
		}
		Out.NewLine();
		Out.NextFillOut(
			"You must clear buffers from the above controller") ;
		if (Size > 1) Out.NextConcat("s") ;
		Out.NextFillOut("before deleting this buffer.") ;
		Out.NewLine();
		return 0 ;
	}
	return 1;
}

BufferDescript::BufferDescript(const char * Name, BufferType Typ,
	int32 Size): UserEntity(Name)
{
	Type=Typ;
	BufferSize=Size;
	if (!BufferNodeList) BufferNodesInit() ;
	WhereAssigned = new NetControlList ;
}

Buffer::Buffer(BufferType Typ, int32 sz):
	TargetBuffer(0),
	Type(Typ),
	Size(sz)
{
	// LogOut << "Buffer::Buffer(" << Typ << "," << sz << ")\n" ;
}

void Buffer::ClearTargetParameters()
{
	delete TargetBuffer ;
	TargetBuffer = 0 ;
}


TargetBufferParameters * Buffer::GetTargetBuffer() const
{
	return TargetBuffer ;
}

TraverseResult Buffer::CheckTargetBufferSize(TraverseObject& obj, int32 Min)
{
	BufferStatus * status = obj.GetBufferStatus();
	if (status->GetGoal() == BufferStatus::ExistingSize)
		AdjustTargetBuffer(Size);
	if (!GetTargetBuffer()) DbgError("Buffer::CheckTargetBufferSize",
		"no target");
/*
 *	LogOut << "Buffer::CheckTargetBufferSize calling with "
 *		<< Size << "\n" ;
 */
	return GetTargetBuffer()->CheckTargetBufferSize(obj,Size,Min);
}

TargetAdjustState Buffer::AdjustTargetBuffer(int32 NewSize) 
{
	// LogOut << "TargetBuffer = " << TargetBuffer << "\n" ;
	if (!TargetBuffer) {
		TargetBuffer = new TargetBufferParameters(NewSize);
		return TargetAdjustOK ;
	}
	// LogOut << "Buffer::AdjustTargetBuffer(" << NewSize << ")\n" ;
	return TargetBuffer->AdjustSize(NewSize);
}



BufferStatus * BufferDescript::GetBufferStatus()
{
	DbgError("Buffer::GetBufferStatus","base function called");
	return 0 ; // avoid warning
}


static void BufferError(char * Routine)
{
	DbgError(Routine,"base function called","Buffer") ;
}

void Buffer::UpdateRead(int32 , int )
{
	BufferError("UpdateRead") ;
}
					// Add Size words to read pointer
void Buffer::UpdateWrite(int32 )// Add Size words to write pointer
{
	BufferError("UpdateWrite") ;
}

MachWord * Buffer::GetWritePtr()
{
	BufferError("GetWritePtr");
	return 0;
}

const MachWord * Buffer::GetReadPtr(int)
{
	BufferError("UpdateWrite") ;
	return 0;
}

MachWord * Buffer::GetBase()
{
	BufferError("GetBase");
	return 0;
}

MachWord * Buffer::GetEnd()
{
	BufferError("GetEnd");
	return 0;
}

CxMachWord Buffer::ReadCxWord(int )
{
	BufferError("ReadCxWord");
	return 0;
}

BinMachWord Buffer::ReadBinary(int )
{
	BufferError("ReadBinary");
	return (int16) 0;
}

MachWord Buffer::ReadWord(int )
{
	BufferError("ReadWord");
	return (int16) 0;
}

void Buffer::WriteWord(MachWord )
{
	BufferError("WriteWord");
}

void Buffer::WriteBinary(BinMachWord )
{
	BufferError("WriteBinary");
}

void Buffer::WriteCxWord(CxMachWord )
{
	BufferError("WriteCxWord");
}

int32 Buffer::GetSpace()
		// Get available buffer space
{
	DbgError("Buffer::GetSpace", "base function called") ;
	return 0;
}

int32 Buffer::GetAvailableData(int)
		// Get available buffer data
{
	DbgError("Buffer::GetAvailableData", "base function called") ;
	return 0;
}

int32 Buffer::GetContiguousSpace()
		// Get available buffer space
{
	DbgError("Buffer::GetContigousSpace", "base function called") ;
	return 0;
}

int32 Buffer::GetContiguousAvailableData(int)
		// Get available buffer data
{
	DbgError("Buffer::GetContiguousAvailableData", "base function called") ;
	return 0;
}

Buffer::~Buffer()
{
	// DbgError("Buffer::~Buffer", "base function called");
}

ErrCode Buffer::Reset()
{
	TheLog << "Base function Buffer::Reset() called\n" ;
	return FatalError ;
}

void Buffer::OutTargetName(BufferOutInfo& Info)
{
	Info.Out.NextConcatToken(Info.DriverNodeName);
	if (Info.DriverChannel) Info.Out.NextConcatToken(Info.DriverChannel);
	Info.Out.NextOutToken("xXx_Buffer");
}

void Buffer::OutTargetDataName(BufferOutInfo& Info)
{
	Info.Out.NextConcatToken("SpaceFor");
	OutTargetName(Info);
}

void Buffer::OutTargetReadersName(BufferOutInfo& Info)
{
	Info.Out.NextConcatToken("ReadersFor");
	OutTargetName(Info);
}

void Buffer::OutTargetDefinition(BufferOutInfo& Info)
{
	OutTokens& Out = Info.Out ;

	if (!GetTargetBuffer())
		DbgError("Buffer::OutTargetDefiniton","no target");
/*
 *	Out.NextOut("static MachWordType");
 *	OutTargetDataName(Info);
 *	Out.NextConcat("[");
 *	Out.NextOut(GetTargetBuffer()->GetSize());
 *	Out.NextConcat("] ;");
 *	Out.NewLine();
 */
	
	
	
	Out.NextOut("static OutputBuffer ");
	OutTargetName(Info);
	Out.NextOut(" = {");
	Out.NewLine();
	Out.NextConcat("\t(MachWord *) ");
	// OutTargetDataName(Info);
	Out.NextConcat(TheSharedBuffer.Name);
	Out.NextOut("+");
	Out.NextOut(TheSharedBuffer.GetSpace(GetTargetBuffer()->GetSize()));
	Out.NextConcat(", ");
	Out.NextOut(GetTargetBuffer()->GetSize());
	Out.NextConcat(", ");
	Out.NextOut(GetNoReaders());
	Out.NextConcat(", &");
	Out.NextOut(Info.DriverNodeName);
	Out.NextConcat(", ");
	OutTargetReadersName(Info);
	Out.NextConcat(",");
	Out.NextOut(" 0} ;");
	Out.NewLine();
	
}


void BufferDescript::AssignTo(NetControl * TheNet)
{
	WhereAssigned->Append(TheNet);
}

void BufferDescript::DeAssign(NetControl * TheNet)
{
	if (!TheNet) return ;
	// LogOut<<"DeAssign buffer descriptor from `"<<TheNet->GetName() << "'\n";
	WhereAssigned->RemoveEntry(TheNet);
}

void BufferDescript::InitArithType(ArithType::ArithTypes type)
{
}

void BufferDescript::assign_to_net_graph()
{
	int length = strlen(GetName()) + 1 ;
	if (length > MaxPacketSize) {
		*Output + OutputCppHelp << "Buffer descriptor `" << GetName() <<
			"'s name is too long. It must be less than " <<
			MaxPacketSize << "characters to assign to an edit network.\n" ;
		return ;
	}
	PacketHeader head(PacketNetworkEdit,CppEnums::set_buffer_descriptor,length);
	WriteSeg->WritePacket(head,GetName());
}

void BufferNodesInit()
{
	BufferNodeList = new EntityList ;
	IntEnt = new InteractiveEntity("BufferDescript",
		BufferNodeList,
		0,InteractiveBuffer);
	TheBuffers->Append(IntEnt) ;
}


static InitObj LocalInit(BufferNodesInit,"BufferDescript", "Buffers");

