/*  network2.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved					*/
#include <stream.h>
#include <fstream.h>
#include "network.h"
#include "buffer.h"
#include "netlnk.h"
#include "timing.h"
#include "circbuf.h"
#include "cbuf.h"
#include "outtok.h"
#include "intfc.h"
#include "user.h"
#include "cgidbg.h"
#include "thread.h"
#include "yacintfc.h"
#include "travparm.h"
#include "netcnt.h"
#include "usercom.h"
#include "cntnet.h"
#include "adjust.h"
#include "bufstat.h"
#include "mktarget.h"
#include "replc.h"
#include "dspe_app.h"
#include "mkstr.h"

// void TestAlloc(const char * msg=0);


static const char * ThisName = "DfNodeLink::AssignBuffers" ;

void DfNodeLink::DeassignBufferDescriptor(BufferDescript * des)
{
	if (descriptor == des) descriptor = 0 ;
	if (OutputArray) for (int i=0;i<OutSize;i++)
		OutputArray[i]->DeassignBufferDescriptor(des);
}

int DfNodeLink::AssignBuffers(BufferDescript * Des)
{
/*
 *	LogOut << "DfNodeLink::AssignBuffers, OutputArray = " <<
 *		(void *) OutputArray << ", Des->GetBufferSize() = " <<
 *		Des->GetBufferSize() << "\n" ; 
 *	LogOut << "Des->GetSize() = " << ((CircBufDes *) Des)->GetSize()
 *		<< " for driver " << GetInputNode()->GetName() << "\n" ;
 */
	if (!InputLink) {
		State.Error("null input link");
		return 0;
	}
	if (!OutputLinks) {
		State.Error("null output links");
		return 0;
	}
	OutSize = OutputLinks->Size() ;
	if (!OutputArray) {
		OutputArray = new DfNodeOut * [OutSize] ;
		DfNodeOutIterator Next(*OutputLinks) ;
		DfNodeOut * Out;
		int i=0;
		while (Out = Next())  OutputArray[i++] = Out ;
	}
	// LogOut << "did get i = " << i << "\n" ;
	// LogOut << "type = " <<  Des->GetType() << "\n" ;
	// LogOut << "size = " <<  Des->GetBufferSize() << "\n" ;
	if (DataBuffer) delete DataBuffer ;
	DataBuffer = 0 ;
	switch (Des->GetType()) {
case CircBufferType :
		DataBuffer = new  CircBuf(Des->GetBufferSize(), OutSize);
		break;
default:
		DbgError(ThisName,"invalid buffer type");
		break ;
	}
	descriptor = Des ;
	int RetVal = 1;
	for (int i=0;i<OutSize;i++) {
		// LogOut << "Assign link " << i << "\n" ;
		RetVal &= OutputArray[i]->AssignBuffers(Des);
	}
	// LogOut << "DfNodeLink::AssignBuffers returning " << RetVal << "\n" ;
	return RetVal;
}

void DfNodeLink::clear_output_array()
{
	if (OutputArray) {
		delete OutputArray ;
		OutputArray = 0;
		OutSize = 0;
	}
}

void DfNodeLink::remove_this_input_link()
{
	if (!OutputLinks) return ;
	DfNodeOutList * tmp = OutputLinks ;
	OutputLinks = 0 ;
	DfNodeOut * out ;
	int i = 0 ;
	while (out = tmp->Get()) {
		if (OutputArray) OutputArray[i++] = 0 ;
		out->remove_this_link();
		delete out ;
	}
	delete tmp ;
	clear_output_array();
	InputLink = 0 ;
}

void DfNodeLink::remove_this_output_link(DfNode * driven, int input_channel)
{

	clear_output_array();
	if (!OutputLinks) return ;
	OutputLinks->remove_link(driven,input_channel);
}

int DfNodeLink::ClearBuffers()
{
	// LogOut << "DfNodeLink::ClearBuffers\n" ;
	int Return = 1;
	if (OutputArray) {
		for (int i = 0 ; i < OutSize ; i++) {
			int ThisClear = OutputArray[i]->ClearBuffers();
			Return = Return && ThisClear ;
		}
		clear_output_array();
	}
	if(OutputLinks) {
		DfNodeOutIterator Next(*OutputLinks);
		DfNodeOut * out ;
		while (out = Next()) out->ClearBuffers();
	}
	if (DataBuffer) {
		DfNode * nod = GetInputNode();
		ProcessNet * net = 0 ;
		if (nod) net = nod->GetActiveNet() ;
		NetControl * con = 0 ;
		if (net) con = net->GetTheController();
		BufferDescript * Des = 0 ;
		if (con) Des = con->GetTheBufferDescript();
		if (Des) switch (Des->GetType()) {
case CircBufferType :
			// LogOut << "deleting buffer for `" << nod->GetName() << "'\n" ;
			delete (CircBuf *) DataBuffer ;
			break;
default:
			DbgError("ClearBuffers","invalid buffer type");
			break ;
		}
/*		  else
 *		LogOut << "Cannot get buffer descriptor\nnod = " <<
 *			(void *)  nod << ", net = " << (void *) net << ", con = " <<
 *			(void *) con << ", Des = " << (void *) Des << "\n" ;
 *		if (nod && !Des) LogOut << nod->GetName() << "\n" ; 
 */
		DataBuffer = 0;
	}
	return Return  ;
}


