/*  dfnode3.C   */
/*  Copyright 1990 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <string.h>
#include <stream.h>
#include <fstream.h>
#include <ctype.h>

#include "portable.h"
#include "debug.h"
#include "outtok.h"
#include "cgidbg.h"

#include "network.h"
#include "netlnk.h"
#include "intfc.h"
#include "yacintfc.h"
#include "strmstr.h"
#include "dskchnhd.h"
#include "netcnt.h"
#include "buffer.h"
#include "travparm.h"
#include "tardb.h"
#include "thread.h"
#include "execseq.h"
#include "tarnodpar.h"
#include "bufstat.h"
#include "mktarget.h"


class ExecutionSequence ;

void DfNode::Init()
{
	delete_if_unlinked(no_delete_if_unlinked);
	TheDfNodeOptions = ClearDfNodeOptions ;
	NodeState = OK ;
	History = 0 ;
	Flags = ClearBits ;
	TimingVisitChannel = -1;
	UseInLink = -1;
	ActiveNet = 0 ;
	was_target_created = 0 ;
	TargetParameters = 0 ;
/*
 *	if (In) InLinks = new DfNodeInLink [In];
 *	else InLinks = 0 ;
 *	if (Out) OutLinks = new DfNodeOutLink [Out];
 *	else OutLinks = 0 ;
 */
}

DfNode::DfNode(const char * name, int16 in, int16 out, int32 EltSz,
	int32 NodeDelay, int32 DeltaIn, int32 DeltaOut,
	int32 Overlap, int32 DelayIn, int32 EltSzOut,
	int32 BlockSize,TimingType type):TargetNode(name,in,out)
{
	// LogOut << "Constructing: `" << GetName() << "'\n" ;
/*
 *	LogOut << "EltSz = " << EltSz << ", EltSzOut = " << EltSzOut <<
 *		"\n" ;
 *	LogOut << "DfNode - DeltaIn = " << DeltaIn << "\n" ;
 */
	In=in;
	Out=out;
	Delay = NodeDelay ;
	Init();
	for (int i = 0 ; i < In ; i++) {
		InLinks[i].Init();
		if (EltSz) InLinks[i].Set(EltSz,DeltaIn,Overlap,
			DelayIn + NodeDelay * DeltaIn);
	}
	if (!EltSzOut) EltSzOut = EltSz ;
	if (!In) { // force parameters to be set for signal generators
		if (!BlockSize) BlockSize =1 ;
		if (!EltSzOut) EltSzOut = 1 ;
	} else if (BlockSize < 2) BlockSize = 0 ; // CHANGE LATER
	for (i = 0 ; i < Out ; i++) {
		OutLinks[i].Init();
		if (EltSz) OutLinks[i].Set(EltSzOut,DeltaOut,
			BlockSize, type);
	}
}

DfNode::DfNode(const char * name, int16 in, int16 out,
		StreamStr * stream_str_in, StreamStr * stream_str_out,
		int32 NodeDelay, int32 DeltaIn, int32 DeltaOut,
		int32 Overlap, int32 DelayIn, TimingType type):
			TargetNode(name,in,out)
{
	// LogOut << "Constructing b: `" << GetName() << "'\n" ;
/*
 *	LogOut << "DfNode - DeltaIn = " << DeltaIn << "\n" ;
 */
	In=in;
	Out=out;
	// LogOut << "In = " << In << ", Out = " << Out << "\n" ;
	Delay = NodeDelay ;
	Init() ;
	int EltSz = 0 ;
	for (int i = 0 ; i < In ; i++) {
		InLinks[i].Init();
		if (stream_str_in) InLinks[i].Set(
			new StreamStr(*stream_str_in), DeltaIn,Overlap,
			DelayIn + NodeDelay * DeltaIn);
	}
	// LogOut << "InLinks Set\n" ;
	for (i = 0 ; i < Out ; i++) {
		OutLinks[i].Init();
		if (stream_str_out) OutLinks[i].Set(
			DeltaOut,new StreamStr(*stream_str_out),type);
	}
	// LogOut << "OutLinks Set\n" ;
	delete stream_str_in ;
	delete stream_str_out ;
}

void DfNode::SetElementSizeIn(int InChannel, int32 ElementSize)
{
	if (GetActiveNet()) {
		State.Error(
		"attempt to change the element size in connected node `",
			GetName(), "'");
		return ;
	}
	if ( InChannel < 0 || InChannel >= In) {
		State.Error( "bad input channel ",Dec(InChannel), " in node `",
			GetName(), "'");
		return ;
	}
	InLinks[InChannel].SetElementSize(GetName(),ElementSize);
}
	
void DfNode::SetElementSizeOut(int OutChannel, int32 ElementSize)
{
	if (GetActiveNet()) {
		State.Error(
		"attempt to change the element size in connected node `",
			GetName(), "'");
		return ;
	}
	if ( OutChannel < 0 || OutChannel >= Out) {
		State.Error( "bad output channel ", Dec(OutChannel),
			" in node `", GetName(), "'");
		return ;
	}
	OutLinks[OutChannel].SetElementSize(GetName(),ElementSize);
}
	
int DfNodeInLink::CheckStreamStr(const StreamStr * stream_str)
{
	StreamStr::CheckStream Check =
		TheStreamStructure->CheckInput(stream_str);
	switch (Check) {
case StreamStr::CheckStreamError :
		// LogOut << "DfNodeInLink::CheckStreamStr error\n" ;
		return 0 ;
case StreamStr::CheckStreamNoChange :
case StreamStr::CheckStreamOk :
		return 1 ;
	}
	DbgError("DfNodeInLink::CheckStreamStr","bad switch");
	return 0 ;
}

