/*  bufcnt.C   */
/*  Copyright 1990 Mountain Math Software  */
/*  All Rights Reserved                    */
#include "dfnode.h"
#include "bufcnt.h"
#include "yacintfc.h"
#include "buffer.h"
#include "cgidbg.h"
#include "netlnk.h"
#ifdef INTERACTIVE

void KernelInterface::Allocate(int Size)
{
	if (Count) return ;
	if (!Size) return ;
	Count = new int32[Size] ;
	Point = new MachWord * [Size] ;
}

BufferInterface::BufferInterface(KernelInterface& kernel_interface,
	Buffer& buffer, int Chan):
	Count(kernel_interface.Count[Chan]),
	Point(kernel_interface.Point[Chan]),
	TheBuffer(buffer)
{
	Save = 0 ;
}

int32 BufferInterface::SetLimits(TargetNode&, int ) 
{
	return LoopInitialization() ;
}

int32 InLimBufferInterface::SetLimits(TargetNode& node, int Chan)
{
	DfNode& Node = (DfNode &) node ;
	WordLimit = Node.GetInDataPerExe(Chan) * Node.GetExecLimit() ;
	// TheLog << "WordLimit = " << WordLimit << "\n" ;
	return LoopInitialization();
}

int32 OutLimBufferInterface::SetLimits(TargetNode& node, int Chan) 
{
	// TheLog << "Chan = " << Chan << "\n" ;
	DfNode& Node = (DfNode &) node ;
	WordLimit = Node.GetOutDataPerExe(Chan) * Node.GetExecLimit() ;
/*
 *	TheLog << "WordLimit = " << WordLimit << "\n" ;
 *	TheLog << "DataPer = " << Node.GetOutDataPerExe(Chan) << ", Limit = "
 *		<< Node.GetExecLimit() << "\n" ;
 */
	return LoopInitialization();
}

InputBufferInterface::InputBufferInterface(NodeBufferInterface& node_intfc,
	KernelInterface& kernel_interface, int InChan):
	BufferInterface(kernel_interface,
	*(((DfNode &)(node_intfc.GetTheNode())).GetInputBuffer(InChan)), InChan)
{
	DfNodeInLink * TheLink =
		((DfNode &)(node_intfc.GetTheNode())).GetInLink(InChan);
	if (!TheLink) 
				DebugCodeError(38,"InputBufferInterface::ctor","bad in channel",0);
	Channel = TheLink->GetBufferChannelIndex();
}

OutputBufferInterface::OutputBufferInterface(NodeBufferInterface& node_intfc,
	KernelInterface& kernel_interface, int OutChan):
	BufferInterface(kernel_interface,
	*(((DfNode &)(node_intfc.GetTheNode())).GetOutputBuffer(OutChan)),
		OutChan)
{
}

InLimBufferInterface::InLimBufferInterface(NodeBufferInterface& node_intfc,
	KernelInterface& kernel_interface, int InChan):
	InputBufferInterface(node_intfc, kernel_interface, InChan)
{
	WordLeftover = 0 ;
	WordLimit = 0 ;
}

OutLimBufferInterface::OutLimBufferInterface(NodeBufferInterface& node_intfc,
	KernelInterface& kernel_interface, int OutChan):
	OutputBufferInterface(node_intfc, kernel_interface, OutChan)
{
	WordLeftover = 0 ;
	WordLimit = 0 ;
}

NodeBufferInterface::NodeBufferInterface(TargetNode& the_node, KernelInterface&
		in_intfc, KernelInterface& out_intfc): TheNode(the_node)
{
	in_intfc.Allocate(TheNode.GetIn());
	out_intfc.Allocate(TheNode.GetOut());

	TheOutputChannels = 0;
	TheInputChannels = 0 ;
	DfNode& TheDfNode = (DfNode &) TheNode ;

	int UseLimitClass = TheDfNode.GetExecuteType() == NodeExecuteFixedBound;
	// TheLog << "UseLimitClass = " << UseLimitClass << "\n" ;
	int InChannels = TheDfNode.GetIn() ;
	if (InChannels) {
		if (!in_intfc.Count || !in_intfc.Point) 
			DebugCodeError(39,"NodeBufferInterface::ctor","in intfc bad",0);
		TheInputChannels = new InputBufferInterface * [InChannels] ;	
		for (int i = 0 ; i < InChannels; i++) {
			if (UseLimitClass) TheInputChannels[i] =
				new InLimBufferInterface( *this, in_intfc, i) ;
			else TheInputChannels[i] =
				new InputBufferInterface( *this, in_intfc, i) ;
		}
	}

	int OutChannels = TheDfNode.GetOut() ;
	if (OutChannels) {
		if (!out_intfc.Count || !out_intfc.Point) 
			DebugCodeError(40,"NodeBufferInterface::ctor","out intfc bad",0);
		TheOutputChannels = new OutputBufferInterface * [OutChannels] ;	
		for (int i = 0 ; i < OutChannels; i++) {
			if (UseLimitClass) TheOutputChannels[i] =
				new OutLimBufferInterface( *this, out_intfc, i);
			else TheOutputChannels[i] =
				new OutputBufferInterface( *this, out_intfc, i); 
		}
	}
}

NodeBufferInterface::~NodeBufferInterface() 
{
	delete TheInputChannels ;
	delete TheOutputChannels ;
}

int32 BufferInterface::LoopInitialization()
{
	State.Error(
		"BufferInterface::LoopInitialization: base function called");
	return 0 ;
}