int DfNodeLink::DeleteAllLinks(const char *, class DfNodeList * List) 
{
	int Return = 1;
	// LogOut << "DfNodeLink::DeleteAllLinks\n" ;
	if (OutputLinks) {
		DfNodeOutList * tmp = OutputLinks ;
		OutputLinks = 0;
		DfNodeOut * Out;
		int i = 0;
		while (Out = tmp->Get()) {
			if (OutputArray) OutputArray[i] = 0;
			int ThisReturn = Out->DeleteAllLinks(List);
			Return = Return && ThisReturn ;
		}
		delete tmp ;
	}
	clear_output_array();
	return Return ;
}


int DfNodeInLink::DeleteAllLinks(const char *, class DfNodeList * )
{
	// LogOut << "DfNodeInLink::DeleteAllLinks\n" ;
	// Clear();
	InLink = 0;
	BufferChannelIndex = 0;
	return 1;
}

int DfNodeOutLink::ClearBuffers()
{
	// LogOut << "DfNodeOutLink::ClearBuffers\n" ;
	Timing.Clear();
	// LogOut<<"Timing.NumeratorSampling = "<< Timing.NumeratorSampling<<"\n" ;
	if (!OutLink) return 1;
	return OutLink->ClearBuffers();
}


int DfNodeOutLink::DeleteAllLinks(const char * Name,DfNodeList * List)
{
	// LogOut << "DfNodeOutLink::DeleteAllLinks\n" ;
	int Return = 1;
	if (OutLink) Return = OutLink->DeleteAllLinks(Name,List);
	OutLink = 0;
	// Clear();
	return Return ;
}


void DfNodeOut::remove_this_link()
{
	TheNode->disconnect_in(Index);
}

void DfNodeOut::DeassignBufferDescriptor(BufferDescript * des)
{
	TheNode->DeassignBufferDescriptor(des);
}

int DfNodeOut::AssignBuffers(BufferDescript * Des)
{
	// LogOut << "DfNodeOut::AssignBuffers\n" ;
	if (!NullNodeCk("AssignBuffers")) return 0;
	return TheNode->AssignBuffers(Des);
}



int DfNodeOut::ClearBuffers()
{
	if (!TheNode) return 1;
	return TheNode->ClearBuffers();
}


int DfNodeOut::DeleteAllLinks(DfNodeList * List)
{
	// LogOut << "DfNodeOut::DeleteAllLinks\n" ;
	if (!TheNode) return 1;
	int Return = TheNode->DeleteAllLinks(List);
	TheNode=0;
	Index = 0;
	SelfLink = 0;
	return Return ;
}


int DfNodeIn::DeleteAllLinks(DfNodeList *)
{
	// LogOut << "DfNodeIn::DeleteAllLinks\n" ;
	TheNode = 0;
	Index = 0;
	return 1;
}

static void error_replace(DfNode& ToReplace, DfNode& Replacement,
	const char * error)
{
	State.Error("cannot replace `",ToReplace.GetName(),
		"' with `", Replacement.GetName(),"' because ",error);
}

void ProcessNet::clear_controller()
{
	TheController =0 ;
	unedit();
}

void ProcessNet::DeassignBufferDescriptor(BufferDescript * des)
{
	ClearTraverse();
	ThreadListIterator Next(*(GetThreads())) ;
	Thread * Node ;
	while (Node=Next()) {
		DfNode * NextNode = Node->GetFirstNode() ;
		if (!NextNode) continue ;
		NextNode->DeassignBufferDescriptor(des);
	}
}

int ProcessNet::DoAssignBuffers(BufferDescript * des)
{
	ClearTraverse();
	ThreadListIterator Next(*(GetThreads())) ;
	Thread * Node ;
	int AssignOk =1;
	while (Node=Next()) {
		DfNode * NextNode = Node->GetFirstNode() ;
		if (!NextNode) continue ;
		// LogOut << "About to assign for Node `"<<NextNode->GetName()<<"'\n";
		int ThisAssign = NextNode->AssignBuffers(des);
		AssignOk = AssignOk && ThisAssign ;
	}
	if (!AssignOk) {
		State.Error("network `", GetName(), "' is damaged or invalid.",
			"Buffer assignment failed");
	}
	if (!AssignOk) DeassignBufferDescriptor(des);
	return AssignOk ;
}