void DfNode::AdjustOutputLinks(const StreamStr * strm_str)
{
/*
 *	LogOut << "Adjusting Output for `" << GetName() << "'.\n" ;
 *	LogOut << "EltSz = " << strm_str->GetElementSize() <<
 *		", BlkSz = " << strm_str->GetBlockSize() << ".\n" ;
 *	LogOut << "SampleRateFactor = " << strm_str->GetSampleRateFactor() <<
 *		"\n" ;
 */
	for (int i = 0 ; i < In ; i++) 
		InLinks[i].Adjust(strm_str);
	for (i = 0 ; i < Out ; i++) 
		OutLinks[i].Adjust(strm_str);
}

void DfNode::SetInLink(DfNodeLink * lk, int i, int OutputChan)
{
	// LogOut << "SetInLink called for `" << GetName() << "'\n" ;
	if (State.IsError()) return ;
	if (i >= In || i< 0) {
		State.Error("invalid input channel in node ",
			GetName());
		return ;
	}
	if (InLinks[i].GetInLink())  {
		State.Error("requested input channel in node `",
			GetName(), "' is in use");
		return ;
	}
	const StreamStr * DriverStr = lk->GetDriverStreamStr();
	if (!DriverStr) {
		State.Error("node input link specified to node `",
			GetName(), "'");
		return ;
	}
	InLinks[i].SetLink(lk,OutputChan);
	int no_error = InLinks[i].CheckStreamStr(DriverStr);
	if (no_error) AdjustOutputLinks(DriverStr);
	input_linked(i);
	if (State.IsError() || !no_error) {
		*Output + OutputHelp << "Error in input links of `"
			<< GetName() << "'.\n" ;
		InLinks[i].UnlinkChannel();
		lk->UnlinkDriven(OutputChan);
		return ;
	}
}

void DfNodeInLink::SetElementSize(const char * Name, int32 EltSz)
{
	if (State.IsError()) return ;
	if (!TheStreamStructure) init_stream(new StreamStr(EltSz));
	else if (TheStreamStructure->Set(EltSz) ||
		InitStreamStructure->Set(EltSz)) 
		State.Error("initialization error in node `", Name, "'");
}

void DfNodeOutLink::SetElementSize(const char * Name, int32 EltSz)
{
	if (State.IsError()) return ;
	if (!TheStreamStructure) init_stream(new StreamStr(EltSz));
	else if (TheStreamStructure->Set(EltSz) ||
		InitStreamStructure->Set(EltSz)) 
		State.Error("initialization error in node `", Name, "'");
}

void DfNodeOutLink::Set(int32 DeltaOut, StreamStr * stream_str,
	TimingType type)
{
	if (State.IsError()) return ;
	init_stream(stream_str) ;
	IncrementOut = DeltaOut;
	timing_type = type ;
	// LogOut << "DfNodeOutLink::Set, timing_type = " << timing_type << "\n" ;
}

void DfNodeOutLink::Set(int32 EltSz,int32 DeltaOut,
	int32 block_size, TimingType type)
{
	if (State.IsError()) return ;
	if (!TheStreamStructure) init_stream(
		new StreamStr(EltSz, block_size));
	else if (TheStreamStructure->Set(EltSz,block_size) ||
		InitStreamStructure->Set(EltSz,block_size)) 
		State.Error("initialization error");
	IncrementOut = DeltaOut;
	timing_type = type ;
	// LogOut << "DfNodeOutLink::Set, timing_type = " << timing_type << "\n" ;
}

void DfNodeInLink::Set(StreamStr * stream_str,int32 DeltaIn,
	int32 Ovlp, int32 Del)
{
	if (State.IsError()) return ;
	init_stream(stream_str) ;
 	IncrementIn = DeltaIn;
	Overlap = Ovlp;
	Delay = Del;
}

void DfNodeInLink::Set(int32 EltSz,int32 DeltaIn,
	int32 Ovlp,int32 Del)
{
	if (State.IsError()) return ;
	if (!TheStreamStructure) init_stream(new StreamStr(EltSz));
	else if (TheStreamStructure->Set(EltSz,0) ||  // CHANGE LATER
		InitStreamStructure->Set(EltSz,0))
		State.Error("initialization error");
/*
 *	if (ElementSize && EltSz) {
 *		State.Error(
 *		"attempt to initialize input link parameters twice in node `",
 *			Name, "'");
 *		return ;
 *	}
 *	ElementSize = EltSz;
 */
	
 	IncrementIn = DeltaIn;
	Overlap = Ovlp;
	Delay = Del;
}

void DfNode::StreamStrReset(StreamStr * )
{
}

const StreamStr * DfNodeLink::GetDriverStreamStr() const
{
	if (!InputLink) return 0;
	return InputLink->GetDriverStreamStr();
}

const StreamStr * DfNodeIn::GetDriverStreamStr() const
{
	if (!GetNode()) return 0;
	return GetNode()->GetOutputStreamStr(Index);
}

const StreamStr * DfNode::GetOutputStreamStr(int Channel) const
{
	if (!GetOutLink(Channel)) return 0 ;
	return GetOutLink(Channel)->GetStreamStr();
}

NodeOutChannelHeader * DfNode::GetOutDiskState(int Channel)
{
	// LogOut << "GetOutDiskState for `" << GetName() << "'\n" ;
	if (!CkOut(Channel," to read timing ")) return 0 ;
	return GetOutLink(Channel)->GetDiskState();
}