void BufferInterface::CheckCount()
{
	if (!Save) Save=Count ;
		// Execute for all available data
	else if (Count > Save) Count = Save ;
		// Limt to execution count requested
	else	// Maybe not enough data to honor request 
	    if (Count < Save) {
		State.Error("insufficient data to complete loop");
		Save = Count ;
	}
/*
 *	TheLog << "CheckCount, Count = " << Count << ", Save = "
 *		<< Save << "\n" ;
 */
}

int32 InputBufferInterface::LoopInitialization()
{
	Count = TheBuffer.GetContiguousAvailableData(Channel);
	CheckCount();
	Point = TheBuffer.GetReadPtr(Channel);
	return Count ;
}

int32 InLimBufferInterface::LoopInitialization()
{
	int32 Total = InputBufferInterface::LoopInitialization();
	if (Total > WordLimit - WordLeftover) Count = WordLimit - WordLeftover;
	return Count ;
}

int32 OutputBufferInterface::LoopInitialization()
{
	Count = TheBuffer.GetContiguousSpace();
/*
 *	TheLog << "OutputBufferInterface::LoopInitialization - Count = " <<
 *		Count << "\n" ;
 */
	CheckCount();
	Point = TheBuffer.GetWritePtr();
	return Count ;
}

int32 OutLimBufferInterface::LoopInitialization()
{
	int32 Total = OutputBufferInterface::LoopInitialization();
	if (Total > WordLimit - WordLeftover) Count = WordLimit - WordLeftover;
	// TheLog << "Count = " << Count << ", Save = " << Save << "\n" ;
	// TheLog << "Total = " << Total << "\n" ;
	return Count ;
}

int NodeBufferInterface::Reset()
{
	int InChannels = TheNode.GetIn() ;
	int i ;
	int CanExecute = 1;
	for (i = 0 ; i < InChannels; i++) 
		if (!(CanExecute=TheInputChannels[i]->SetupLoop(TheNode,i)))
			break ;
	if (!CanExecute) return 0 ;
	int OutChannels = TheNode.GetOut() ;
	for (i = 0 ; i < OutChannels; i++) 
		if (!(CanExecute=TheOutputChannels[i]->SetupLoop(TheNode,i)))
			break ;
	return CanExecute ;
}

int32 BufferInterface::GetWordLimit(TargetNode&, int)
{
	DebugCodeError(41,"BufferInterface::GetWordLimit","base called",0);
	return 0 ;
}

int32 InputBufferInterface::GetWordLimit(TargetNode& node, int Chan)
{
	return ((DfNode&)node).GetInDataPerExe(Chan);
}

int32 OutputBufferInterface::GetWordLimit(TargetNode& node, int Chan)
{
	return ((DfNode&) node).GetOutDataPerExe(Chan);
}

int32 BufferInterface::SetupLoop(TargetNode& node, int Chan)
{
	// Must first set limits for each channel if needed.
	// Cases are:
	//	- execute until data exhausted - no limits
	// 	- execute a fixed limit (there must be sufficient
	//		CONTIGUOUS data available or error reported)
	//	- execute a fixed limit (there must be sufficient
	//		data available but it need not be contiguous)
	switch (((DfNode&)node).GetExecuteType()) {
case NodeExecuteFixedBound:
		return SetLimits(node, Chan);
case NodeExecuteSpaceBound:
		break ;
case NodeExecuteFixedIgnoreSpace :
		Count = GetWordLimit(node, Chan);
		break ;
default:
		DebugCodeError(42,"BufferInterface::SetupLoop","bad case",0);
	}
	return LoopInitialization();
}

int32 BufferInterface::EndTest()
{
	State.Error(
		"BufferInterface::EndTest: base function called");
	return 0 ;
}

int32 InputBufferInterface::EndTest()
{
	TheBuffer.UpdateRead(Save - Count, Channel);
	Save = Count ;
	return LoopInitialization();
}

int32 OutputBufferInterface::EndTest()
{
/*
 *	TheLog << "OutputBufferInterface::EndTest, Save = " << Save
 *		<< ", Count = " << Count << "\n" ;
 */
	TheBuffer.UpdateWrite(Save - Count);
	Save = Count ;
	return LoopInitialization();
}


int32 NodeBufferInterface::EndTest()
{
	DfNode& TheDfNode = (DfNode &) TheNode ;
	switch (TheDfNode.GetExecuteType()) {
case NodeExecuteFixedBound:
case NodeExecuteSpaceBound:
		break ;
case NodeExecuteFixedIgnoreSpace :
		return 0 ;
default:
		DebugCodeError(43,"NodeBufferInterface::EndTest","bad case",0);
	}

	int DataLeft = 1 ;
	int InChannels = TheNode.GetIn() ;
	int i ;
	for (i = 0 ; i < InChannels; i++) 
		if (!(DataLeft=TheInputChannels[i]->EndTest())) break;
	if (!DataLeft) return 0 ;

	int OutChannels = TheNode.GetOut() ;
	for (i = 0 ; i < OutChannels; i++) 
		if (!(DataLeft=TheOutputChannels[i]->EndTest())) break ;
	return DataLeft ;
}

ErrCode NodeBufferInterface::LoopCleanUp(ErrCode err_in)
{
	EndTest();
	return err_in ;
}

void NodeBufferInterface::SetErrorState(ErrCode NewState)
{
	if (ErrorState < NewState) {
		ErrorState = NewState ;
		switch (ErrorState) {
case EndOfData :
			State.Warning("end of data in node `",
				TheNode.GetName(), "'");
			break ;
case FatalError :
			State.Error("buffer interface error in node `",
				TheNode.GetName(), "'");
			break ;
case ExecutionComplete :
case OK:
case Warning:
			break ;
default:
			
				DebugCodeError(44,"NodeBufferInterface::SetErrorState","bad case",0);
		}
	}
}
#endif