void ProcessNet::ReplaceNode(DfNode& ToReplace, DfNode& Replacement)
{
/*
 *	LogOut << "ProcessNet::ReplaceNode\n" ;
 *	LogOut << "replacing `" << ToReplace.GetName() << "' with `" <<
 *		Replacement.GetName() << "'\n";
 */
	// make sure ToReplace is in the netwrok
	int no_reference = 0 ;
	int no_link = 0 ;
	if (ToReplace.GetActiveNet() != this) no_reference = 1;
	if (!FindNode(&ToReplace)) no_link = 1 ;
	if (no_link) error_replace(ToReplace,Replacement,
		"the first node is not linked");
	if (no_reference && !no_link) error_replace(ToReplace,Replacement,
		"the first node is not referenced");
	if (no_link && !no_reference) error_replace(ToReplace,Replacement,
		"the first node is inconsistently referenced");

	// make sure Replacement is not linked
	int replacement_active = 0 ;
	int replacement_linked = 0 ;
	if (Replacement.GetActiveNet()) replacement_active = 1 ;
	if (Replacement.any_links()) replacement_linked = 1 ;
	if (replacement_linked) error_replace(ToReplace,Replacement,
		"the second node is linked");
/*
 *	if ( replacement_active && !replacement_linked)
 *		error_replace(ToReplace,Replacement,
 *		"the second node is referenced");
 */
	if (replacement_linked && ! replacement_active)
		error_replace(ToReplace,Replacement,
		"the second node is inconsistently referenced");

	// make sure in and out sizes are compatible
	if (Replacement.GetIn() != ToReplace.GetIn())
		error_replace(ToReplace,Replacement,
	 	"the number of input channels differ");
	if (Replacement.GetOut() != ToReplace.GetOut())
		error_replace(ToReplace,Replacement,
	 	"the number of output channels differ");

	if (State.IsError()) return ;
	if (GetTheController()) GetTheController()->EditNetwork("replacement node");
	// get the current links of ToReplace
	ReplaceNodeLinks the_linkage(ToReplace);
	if (State.IsError()) return ;
	
	// unlink to replace

	// attempt to link Replacement
	the_linkage.LinkNewNode(*this, Replacement);
	if (State.IsError()) {
		State.ClearError();
		Replacement.RemoveAllLinks();
		the_linkage.LinkNewNode(*this,ToReplace);
		if (State.IsError()) State.Error("cannot relink original node");
		else State.Warning("have relinked original node");
		return ;
	}
	remove_unlinked_node(&Replacement);
	Replacement.SetActiveNet(0);
	Replacement.SetActiveNet(this);
	add_unlinked_node(&ToReplace);
	if (!DspApplication::make_target_mode())
		if (ToReplace.delete_if_unlinked()) delete (DfNode *) &ToReplace ;
	if (State.IsConfirm()) *Output + OutputCppHelp << "Replacement complete.\n" ;
}

const char * ProcessNet::GetFreeNodeIn()
{
	ThreadListIterator Next(*Threads) ;
	Thread * Elt;
	while (Elt = Next()) {
		DfNode * TheNode = Elt->GetFirstNode();
		if (!TheNode) continue ;
		const char * Return = TheNode->GetFreeNodeIn();
		if (Return) return Return ;
	}
	return 0;
}


const char * ProcessNet::GetFreeNodeOut()
{
	ThreadListIterator Next(*Threads) ;
	Thread * Elt;
	while (Elt = Next()) {
		DfNode * TheNode = Elt->GetFirstNode();
		if (!TheNode) continue ;
		const char * Return = TheNode->GetFreeNodeOut();
		if (Return) return Return ;
	}
	return 0;
}

const char * DfNodeLink::GetFreeNodeIn()
{
	if (!OutputLinks) return 0;
	DfNodeOutIterator Next(*OutputLinks);
	DfNodeOut * Out;
	while (Out=Next()) {
		DfNode * TheNode = Out->GetNode();
		if (!TheNode) continue ;
		const char * Return = TheNode->GetFreeNodeIn();
		if (Return) return Return ;
	}
	return 0;
}


const char * DfNodeLink::GetFreeNodeOut()
{
	if (!OutputLinks) return 0;
	DfNodeOutIterator Next(*OutputLinks);
	DfNodeOut * Out;
	while (Out=Next()) {
		DfNode * TheNode = Out->GetNode();
		if (!TheNode) continue ;
		const char * Return = TheNode->GetFreeNodeOut();
		if (Return) return Return ;
	}
	return 0;
}

GraphInfo::GraphInfo(OutTokens& out)
{
	Out = &out;
	Flags=0;
	NodeName = NetName = 0;
	OutChannel = 0;
}

void GraphInfo::OutErrorEnd(const char * msg)
{
	if (DoCpp()) Out->NextOut(";");
	else Out->NextFillOut(msg);
}