NodeOutChannelHeader * DfNode::GetDiskState(int Channel)
{
	// LogOut << "DfNode::GetDiskState for `" << GetName() << "'\n" ;
	if (!CkIn(Channel," to read timing ")) return 0 ;
	return GetDriverNode(Channel)->GetOutDiskState();
}

void DfNode::SetInputFromDisk(const NodeOutChannelHeader& DiskState,
	int Channel)
{
	if (!CkIn(Channel," to initialize timing ")) return ;
	GetInLink(Channel)->SetFromDisk(DiskState);
}

void DfNode::SetFromDisk(const NodeOutChannelHeader& DiskState, int Channel)
{
	if (!CkOut(Channel," to initialize timing ")) return ;
	GetOutLink(Channel)->SetFromDisk(DiskState);
}

void DfNodeInLink::Adjust(const StreamStr * strm_str)
{
/*
 *	LogOut << "In Values to adjust: EltSz = " <<
 *		TheStreamStructure->GetElementSize() << ", BlkSz = " <<
 *		TheStreamStructure->GetBlockSize() << "\n" ;
 *	LogOut << "Input SampleRateFactor = " << strm_str->GetSampleRateFactor()
 *		<< ", Output is " << TheStreamStructure->GetSampleRateFactor()
 *			<< "\n" ;
 */
	TheStreamStructure->AdjustInput(strm_str);
}

NodeOutChannelHeader * DfNodeInLink::GetDiskState()
{
	if (!TheStreamStructure) DbgError("DfNodeOutLink::GetDiskState","null");
	// LogOut << "DfNodeInLink::GetDiskState - timing:\n" ;
	// GetTiming()->Display();
	return new NodeOutChannelHeader(*(GetTiming()), *TheStreamStructure);
}

void DfNodeInLink::SetFromDisk(const NodeOutChannelHeader& DiskState)
{
	if (!TheStreamStructure) DbgError("DfNodeOutLink::SetFromDisk","null");
	// LogOut << "Setting Inlink to:\n" ;
	// DiskState.Dump(LogOut);
	TheStreamStructure->SetFromDisk(DiskState);
	InitStreamStructure->SetFromDisk(DiskState);
	// TheStreamStructure->Display();
	// Timing.SetFromDisk(DiskState);
	// Timing.Display();
}

NodeOutChannelHeader * DfNodeOutLink::GetDiskState()
{
	// LogOut << "DfNodeOutLink::GetDiskState - timing:\n" ;
	// GetTiming()->Display();
	if (!TheStreamStructure) DbgError("DfNodeOutLink::GetDiskState","null");
	return new NodeOutChannelHeader(Timing, *TheStreamStructure);
}

void DfNodeOutLink::SetFromDisk(const NodeOutChannelHeader& DiskState)
{
	if (!TheStreamStructure) DbgError("DfNodeOutLink::SetFromDisk","null");
	// LogOut << "Setting Outlink to:\n" ;
	// DiskState.Dump(LogOut);
	TheStreamStructure->SetFromDisk(DiskState);
	InitStreamStructure->SetFromDisk(DiskState);
	// TheStreamStructure->Display();
	Timing.SetFromDisk(DiskState);
	// Timing.Display();
}

void DfNodeOutLink::Adjust(const StreamStr * strm_str)
{
/*
 *	LogOut << "Out values to adjust: EltSz = " <<
 *		TheStreamStructure->GetElementSize() << ", BlkSz = " <<
 *		TheStreamStructure->GetBlockSize() << "\n" ;
 *	LogOut << "Input SampleRateFactor = " << strm_str->GetSampleRateFactor()
 *		<< ", Output is " << TheStreamStructure->GetSampleRateFactor()
 *			<< "\n" ;
 */
	TheStreamStructure->AdjustInput(strm_str);
}

const char * DfNode::GetCaption() const
{
	// LogOut << "Base function `GetCaption()' called for `"<<GetName()<<"'\n";
	return 0;
}

const char * DfNode::GetNetName() const
{
	if (!GetActiveNet()) return "NOT CONNECTED TO A NETWORK" ;
	return GetActiveNet()->GetName();
}

int DfNode::net_complete()
{
/*
 *	LogOut << "DfNode::net_complete\n" ;
 *	LogOut << GetName() << "\n" ;
 */
	if (!GetActiveNet()) return 0 ;
	NetControl * Controller = GetActiveNet()->GetTheController();
	if (!Controller) return 0 ;
	return Controller->is_complete();
}

int DfNode::CheckNetComplete()
{
	if (!GetActiveNet()) {
		State.Error("node `", GetName(),
			"' not connected to a network");
		return 0 ;
	}
	NetControl * Controller = GetActiveNet()->GetTheController();
	if (!Controller) {
		State.Error("node `", GetName(), "' is in network `",
			GetNetName(), "' which has no controller assigned");
		return 0 ;
	}
	return Controller->AssignBuffers();
}


void DfNode::DisplayInputTiming(int16 Channel)
{
	if (GetIn() < Channel) {
		*Output + OutputHelp << "Node `" << GetName() <<
			"' has only " << GetIn() << " input channels.\n";
		return ;
	}
	if (!CheckNetComplete()) {
		*Output + OutputHelp << "Timing is not available for node `"
			<< GetName() << "' because\n" ;
		*Output << "timing cannot be assigned to network `"
			<< GetNetName() << "'.\n" ;
		return ;
	}
	*Output +OutputHelp << "Input timing for `" << GetName()
		<< "':\n" ;
	NodeOutChannelHeader * Head = GetDiskState();
	Head->Display(OutputHelp);
	delete Head ;
}