const char * ProcessNet::DoStateEmit(OutTokens& Out)
{
	// LogOut << "DoStateEmit called for `"  << GetName() << "'.\n" ;
	if (unlinked_nodes.Size()) {
		Out.NewLine();
		Out.NextConcatToken("// Nodes associated with but not linked to `");
		Out.NextConcatToken(GetName());
		Out.NextOutToken("'.");
		Out.NewLine();
		DfNodeIterator Next(unlinked_nodes);
		DfNode * node ;
		if (!in_validate) while (node = Next()) {
			Out.NextFillOut(GetName());
			Out.NextWrite(".AssociateNode(");
			Out.NextFillOut(node->GetName());
			Out.NextFillOut(");");
			Out.NewLine();
		}
	}
	Out.NewLine();
	Out.NextConcatToken("// Network topology for `");
	Out.NextConcatToken(GetName());
	Out.NextOutToken("'.");
	Out.NewLine();
	if (exact_timing) {
		Out.NextConcatToken(GetName());
		Out.NextOutToken(".SetTimingExact(1);");
		Out.NewLine();
	}
	ThreadListIterator Next(*Threads);
	Thread * TheThread ;
	while (TheThread = Next()) {
		DfNode * First = TheThread->GetFirstNode();
		if (!First) continue ;
		Out.NextFillOut(GetName()) ;
		Out.NextFillOut("+");
		GraphInfo TheInfo(Out);
		TheInfo.ClearContinuation();
		TheInfo.NetName = GetName();
		TheInfo.NodeName = TheThread->GetName();
		TheInfo.SetCpp();
		First->GraphDisplay(TheInfo);
	}
	Out.NewLine();
	BufferDescript * Descript ;
	if (TheController) if (Descript=TheController->GetTheBufferDescript()) {
		Out.NextFillOut(GetName());
		Out.NextWrite(".SetBufferDescriptor(");
		Out.NextFillOut(Descript->GetName());
		Out.NextFillOut(");");
		Out.NewLine();
	}
	int display = unlinked_nodes.Size() > 0;
	if (Threads) if (Threads->Size()) display = 1 ;
	if (display) {
		Out.NextFillOut(GetName());
		Out.NextWrite(".GraphDisplay();");
		Out.NewLine();
	}
	return 0;
}

void ProcessNet::CreateController()
{
	if (TheController) return ;
	const char * NewName = MakeNewEntityNameSuf(GetName(),"Control");
	// LogOut << "CreateController - " << NewName << "\n" ;
	TheController = new DataFlow(NewName, *this);
	if (!TheController) {
		State.Error("cannot assign a controller for network: `",
			GetName(), "'");
		return ;
	}
};

void ProcessNet::Execute(int32 InputSamples)
{
	CreateController();
	if (State.IsError()) return ;
	TheController->DoExecute(InputSamples);
}

void ProcessNet::set_buffer_descriptor(BufferDescript& Desc)
{
	CreateController();
	if (State.IsError()) return ;
	TheController->set_buffer_descriptor(Desc);
}

void ProcessNet::AssignBuffers(BufferDescript& Desc)
{
	CreateController();
	if (State.IsError()) return ;
	TheController->AssignBuffers(Desc);
}

void ProcessNet::ClearBuffers()
{
	int DeleteOk = 1 ;
	Thread * node_list ;
	ThreadListIterator Next(*(GetThreads())) ;
	while (node_list=Next()) {
/*
 *	  LogOut << "Clearing thread starting with node `" <<
 *		  node_list->GetFirstNode()->GetName() << "'\n" ;
 */
		node_list->ClearTiming();
		int ThisDelete = node_list->GetFirstNode()->ClearBuffers();
		DeleteOk = DeleteOk && ThisDelete ;
	}
	if (!DeleteOk)
		State.Error("the network is damaged or invalid, canot clear buffers") ;
}

void ProcessNet::ClearNetwork()
{
	if (State.IsInteractive()) {
		*Output + OutputPrompt << "Type yes to clear all nodes from network `"
			<< GetName() << "' or anything else to abort:\n" ;
		char * RawBuf = GetRawBufLine(InputPrompt);
		if (!RawBuf) return ;
		if (strcmp(RawBuf,"yes")) return ;
	}
	if (TheController) {
		TheController->ClearBuffers();
		TheController->DeleteAllLinks();
	} else {
		ClearBuffers();
		DeleteAllLinks();
	}
	if (State.IsInteractive()) if (is_displayed()) GraphDisplay();
}

void ProcessNet::ClearTraverse()
{
	ThreadListIterator Next(*Threads) ;
	Thread * Elt;
	while (Elt = Next()) {
		DfNode * TheNode = Elt->GetFirstNode();
		if (!TheNode) continue ;
		TheNode->ClearTraverse();
	}
}

void ProcessNet::TraverseNetwork(TraverseObject& Obj)
{
	if (State.IsError()) {
		// LogOut << "TraverseNetwork - state error\n" ;
		return ;
	}
	if (!Threads->CheckComplete(GetName())) {
		State.Error("network `", GetName(),
		"' is incomplete,\nthe error was detected while ",
		Obj.GetDoingWhat());
		return ;
	}
	TraverseResult Result ;
	ClearTraverse();
	do {
		Result = TraverseOK ;
		ThreadListIterator Next(*Threads) ;
		Thread * Elt;
		while (Elt = Next()) {
			DfNode * TheNode = Elt->GetFirstNode();
			if (!TheNode) continue ;
/*
 *			LogOut <<
 *				"ProcessNetwork::TraverseNetwork starting at `"
 *				<< GetName() << "', `" << TheNode->GetName()
 *				<< "'\n" ;
 */
			TraverseResult temp = TheNode->Traverse(Obj);
			if (temp > Result) Result = temp ;
			if (Result >= TraverseError) break ;
		}
	} while (Result == TraverseNotDone) ;
	ClearTraverse();
}

void ProcessNet::WriteKernelNodes(TraverseObject& Obj)
{
	// LogOut << "ProcessNet::WriteKernelNodes\n" ;
	Obj.SetForwardAction(&DfNode::WriteObject);
	TraverseNetwork(Obj);
	Obj.ClearForwardAction();
}

void ProcessNet::SetTheController(NetControl * cnt)
{
	if (TheController) if (TheController != cnt)
		DbgError("ProcessNet::SetTheController", "already set");
	TheController = cnt;
}


void ProcessNet::ClearTargetParameters(TraverseObject& Obj)
{
	Obj.ClearForwardAction();
	Obj.ClearBackwardAction();
	Obj.SetForwardAction(&DfNode::ClearTargetParameters);
	TraverseNetwork(Obj);
	Obj.ClearForwardAction();
}