void DfNode::DisplayOutputTiming(int16 Channel)
{
	if (GetOut() < Channel) {
		*Output + OutputHelp << "Node `" << GetName() <<
			"' has only " << GetOut() << " input channels.\n" ;
		return ;
	}
	if (!CheckNetComplete()) {
		*Output +OutputHelp << "Timing is not available for node `"
			<< GetName() << "'.\n" ;
		*Output << "because timing cannot yet be assigned to network `"
			<< GetNetName() << "'.\n" ;
		return ;
	}
	*Output +OutputHelp << "Output timing for `" << GetName()
		<< "':\n" ;
	NodeOutChannelHeader * Head = GetOutDiskState();
	Head->Display(OutputHelp);
	delete Head ;
}

int32 DfNode::GetInDataPerExe(int Channel) const
{
	return  GetInLink(Channel)->GetDataPerExe();
}

int32 DfNode::GetOutDataPerExe(int Channel) const
{
	if (!CkOut(Channel,"get chunk size")) return 0 ;
	return  GetOutLink(Channel)->GetDataPerExe();
}

Buffer* DfNode::GetInputBuffer(int chan) const
{
	DfNodeLink * TheLink = 0 ;
	DfNodeInLink * TheInLink = GetInLink(chan) ;
	if (TheInLink) TheLink = TheInLink->GetInLink() ;
	if (TheLink) return TheLink->GetDataBuffer();
	return 0 ;
}

Buffer* DfNode::GetOutputBuffer(int chan) const
{
	DfNodeLink * TheLink = 0 ;
	DfNodeOutLink * TheOutLink = GetOutLink(chan) ;
	if (TheOutLink) TheLink = TheOutLink->GetOutLink() ;
	if (TheLink) return TheLink->GetDataBuffer();
	return 0 ;
}

int32 DfNode::GetInDataPerExe(int chan)
{
	if (!CkIn(chan,"get chunk size")) return 0 ;
	return InLinks[chan].GetDataPerExe();
}

TraverseResult DfNode::WriteObject(TraverseObject& obj)
{
	// LogOut << "DfNode::WriteObject for `" << GetName() << "'\n" ;
	const char * ClassName = GetClassName();
	if (!ClassName) State.Warning("cannot get class name for object `",
		GetName(), "'");
	else {	
/*
 *		LogOut << "DfNode::WriteObject for class `" <<
 *			ClassName << "'\n" ;
 */
		NamePair * Pair =
			TargetDataBase->FindValue(ClassName);
		if (Pair) {
			if (!Pair->AlreadyOutput) {
				obj.GetHeaderOut() << "#include \"" <<
					Pair->Value << "\"\n" ;
				Pair->AlreadyOutput=1;
			}
		} else State.Warning("cannot find header file name for node `",
			GetName(), "'");
	}
	if (!SaveState(obj.GetOut(), CppListTargetCtor)) return TraverseError ;
	EmitTargetCode * EmitTarget = obj.GetParameters()->GetEmitTargetCode() ;
	if (EmitTarget) EmitTarget->GetNodeList() << "extern class " <<
		ClassName << " " << GetName() << ";\n" ;
	return TraverseOK ;
}

void DfNode::EmitTargetCtor(OutTokens&)
{
}


int DfNode::AllInputsTraversed() const
{
	// LogOut << "AllInputsTraversed for " << GetName() << "\n" ;
	for (int i = 0 ; i < In ; i++) {
/*
 *		LogOut << i << ":AllInputsTraversed for " << GetName() <<
 *			" NO\n" ;
 */
		if (!GetInLink(i)->IsTraverseComplete()) return 0 ;
	}
	// LogOut << "AllInputsTraversed for " << GetName() << " YES\n" ;
	return 1;
}

int DfNode::AllOutputsTraversed() const
{
	// LogOut << "AllOutputsTraversed for `" << GetName() << "\n" ;
	for (int i = 0 ; i < Out ; i++) 
		if (!GetOutLink(i)->IsTraverseComplete()) {
/*
 *			LogOut << "AllOutputsTraversed for `" << GetName() 
 *				<< "' returning 0.\n" ;
 */
			return 0 ;
		}
/*
 *	LogOut << "AllOutputsTraversed for `" << GetName() <<
 *		"' returning 1.\n" ;
 */
	return 1;
}

/*
TraverseResult DfNode::SetTargetStateBack(TraverseObject& obj)
{
	// Check to see all output channels are done
	for (int i = 0 ; i < Out ; i++) if (!GetOutLink(i)->IsTargetDefined())
		return TraverseNotDone ;
	// 
}

TraverseResult DfNode::SetTargetState(TraverseObject& obj)
{
	int i ;
	BufferStatus& Status = *(obj.GetBufferStatus());
	// Check to see all input channels are defined if not clear
	// visit flag and return
	for (i = 0 ; i < In ;i++) if (!GetInLink(i)->IsTargetDefined())
		return TraverseNotDone ;

	
	// Compute reader parameters  for output channels
	for (i = 0 ; i < Out ; i ++) Status.Update(GetOutLink(i));
	return TraverseOK ;
}
*/