void ProcessNet::CreateStaticExecutionTables(TraverseObject& Obj)
{
/*
 *	Algorithm for assigning target state is:
 *		1. Assign initialization sequences to prime input.
 *		(If network is feedback note this at this point and
 *		revert to dynamic scheduling approach.)
 *
 *		Initialization sequences are only at terminal nodes
 *		(no output) or nodes in which output nodes already have
 *		their initial sequences assigned.
 *		
 *		2. Assign steady state sequences.
 *		
 *	Passes through the tree:
 *
 *		1. Backwards pass to determine init sequence for each node.
 *		2. Forward pass to reconcile all init sequences.
 *		3. Backwards pass to determine repeat sequence for each node.
 *		4. Forward pass to reconcile execute sequence and assign
 *		   buffer sizes.
 *		   (If at any pass, the sequence mode of execution fails we
 *		   advance to pass 4.)
 *		5. Optional pass to clear sequences if sequence mode failed.
 *
 */

	Obj.SetForwardAction(&DfNode::ClearTargetParameters);
	Obj.SetTraverseType(TraverseObject::TraverseStandard);
	TraverseNetwork(Obj);
	Obj.ClearForwardAction();
	// LogOut << "After ClearTargetParameters\n" ;
	if (State.IsError()) return ;

	CreateController() ;
	if (!GetTheController()) return ;
	BufferDescript * TempDescript =
		GetTheController()->GetTheBufferDescript();
	if (!TempDescript) DbgError("ProcessNet::CreateStaticExecutionTables",
		"no buffer description");

	ostream& Help_Out = OutputToWindow(OutputCppHelp);
	TargetAdjustState StateOut = TargetAdjustChange ;

	BufferStatus * TempStatus = TempDescript->GetBufferStatus() ;
	if (!TempStatus) DbgError("ProcessNet::CreateStaticExecutionTables",
		"no buffer status");
	BufferStatus& Status = *TempStatus;

	if (!Obj.GetParameters()) {
		TraverseParameters * TheParams = new TraverseParameters(Status);
		Obj.SetParameters(TheParams);
	} else Obj.GetParameters()->SetTheParameters(&Status) ;
	// First goal should be fixed sequence execution
/*
 *	Status.SetGoals(BufferStatus::FixedSequence,
 *		BufferStatus::DesiredSizeUpperBound, 512, 8192);
 */

	Status.SetForwardFlag();
	Status.SetPreInitState() ;
	Obj.SetForwardAction(&DfNode::SetTerminalNodeResampling);
	Obj.SetTraverseType(TraverseObject::TraverseStandard);
	TraverseNetwork(Obj);
	if (State.IsError()) goto CleanUp ;
	if (Status.IsFailure()) goto AssignBufferSize ;

	Status.SetPreInitMaxBufState() ;
	TraverseNetwork(Obj);
	Obj.ClearForwardAction();
	if (State.IsError()) goto CleanUp ;
	if (Status.IsFailure()) goto AssignBufferSize ;

	Status.SetExecutionSequenceIndex(0);
	Status.SetInitState() ;
	for(;;) {
		if (StateOut < TargetAdjustChange)
			if (!Status.AdvanceStateInit()) break ;
			
		Status.SetState(TargetAdjustOK);
		// SetTargetInitSequence
		for (;;) {
			Obj.SetBackwardAction(&DfNode::SetTargetInitSequence);
			Status.ClearForwardFlag();
			Obj.SetTraverseType(
				TraverseObject::TraverseOutputNodesFirst);
			TraverseNetwork(Obj);
			Obj.ClearBackwardAction();
			if (State.IsError()) goto CleanUp ;
			if (Status.GetTryLargerBuffer()) 
				if (Status.TryLarger()) continue ;
			if (Status.IsFailure()) goto AssignBufferSize ;
			break ;
		}
		// ReconcileTargetInitSequence

		Status.SetForwardFlag();
		Obj.SetForwardAction(&DfNode::ReconcileTargetInitSequence);
		Obj.SetTraverseType(TraverseObject::TraverseInputNodesFirst);
		TraverseNetwork(Obj);
		Obj.ClearForwardAction();
		if (State.IsError()) goto CleanUp ;
		if (Status.IsFailure()) goto AssignBufferSize ;
		StateOut = Status.GetState();
/*
 *		LogOut << "Loop init StateOut = " << StateOut << 
 *			", State = " << Status.StateToString() << "\n" ;
 */
	} 

	if (Status.TheGoal == BufferStatus::ExistingSize) {
		Status.ClearExecutionSequences();
		goto AssignBufferSize ;
	}
	Status.SetExecutionSequenceIndex(1);

	StateOut = TargetAdjustChange ;
	Status.SetInitState();
	for (;;) {
		if (StateOut < TargetAdjustChange)
			if (!Status.AdvanceStateExecute()) break ;

		Status.SetState(TargetAdjustOK);
		if (Status.IsMultipleState()) {
			// Set multiples
			Status.SetState(TargetAdjustOK);
			Obj.SetBackwardAction(&DfNode::AssignMultiples);
			Obj.SetTraverseType(
				TraverseObject::TraverseOutputNodesFirst);
			TraverseNetwork(Obj);
			Obj.ClearBackwardAction();
			if (State.IsError()) goto CleanUp ;
		}
		// SetTargetSequence
		Obj.SetBackwardAction(&DfNode::SetTargetSequence);
		Status.ClearForwardFlag();
		Obj.SetTraverseType(TraverseObject::TraverseOutputNodesFirst);
		TraverseNetwork(Obj);
		Obj.ClearBackwardAction();
		if (State.IsError()) goto CleanUp ;
		if (Status.IsFailure()) goto AssignBufferSize ;

		Obj.SetForwardAction(&DfNode::ReconcileTargetSequence);
		Status.SetForwardFlag();
		Obj.SetTraverseType(TraverseObject::TraverseInputNodesFirst);
		TraverseNetwork(Obj);
		Obj.ClearForwardAction();
		Obj.SetTraverseType(TraverseObject::TraverseStandard);
		if (Status.IsFailure()) goto AssignBufferSize ;
		if (State.IsError()) goto CleanUp ;
		StateOut = Status.GetState();
/*
 *		LogOut << "Loop execute StateOut = " << StateOut <<
 *			", State = " << Status.StateToString() << "\n" ;
 */
	}

AssignBufferSize:
	Obj.ClearForwardAction();
	Obj.ClearBackwardAction();
	// Obj.SetTraverseType(TraverseObject::TraverseOutputNodesFirst);
	Obj.SetTraverseType(TraverseObject::TraverseStandard);
	if (Status.IsFailure()) {
		int Fatal = Status.IsFatal() ;
		// LogOut << "Fatal = " << Fatal << "\n" ;
		Status.Explain(Help_Out);
		Status.Clear();
		if (Fatal) {
			State.Error("target code not generated");
			goto CleanUp ;
		}
		HelpOut <<
		"Target code will be generated with dynamic scheduling.\n" ;
		Obj.SetBackwardAction(&DfNode::CheckTargetBufferSize);
		Status.SetGoals(BufferStatus::ExistingSize,
			BufferStatus::DesiredSizeUpperBound, 512, 8192, 64);
		Status.ClearExecutionSequences();
		TraverseNetwork(Obj);
		Obj.ClearBackwardAction();
	} else {
		Obj.SetBackwardAction(&DfNode::CheckTargetBufferSize);
		TraverseNetwork(Obj);
		Obj.ClearBackwardAction();
	}
	Obj.SetTraverseType(TraverseObject::TraverseStandard);
	if (Status.IsFailure()) {
		Status.Explain(Help_Out);
		Status.Clear();
		State.Error("cannot set buffer sizes to make target");
		goto CleanUp;
	}
CleanUp:
	;
}

void ProcessNet::WriteTargetState(TraverseObject& Obj)
{
	// LogOut << "ProcessNet::WriteTargetStateNodes\n" ;
	// LogOut << "WriteTargetBuffers\n" ;
	Obj.SetForwardAction(&DfNode::WriteTargetBuffers);
	TraverseNetwork(Obj);
	// LogOut << "WriteTargetBuffers done\n" ;
	Obj.ClearForwardAction();
	Obj.SetForwardAction(&DfNode::WriteTargetState);
	TraverseNetwork(Obj);
	Obj.ClearForwardAction();
}