static TraverseResult ConvertToTraverse(TargetAdjustState StateOut)
{
	switch (StateOut) {
case TargetAdjustOK:
case TargetAdjustChange:
case TargetAdjustMajorChange:
		return TraverseOK ;
case TargetAdjustFail:
		return TraverseError ;
default:
		TheLog << "DbgError::StateOut = " << (int) StateOut << "\n" ;
		DbgError("ConvertToTraverse","bad type");
	}
	return TraverseError  ;
}

TargetAdjustState DfNode::ProcessConsumerSequences(
	ProcessConsumerSequence Process, class ExecutionSequence& Seq,
	TraverseObject& obj)
{
	// LogOut << "InProcessConsumerSequences for " << GetName() << "\n" ;
	TargetAdjustState StateOut ;
	do  {
		StateOut = TargetAdjustOK ;
		for (int OutChan = 0 ; OutChan < Out ; OutChan ++) {
			// LogOut<< "Outer loop OutChan = " << OutChan << "\n" ;
			DfNodeOutLink * OutLink = GetOutLink(OutChan);
			DfNodeLink * Link = OutLink->GetOutLink();
			int ConsumerCount = Link->GetOutSize();
			if (ConsumerCount < 1) {
				State.Error("output channel for `", GetName(),
					"' has no reader");
				break ;
			}
			for (int Reader = 0 ; Reader < ConsumerCount; Reader++ )
			{
/*
 *				LogOut << "ProcessConsumerSequences - " <<
 *					Reader	<< "\n" ;
 *				LogOut << "Link = 0x"  << Link << "\n" ;
 *				LogOut << "Link->GetDfNodeOut(" << Reader <<
 *					") = " << Link->GetDfNodeOut(Reader)
 *					<< "\n" ;
 */
				TargetAdjustState Temp = (Seq.*Process)
					(Link->GetDfNodeOut(Reader),obj);
/*
 *				LogOut << "Seq.*Process returning " << Temp
 *					<< "\n" ;
 */
				if (Temp >= TargetAdjustFail) return Temp ;
				if (Temp > StateOut) StateOut = Temp ;
			}
			if (State.IsError()) StateOut = TargetAdjustFail ;
		}
	} while (StateOut >= TargetAdjustChange &&
		StateOut <= TargetAdjustMajorChange) ;
/*
 *	LogOut << "DfNode::ProcessConsumerSequences returning" <<
 *		StateOut << "\n" ;
 */
	return StateOut ;
}
	

TraverseResult DfNode::SetTargetInitSequence(TraverseObject& obj)
{
/*
 *	LogOut << "DfNode::SetTargetInitSequence `" << GetName() <<
 *		"', feedback = " << IsInFeedbackLoop() << "\n" ;
 */
	BufferStatus& Status = *(obj.GetBufferStatus());
	if (!GetTargetParameters()) {
		SetTargetParameters(new TargetNodeParameters(*this,Status));
/*
 *		LogOut << "SetTargetParameters to " << (void *)
 *			GetTargetParameters() << " for `" << GetName() << "'\n" ;
 */
	}
	class ExecutionSequence& Seq = GetTargetParameters()->GetInit() ;
	// Seq.dump();

	int out = GetOut();
	// if (out) LogOut << "Checking output timing for `" << GetName() << "'.\n" ;
	for (int i = 0 ; i < out; i++) {
		DfNodeOutLink * outlk = GetOutLink(i);
		if (!outlk) DbgError("ExecutionSequence::InitInit","null outlk");
		if (outlk->get_timing_type() != TimingTypeLinear) {
			ReasonForFailure * the_problem = new ReasonForFailure
            	("node timing is not linear",
            	GetName(),-1, i, ReasonForFailure::Output);
        	Status.SetReason(the_problem);
        	return TraverseError ;
		}
	}



	TargetAdjustState StateOut = TargetAdjustOK ;
	if (!Out) StateOut = Seq.InitInit(obj) ;
	else StateOut = ProcessConsumerSequences(
		&(ExecutionSequence::AdjustInit), Seq, obj);
/*
 *	LogOut << "DfNode::SetTargetInitSequence returning " <<
 *		StateOut << "\n" ;
 */
	// Seq.dump();
	return ConvertToTraverse(StateOut);
}

TraverseResult DfNode::ReconcileTargetInitSequence(TraverseObject& obj)
{
	// Initial nodes have sequence assigned correctly
	// we must project these forward to modify sequences
	// if needed.
	
	// LogOut << "DfNode::ReconcileTargetInitSequence for " << GetName()<<"\n";
	// Check if this sequence is consistent with all feeders

	if (!IsTargetDefined()) DbgError("DfNode::ReconcileTargetInitSequence",
		"no parameters");
/*
 *	LogOut << "GetIn() = " << GetIn() << ", GetOut() = " << GetOut() <<
 *		"\n" ;
 */
	if (!GetIn() || !GetOut()) return TraverseOK ;
	TargetAdjustState StateOut = TargetAdjustOK ;
	class ExecutionSequence& Seq = GetTargetParameters()->GetInit() ;
	// Seq.dump();
	// LogOut << "Before ProcessConsumerSequences\n" ;
	StateOut = ProcessConsumerSequences(
		&(ExecutionSequence::ReconcileInit),Seq,obj);
	// Seq.dump();
/*
 *	LogOut << "DfNode::ReconcileTargetInitSequence returning " << StateOut
 *		<< "\n" ;
 */
	return ConvertToTraverse(StateOut);
}