void ProcessNet::TraverseThread(TraverseObject& obj,DfNode& node)
{
	ClearTraverse();
	node.Traverse(obj);
	ClearTraverse();
}

void ProcessNet::Traverse(TraverseObject& obj)
{
	ClearTraverse();
	Thread * thread ;
	ThreadListIterator Next(*Threads);
	while(thread=Next()) {
		DfNode * node = thread->GetFirstNode();
		node->Traverse(obj);
		if (State.IsError()) {
				ClearTraverse();
				return ;
		}
		// Out << "\t&" << thread->GetName() << ",\n" ;
	}
	ClearTraverse();
}

void ProcessNet::EmitThreadTable(TraverseObject& Obj, const char *StateName,
	const char *IncludeName)
{
	ostream& Out = Obj.GetHeaderOut() ;
	const char * base_name = 
		Obj.GetParameters()->GetEmitTargetCode()->GetOutName();
	if (!base_name) {
		State.Error("bad target out name");
		return ;
	}
	Out << "#include \"" << base_name << "\"\n\n" ;
	Out << "#include \"exec.h\"\n\n" ;
	Out << "static TargetNode * TheThreads[] = {\n" ;

	Obj.ClearForwardAction();
	Obj.SetForwardAction(&DfNode::EmitThreadTable);
	Obj.SetTraverseType(TraverseObject::TraverseInputNodesFirst);

	Traverse(Obj);


	Obj.ClearForwardAction();
	Obj.SetTraverseType(TraverseObject::TraverseStandard);
	Out << "\t0\n};\n\n" ;

	BufferStatus& Status = *(Obj.GetBufferStatus());

	Out << "static TargetNode * TheFeedbackNodes[1] = {0};\n\n" ;
	Out << "ExecuteState " << StateName <<
		"( TheThreads, TheFeedbackNodes, " <<
		Status.GetInitSequenceLength() << ", " <<
		Status.GetExecuteSequenceLength() << ", " <<
		"&TheNetworkReferenceFor_" << GetName() << ");\n\n" ;

	if (IncludeName) Out << "#include \"" << IncludeName << "\"\n\n" ;

	ostream& Help_Out = OutputToWindow(OutputCppHelp);
	Status.Report(Help_Out);
	Obj.ClearParameters();
}

int ProcessNet::CheckComplete()
{
	if (State.IsError()) return 0 ;
	if (!TheController) return 0 ;
	return TheController->CheckComplete();
}

void ProcessNet::remove_signal_node(DfNode& node)
{
	clear_signal_node(node,1); 
}

void ProcessNet::clear_signal_node(DfNode& node, int warn_flag)
{ 
	if (!warn_flag) if (node.GetIn()) return ;
	if (!Threads) return ;
	if (Threads->Remove(&node) != OK) if (warn_flag)
		State.Warning("no signal node `", node.GetName(),
		"' in network `", GetName(), "'");
	LastAccessed = 0 ;
	TheFlag = 0 ;
	LastMultipleAccessed = 0 ;
	NextOut = 0 ;
}

int DfNodeLink::IsFeedbackHead() const
{
	for (int i = 0 ; i < OutSize ; i++)
		if (OutputArray[i]->IsFeedbackHead()) return 1 ;
	return 0 ;
}

int DfNodeOut::AnyConsumerOverlap() const
{
	return TheNode->GetOverlap(Index);
}

int DfNodeLink::AnyConsumerOverlap() const
{
	for (int i = 0 ; i < OutSize ; i++)
		if (OutputArray[i]->AnyConsumerOverlap()) return 1 ;
	return 0 ;
}

int DfNodeLink::IsTraverseComplete() const
{
/*
 *	LogOut << "DfNodeLink::IsTraverseComplete()\n" ;
 *	LogOut << "OutSize = " << OutSize << "\n" ;
 */
	for (int i = 0 ; i < OutSize ; i++)
		if (!OutputArray[i]->IsTraverseComplete()) return 0 ;
	// LogOut << "DfNodeLink::IsTraverseComplete() exit\n" ;
	return 1 ;
}

int DfNodeLink::IsTargetDefined() const
{
	for (int i = 0 ; i < OutSize ; i++)
		if (!OutputArray[i]->IsTargetDefined()) return 0 ;
	return 1 ;
}

int DfNodeOut::IsTargetDefined() const
{
	return GetNode()->IsTargetDefined() ;
}

int DfNodeOut::IsTraverseComplete() const
{
/*
 *	LogOut 	<< "DfNodeOut::IsTraverseComplete()\n" ;
 *	LogOut << "Node is 0x" << GetNode() << "\n" ;
 *	LogOut << "Name is " << GetNode()->GetName() << "\n" ;
 */
	int Return = GetNode()->IsTraverseComplete() ;
	// LogOut << "DfNodeOut::IsTraverseComplete() exit\n" ;
	return Return ;
}