TraverseResult DfNode::AssignMultiples(TraverseObject& obj)
{
	// LogOut << "DfNode::AssignMultiples " << GetName() << "\n" ;
	BufferStatus& Status = *(obj.GetBufferStatus());
/*
 *	if (!IsTargetDefined()) 
 *		SetTargetParameters(new TargetNodeParameters(*this,Status));
 */
	TargetNodeParameters * Parameters = GetTargetParameters();
	if (!Parameters) DbgError("DfNode::AssignMultiples",
		" - no parameters");
	if (!GetOut()) return TraverseOK ;
	
	int32 LastCommonDivisor = 1 ;
	int32 FirstDivisor ;
	do for (int i = 0 ; i < Out ;i++) {
		Buffer * TheBuffer = GetOutLink(i)->GetOutLink()->
			GetDataBuffer();
		if (!TheBuffer) DbgError("DfNode::AssignMultiples","no buffer");

		TargetBufferParameters * BufParam =
			TheBuffer->GetTargetBuffer();
		if (!BufParam) {
			TheBuffer->AdjustTargetBuffer(1);
			BufParam = TheBuffer->GetTargetBuffer();
		}
		// LogOut << "DfNode::AssignMultiples Out = " << Out << "\n" ;
		DfNodeOutLink * Link = GetOutLink(i);
		LastCommonDivisor = BufParam->
			AdjustMultiple(Link, LastCommonDivisor,
				obj.GetBufferStatus());
		if (!LastCommonDivisor) {
			// LogOut << "DfNode::AssignMultiples Error return \n" ;
			return TraverseError ;
		}
		if (!i) FirstDivisor = LastCommonDivisor ;
	} while (LastCommonDivisor != FirstDivisor) ;
/*
 *	LogOut << "NeededCount for " << GetName() << " = " <<
 *		LastCommonDivisor << "\n" ;
 */
	Parameters->SetNeededCount(LastCommonDivisor) ;
	return TraverseOK ;
}

TraverseResult DfNode::SetTargetSequence(TraverseObject& obj)
{
/*
 *	LogOut << "DfNode::SetTargetSequence " << GetName() <<
 *		"', feedback =" << IsInFeedbackLoop() << "\n" ;
 */
	if (!IsTargetDefined()) DbgError("DfNode::SetTargetSequence",
		" - no parameters");
	class ExecutionSequence& Seq = GetTargetParameters()->GetExecute() ;
	// Seq.dump();

	TargetAdjustState StateOut = TargetAdjustOK ;
	if (!GetOut()) {
		StateOut = Seq.ExecuteInit(obj);
	} else {
		StateOut = ProcessConsumerSequences(
		&(ExecutionSequence::AdjustExecute),Seq,obj);
	}
	// Seq.dump();
	// LogOut<<"DfNode::SetTargetSequence - returning " << StateOut << "\n" ;
	return ConvertToTraverse(StateOut);
}

TraverseResult DfNode::ReconcileTargetSequence(TraverseObject& obj)
{
	// Initial nodes have sequence assigned correctly
	// we must project these forward to modify sequences
	// if needed.
/*	
 *	LogOut << "DfNode::ReconcileTargetSequence for " << GetName()
 *		<< "\n" ;
 */
	// Check if this sequence is consistent with all feeders

	if (!IsTargetDefined()) DbgError("DfNode::ReconcileTargetSequence",
		"no parameters");
/*
 *	LogOut << "GetIn() = " << GetIn() << ", GetOut() = " << GetOut() <<
 *		"\n" ;
 */
	if (!GetOut()) return TraverseOK ;
	TargetAdjustState StateOut = TargetAdjustOK ;
	class ExecutionSequence& Seq = GetTargetParameters()->GetExecute() ;
	// Seq.dump();
	// LogOut << "Before ProcessConsumerSequences\n" ;
	StateOut = ProcessConsumerSequences(
		&(ExecutionSequence::ReconcileExecute),Seq,obj);
	// Seq.dump();
/*
 *	LogOut << "DfNode::ReconcileTargetSequence returning " << StateOut
 *		<< "\n"  ;
 */
	return ConvertToTraverse(StateOut);
}


TraverseResult DfNode::WriteTargetBuffers(TraverseObject& obj)
{
	// LogOut << "DfNode::WriteTargetBuffers for `" << GetName() << "'\n" ;
	BufferStatus * status = obj.GetBufferStatus();
	if (status->GetGoal() != BufferStatus::ExistingSize)
		GetTargetParameters()->EmitSequences(obj);
	// LogOut << "after EmitSequences\n" ;
	// Emit output buffer
	for (int i = 0 ; i < GetOut(); i++) {
		// LogOut << "Construct info\n" ;
		BufferOutInfo Info(obj.GetOut(),GetName(),i);
		// LogOut << "EmitTargetBuffer\n" ;
		GetOutLink(i)->EmitTargetBuffer(Info);
		// LogOut << "EmitTargetBuffer done\n" ;
	}
	return TraverseOK ;
}

TraverseResult DfNode::WriteTargetState(TraverseObject& obj)
{
	// LogOut << "DfNode::WriteTargetState for `" << GetName() << "'\n" ;
	if (!SaveState(obj.GetOut(), CppListTargetState)) return TraverseError ;
	return TraverseOK ;
}

void DfNode::ClearTraverse()
{
	if (IsTraverseFlag(GenericTraverseTouch)) {
		// LogOut << "ClearTraverse for " << GetName() << "\n" ;
		ClearTraverseFlag(GenericTraverseBit);
		ClearTraverseFlag(GenericTraverseComplete);
		ClearTraverseFlag(GenericTraverseTouch);
		for (int i = 0; i < Out ; i++) OutLinks[i].ClearTraverse();
	}
}