DfNodeOut * DfNodeLink::GetDfNodeOut(int i) const
{
	if (i < OutSize ) return OutputArray[i];
	DbgError("DfNodeLink::DfNodeLink","bad index");
	return  0 ;
}

TargetAdjustState DfNodeLink::AdjustTargetBuffer(int32 NewSize) 
{
	if (!GetDataBuffer()) DbgError("DfNodeLink::AdjustTargetBuffer",
		"no buffer");
	// LogOut << "DfNodeLink::AdjustTargetBuffer(" << NewSize << ")\n" ;
	return GetDataBuffer()->AdjustTargetBuffer(NewSize);
}

void DfNodeLink::ClearTargetParameters()
{
	if (!GetDataBuffer()) DbgError("DfNodeLink::ClearTargetParameters",
		"no buffer");
	GetDataBuffer()->ClearTargetParameters();
}

int32 DfNodeLink::GetMinBufferSize() const
{
	int MinSize = InputLink->GetMinBufferSize() ;
	for (int i = 0 ; i < OutSize; i++) {
		int32 LocMin = OutputArray[i]->GetMinBufferSize();
		if (LocMin > MinSize) MinSize = LocMin ;
	}
	return MinSize ;
}

TraverseResult DfNodeLink::CheckTargetBufferSize(TraverseObject& obj)
{
	int32 MinBufferSize = GetMinBufferSize() ;
	if (!GetDataBuffer()) DbgError("DfNodeLink::CheckTargetBufferSize",
		"no buffer");
	return GetDataBuffer()->CheckTargetBufferSize(obj,MinBufferSize);
}

TargetBufferParameters * DfNodeLink::GetTargetBuffer() const
{
	if (!GetDataBuffer()) DbgError("DfNodeLink::GetTargetBuffer",
		"no buffer");
	return GetDataBuffer()->GetTargetBuffer();
}

TargetAdjustState DfNodeLink::AdjustForward(Adjustment& TheAdjustment)
{
	TargetAdjustState Return = TargetAdjustOK ;
	Adjustment Save = TheAdjustment ;
	for (int i = 0 ; i < OutSize ; i++) {
		Adjustment Use = Save ;
		TargetAdjustState Temp ;
		Use.SetInLink(GetDfNodeOut(i)->GetConsumerLink()) ;
		Temp = OutputArray[i]->Adjust(Use) ;
		if (Temp > Return) Return = Temp ;
		if (Return >= TargetAdjustFail) return Return;
	}
	return Return ;
}

TargetAdjustState DfNodeLink::AdjustBackward(Adjustment& TheAdjustment)
{
	return GetInputLink()->Adjust(TheAdjustment) ;
}

void DfNodeLink::Unlink()
{
	// LogOut << "DfNodeLink::Unlink\n" ;
	clear_output_array();
	if (!OutputLinks) return ;
	DfNodeOutList * tmp = OutputLinks ;
	OutputLinks = 0 ;
	DfNodeOut * to_unlink ;
	while (to_unlink = tmp->Get()) {
		to_unlink->Unlink();
		// LogOut << "at DfNodeLink::Unlink\n" ;
	}
	delete tmp ;
	// LogOut << "DfNodeLink::Unlink exit\n" ;
}

void DfNodeLink::UnlinkDriven(int buffer_channel)
{
	// LogOut << "DfNodeLink::UnlinkDriven(" << buffer_channel << ")\n" ;
	if (OutputArray) clear_output_array();
	if (!OutputLinks) return ;
	if (buffer_channel >= OutputLinks->Size()) return ;
	DfNodeOutIterator Next(*OutputLinks) ;
	DfNodeOut * Remove = 0 ;
	int i=0;
	while (Remove = Next())  if (i++ == buffer_channel) break ;
	if (!Remove) {
		// LogOut << "DfNodeLink::UnlinkDriven(" << buffer_channel << ")\n" ;
		return ;
	}
	DfNodeOut * Out ;
	i-- ;
	while(Out = Next()) Out->new_buffer_channel(i++);
	if (OutputLinks->RemoveEntry(Remove) != OK) {
		State.Error("problem unlinking input");
		return ;
	}
	delete Remove ;
	if (OutputLinks) if (!OutputLinks->Size()) {
		delete OutputLinks ;
		OutputLinks = 0 ;
		DfNodeIn * node_in = InputLink ;
		InputLink = 0 ;
		if (node_in) node_in->Unlink();
		// delete node_in ;
		// delete this ;
	}
	// LogOut << "DfNodeLink::UnlinkDriven(" << buffer_channel << ") exit\n" ;
}

TargetAdjustState DfNodeIn::Adjust(Adjustment& TheAdjustment)
{
	return TheNode->AdjustBackward(TheAdjustment);
}

TargetAdjustState DfNodeOut::Adjust(Adjustment& TheAdjustment)
{
	return TheNode->AdjustForward(TheAdjustment);
}

void DfNodeOut::new_buffer_channel(int new_chan)
{
	TheNode->new_buffer_channel(Index,new_chan);
}