TraverseResult DfNode::Traverse(TraverseObject& obj)
{
	// LogOut << "Traverse " << GetName() ;
	TraverseResult Result = TraverseOK ;
	TraverseResult Temp = TraverseOK ;
	int i ;
	if (State.IsError()) return TraverseError ;
	if (IsTraverseFlag(GenericTraverseBit)) return TraverseOK ;

/*********** BEGIN SEQUENCE DEBUG CODE **********
 *	TargetNodeParameters * test = 0 ;
 *	TraverseParameters * params = obj.parameters();
 *	if (params) if (params->TheType == TraverseParameters::BufferStatusType) {
 *		test = GetTargetParameters() ;
 *		if (test) test->dump(GetName());
 *	}
 ************ END SEQUENCE DEBUG CODE **********/

	// Following test is necessary HERE to avoid infinite loop
/*
 *	LogOut << "forward_action = " << (void *) obj.forward_action()
 *		<< ", SetTerminalNodeResampling = " <<
 *		(void *) &DfNode::SetTerminalNodeResampling << "\n" ;
 */
	if (obj.backward_action() == &DfNode::SetTargetInitSequence)
		if (IsInFeedbackLoop()) {
/*
 *		LogOut << "DfNode::Traverse detected feedback in "
 *			<< GetName() << "\n" ;
 */
		obj.GetBufferStatus()->SetReason(new ReasonForFailure (
			"this node is in a feedback loop", GetName(), 0,
			ReasonForFailure::Input));
		return TraverseError ;
	}
	SetTraverseFlag(GenericTraverseBit);
/*
 *	LogOut << "A:SetTraverseFlag(GenericTraverseBit) for " << GetName()
 *		<< "\n" ;
 */
	SetTraverseFlag(GenericTraverseTouch);
	int DoAction = 1 ;
	switch (obj.GetTraverseType()) {
case TraverseObject::TraverseStandard:
		break ;
case TraverseObject::TraverseOutputNodesFirst:
		DoAction = -1 ;
		break ;
case TraverseObject::TraverseInputNodesFirst:
		// avoid infinite loop in feedback
		if (obj.GetParameters()->is_checked(this)) break ;
		obj.GetParameters()->append(this);
		if (AllInputsTraversed()) break ;
		ClearTraverseFlag(GenericTraverseBit);
/*
 *		LogOut << "A:ClearTraverseFlag(GenericTraverseBit) for " <<
 *			GetName() << "\n" ;
 */
		DoAction = 0 ;
		break ;
default:
		DbgError("DfNode::Traverse","bad type");
	}
	if (DoAction==1) {
		// LogOut << "Doing forward action from traverse.\n" ;
		TraverseResult Result = obj.DoForwardAction(*this) ;
		if (State.IsError()) return TraverseError ;
/*
 *			State.Error("while ", obj.GetDoingWhat(),
 *				" in node `", GetName(), "'");
 */
		switch (Result) {
case TraverseOK:
			SetTraverseFlag(GenericTraverseComplete) ;
/*
 *			LogOut << "SetComplete for forward action at "
 *				<< GetName() << "\n" ;
 */
			break ;
case TraverseNotDone:
			ClearTraverseFlag(GenericTraverseBit);
/*
 *			LogOut <<
 *				"B:ClearTraverseFlag(GenericTraverseBit) for "
 *				<< GetName() << "\n" ;
 */
			/*********** BEGIN SEQUENCE DEBUG CODE **********/
			// if (test) test->dump(GetName());
			/*********** END SEQUENCE DEBUG CODE **********/
			return TraverseNotDone ;
case TraverseError: 
			return TraverseError ;
		}
	} 

	for (i = 0; i < Out ; i++) {
		Temp = OutLinks[i].Traverse(obj);
		if (State.IsError()) return TraverseError ;
		if (Temp > Result) Result = Temp ;
	}
	if (DoAction == -1 )
		if (AllOutputsTraversed()) DoAction = 1 ; else DoAction =0 ;
	// if (DoAction) LogOut << "Doing action from traverse.\n" ;
	if (DoAction) switch (obj.DoBackwardAction(*this)) {
case TraverseOK:
		break ;
case TraverseNotDone:
		ClearTraverseFlag(GenericTraverseBit);
/*
 *		LogOut << "C:ClearTraverseFlag(GenericTraverseBit) for " <<
 *			GetName() << "\n" ;
 */
		/*********** BEGIN SEQUENCE DEBUG CODE **********/
		// if (test) test->dump(GetName());
		/*********** END SEQUENCE DEBUG CODE **********/
		return TraverseNotDone ;
case TraverseError: 
/*
 *		State.Error("while ",obj.GetDoingWhat(), " in node `",
 *			GetName(), "'");
 *		obj.GetBufferStatus()->CheckError();
 */
		return TraverseError ;
	}
	/*********** BEGIN SEQUENCE DEBUG CODE **********/
	// if (test) test->dump(GetName());
	/*********** END SEQUENCE DEBUG CODE **********/

	if (State.IsError()) return TraverseError ;
	if (DoAction) SetTraverseFlag(GenericTraverseComplete) ;
	return Result ;
}


void DfNodeOutLink::ClearTraverse()
{
	if (!OutLink) return ;
	OutLink->ClearTraverse();
}

TraverseResult DfNodeOutLink::Traverse(TraverseObject& obj)
{
	if (!OutLink) return TraverseOK ;
	return (OutLink->Traverse(obj));
}


ostream& TraverseObject::GetMainOut()
{
	ostream * Return = Out.GetStream();
	if (!Return) DbgError("TraverseObject::GetMainOut","no stream");
	return * Return ;
}

int DfNode::IsFeedbackHead() const
{
	for (int i = 0; i < Out; i++)
		if (GetOutLink(i)->IsFeedbackHead()) return 1 ;
	return 0 ;
}

int DfNode::IsInFeedbackLoop() const
{
	for (int i = 0; i < Out; i++)
		if (GetOutLink(i)->IsInFeedbackLoop()) return 1 ;
	return 0 ;
}

TargetBufferParameters * DfNode::GetTargetBufferIn(int InChan) const 
{
	// LogOut << "GetTargetBuffer(" << InChan << "\n" ;
	return GetInLink(InChan)->GetTargetBuffer();
}

TargetAdjustState DfNode::AdjustTargetBufferIn(int InChan, int32 NewSize) 
{
/*
 *	LogOut << "DfNoded::AdjustTargetBuffer(" << InChan << ") for `" <<
 *		GetName() << "'\n" ;
 */
	return GetInLink(InChan)->AdjustTargetBuffer(NewSize);
}

void DfNode::ClearTargetInParameters(int InChan)
{
	// LogOut << "DfNode::ClearTargetInParameters(" << InChan << ")\n" ;
	GetInLink(InChan)->ClearTargetParameters();
	// LogOut << "DfNode::ClearTargetInParameters(" << InChan << ") exit\n" ;
}

TraverseResult DfNode::ClearTargetParameters(TraverseObject& )
{
	// LogOut << "DfNode::ClearTargetParameters()\n" ;
	delete TargetParameters ;
	TargetParameters = 0 ;
	for (int i = 0 ; i  < GetIn() ; i++)
		ClearTargetInParameters(i) ;
	// LogOut << "DfNode::ClearTargetParameters() - exit\n" ;
	return TraverseOK ;
}

TraverseResult DfNode::CheckTargetBufferSize(TraverseObject& obj)
{
/*
 *	LogOut << "DfNode::CheckTargetBufferSize for `" << GetName()
 *		<< "'\n" ;
 */
	TraverseResult Return = TraverseOK ;
	BufferStatus * status = obj.GetBufferStatus() ;
	BufferStatus::Goal goal = status->GetGoal() ;
	switch (goal) {
case BufferStatus::FixedSequence:
		break ;
case BufferStatus::ExistingSize:
		if (!GetTargetParameters()) {
        	SetTargetParameters(new TargetNodeParameters(*this,*status));
/*
 *        	LogOut << "SetTargetParameters to " << (void *)
 *          	GetTargetParameters() << " for `" << GetName() << "'\n" ;
 */
		} else {
			GetTargetParameters()->ClearSequences();
		}
		break ;
default:
		DbgError("DfNode::CheckTargetBufferSize","bad case");
	}
	for (int i = 0 ; i < GetIn() ; i++) {
		// LogOut << "in = " << i<< "\n" ;
		TraverseResult Local = GetInLink(i)->CheckTargetBufferSize(obj);
		if (Local > Return) Return = Local ;
		if (Return >= TraverseError) break ;
	}
	for (i = 0 ; i < GetOut() ; i++) {
		// LogOut << "out = " << i<< "\n" ;
		TraverseResult Local=GetOutLink(i)->CheckTargetBufferSize(obj);
		if (Local > Return) Return = Local ;
		if (Return >= TraverseError) break ;
	}
	return Return ;
}

TraverseResult DfNode::EmitThreadTable(TraverseObject& obj)
{
	// 1. If this node has more than one input list all thread
	//    initial segments that feed this node.
	// 2. If the node has not already been listed then list it.
	// LogOut << "DfNode::EmitThreadTable for `" << GetName() << "'\n" ;
	if (GetIn() > 1) {
		ProcessNet * TheNet = GetActiveNet();
		if (!TheNet) DbgError("DfNode::EmitThreadTable","no table");
		ThreadList * Threads = TheNet->GetThreads();
		if (!Threads) DbgError("DfNode::EmitThreadTable","no threads");
		ThreadListIterator Next(*Threads);
		Thread * TheThread ;
		DfNodeList already_checked ;
		while(TheThread = Next()) {
			// LogOut << "TheThread = " << (void *) TheThread << "\n" ;
			DfNode * FirstNode = TheThread->GetFirstNode();
/*
 *			LogOut << "Emitting thread for `" << FirstNode->GetName() <<
 *				"'.\n" ;
 */
			if (already_checked.InList(FirstNode)) continue ;
			if (FirstNode->FindNode(this)) FirstNode->Traverse(obj);
			already_checked.Append(FirstNode);
			if (State.IsError()) return TraverseError ;
		}
		already_checked.Clear();
	}
	obj.GetHeaderOut() << "\t&" << GetName() << ",\n" ;
	// LogOut << "DfNode::EmitThreadTable for `" << GetName() << "' exit\n" ;
	return TraverseOK ;
}

NodeOutChannelHeader * TargetNode::GetDiskState(int)
{
	DbgError("TargetNode::GetDiskState","base called");
	return 0 ; // avoid warning
}

int32 DfNode::GetMinInBufferSize(int InChan) const
{
	if (!CkIn(InChan," get minimum buffer size ")) return 0 ;
	return InLinks[InChan].GetMinBufferSize();
}

int32 DfNode::GetMinOutBufferSize(int OutChan) const
{
	if (!CkOut(OutChan," get minimum buffer size ")) return 0 ;
	return OutLinks[OutChan].GetMinBufferSize();
}

