/*  network.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include "shared.h"
#include "net.h"
#include "buffer.h"
#include "netlnk.h"
#include "timing.h"
#include "circbuf.h"
#include "outtok.h"
#include "intfc.h"
#include "user.h"
#include "cgidbg.h"
#include "thread.h"
#include "yacintfc.h"
#include "netcnt.h"
#include "interinit.h"
#include "strmstr.h"
#include "netdescd.h"
#include "travparm.h"
#include "editnet.h"
#include "remcom.h"

// void TestAlloc(const char * msg);

static EntityList * ProcessNetList =0 ;

Thread::Thread(DfNode * TheNode, TimingDescription * timing)
{
	FirstNode = TheNode ;
	Timing = new TimingDescription ;
	if (timing) *Timing = *timing ;
	
	if (!FirstNode)
		State.Error("assigning a null node pointer to a thread");
}

Thread::~Thread()
{
}
int Thread::AssignTiming(int allow_reset)
{
/*
 *	LogOut << "Thread::AssignTiming(" << allow_reset << "), for `" <<
 *		FirstNode->GetName() << "'\n" ;
 */
	Timing->CheckInitDefault();
/*
 *	LogOut << "Timing after init:\n" ;
 *	Timing->Display();
 */
	TimingAdjustment * adjust = 0 ;
	if (allow_reset) (adjust =  new TimingAdjustment)->Clear();
	TimingDescription::set_thread_base = adjust ;
	TimingDescription::state = TimingDescription::not_set ;
	int Return = FirstNode->AssignTiming(*Timing);
	if (TimingDescription::state == TimingDescription::restart) {
/*
 *		LogOut << "Adjust in Thread::AssignTiming for `" <<
 *       	FirstNode->GetName() << "'\n" ;
 *		adjust->Display();
 */
		// adjust timing
/*
 *		LogOut << "Adjusting base thread for `" << FirstNode->GetName()
 *			<< "' from\n" ;
 */
		int warn = 0 ;
		for (int i = 0 ; i < FirstNode->GetOut() ; i++) 
			if (FirstNode->GetInitTiming(i)) warn++ ; 
		if (warn) State.Warning("The initial timing in node `", GetName(),
			"' is inconsistent with the network. It has been altered.");

		adjust->adjust_base(*Timing);
		
		TimingDescription::state = TimingDescription::adjusted ;
		Return = FirstNode->AssignTiming(*Timing);
	}
	TimingDescription::state = TimingDescription::not_set ;
	delete adjust ;
	adjust = 0 ;
/*
 *	LogOut << "Thread::AssignTiming for `" << FirstNode->GetName() <<
 *		"' returning " << Return << "\n" ;
 */
	return Return ;
}

void Thread::ClearTiming()
{
	if (Timing) Timing->Clear();
}

void ProcessNet::Init()
{
	Threads = new ThreadList ;
	LastAccessed = 0 ;
	LastMultipleAccessed = 0 ;
	NextOut = 0;
	
	TheFlag = 1;
}

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

static InteractiveEntity * IntEntProcessNet =  0 ;

ProcessNet::ProcessNet(const char * name):
	TheNetworkReference(0),
	UserEntity(name),
	TheController(0),
	displayed(0),
	in_validate(0)
{
	// LogOut << "ProcessNet::ctor(" << name << ")\n" ;
	Init();
	if (!ProcessNetList) ProcessNetsInit() ;
	ProcessNetList->Append(MakeDeclaredEntity(this,IntEntProcessNet));
}

ProcessNet::~ProcessNet()
{
	unedit();
	// LogOut << "ProcessNet::dtor for " << GetName() << "\n" ;
	// LogOut << "controller is " << (void *) GetTheController() << "\n" ;
	DfNode * node ;
	while (node = unlinked_nodes.Get()) node->SetActiveNet(0);
	ClearActiveNetwork();
	NetControl * StupidCompiler = GetTheController();
	if (StupidCompiler) {
		// LogOut<<"Deleting controller `"<< StupidCompiler->GetName()<<"'\n" ;
		delete StupidCompiler ;
	}
	TheController = 0 ;
	delete Threads ;
	ProcessNetList->Delete(GetName());
}

void ProcessNet::unedit()
{
	if (State.is_exiting()) return ;
	int name_size = strlen(GetName());
    if (name_size < MaxPacketSize) {
        PacketHeader head(PacketNetworkEdit,CppEnums::delete_network,
            name_size+1);
        WriteSeg->WritePacket(head,GetName());
    }
}

void ProcessNet::ClearActiveNetwork()
{
	// LogOut << "ProcessNet::ClearActiveNetwork\n" ;
	if (GetTheController()) GetTheController()->ClearNetwork();
	else ClearNetwork();
}

int ProcessNet::DeleteAllLinks(DfNodeList * List)
{
	// LogOut << "ProcessNet::DeleteAllLinks\n" ;
	int DeleteOk = 1;
	ThreadList * TheThreads = GetThreads();
	Thread * Node ;
	while (Node=TheThreads->Get()) {
		DfNode * ThisNode = Node->GetFirstNode();
		if (!ThisNode) continue ;
		int ThisDelete = ThisNode->DeleteAllLinks(List);
		DeleteOk = DeleteOk && ThisDelete ;
	}
	if (!DeleteOk) {
		State.Error("damaged network found `", GetName(),
			"' found while deleting links");
	}
	delete GetThreads();
	Init();
	return DeleteOk ;
}

int ProcessNet::check_safe_delete()
{
	// check if a network is attached prompt if you want all
	// links deleted
	if (State.IsInOverwrite()) return 1 ;
	if (Threads) if (Threads->Size()) {
		*Output + OutputCppHelp << "Network `" << GetName() <<
			"' contains some links to processing nodes.\n" ;
		*Output + OutputCppHelp << "It cannot be deleted.\n" ;
		return 0 ;
	}
	NetControl * TheControl = GetTheController() ;
	if (TheControl) {
		const char * LastMessage =
	"'.\nYou must delete the controller before deleting the network.\n" ;
		if (State.IsInOverwrite()) {
			int DidDelete = TheControl->CheckSafeDelete() ;
			if (DidDelete) DidDelete = TheControl->Delete();
			if (DidDelete) return 1 ;
			State.Error("cannot delete network controller");
			LastMessage = 
	"'.\nThis controller cannot be deleted. It may be a prefined default." ;
		}
		HelpOut << "Network`" << GetName() <<
		"' is being controlled by `" << GetTheController()->GetName() ;
		HelpOut << LastMessage ;
		return 0 ;
	}
/*
 *	if (Threads) if (!State.IsInOverwrite()) if (Threads->Size()) {
 *		*Output + OutputPrompt << "Network `" << GetName() <<
 *		"' contains links to processing nodes.\n" ;
 *		for (;;) {
 *			const char * Resp = ::GetName(
 *		"`yes' to clear the network links or\n\tRETURN to abort deleting it");
 *			if (!strcmp(Resp,"yes")) return 1;
 *			if (!strlen(Resp)) return 0;
 *		}
 *	}
 */
	return 1;
}

DfNode * ProcessNet::GetFirstThreadEntry()
{
	return Threads->GetFirst()->GetFirstNode();
}

DfNodeOut::DfNodeOut (DfNode * Nd, int Ix, int Self):
TheNode(Nd),
Index(Ix),
SelfLink(Self)
{
	// LogOut << "DfNodeOut - ctor test\n" ;
	// LogOut << "for " << TheNode->GetName() << "\n" ;
}

int DfNodeOut::NullNodeCk(const char * Routine)
{
	if (State.IsError()) return 0;
	if (TheNode) return 1;
	State.Error("null node from", Routine);
	return 0;
}

void DfNodeOut::Unlink()
{
	// LogOut << "DfnodeOut::UnLink, TheNode = " << (void *) TheNode << "\n" ;
	if (!TheNode) return ;
	// LogOut << TheNode->GetName() << "\n" ;
	DfNode * node = TheNode ;
	TheNode = 0 ;
	node->UnlinkInputChannel(Index);
	TheNode = 0 ;
	// LogOut << "DfnodeOut::UnLink exit\n" ;
}


DfNodeLink::DfNodeLink(DfNode * Source, int Index):
	InputLink(new DfNodeIn( Source, Index)),
	OutputLinks(new DfNodeOutList),
	descriptor(0),
	DataBuffer(0),
	OutputArray(0)
{
}

DfNodeLink::~DfNodeLink()
{
	delete InputLink ;
	if (OutputLinks) {
		OutputLinks->Clear();
		delete OutputLinks ;
	}
	OutputLinks = 0 ;
	delete OutputArray ;
	OutputArray = 0 ;
	delete DataBuffer ;
	DataBuffer = 0 ;
}

DfNodeOutLink * DfNodeLink::GetDriverOutputChannel(int DriverOutputChannel)
{
	if (State.IsError()) return 0;
	if (!InputLink) {
		State.Error("bad links");
		return 0;
	}
	DfNode * DriverNode = InputLink->GetNode();
	if (!DriverNode) {
		State.Error("null driver node");
		return 0;
	}
	return DriverNode->GetOutLink(DriverOutputChannel);
}

void DfNodeLink::WriteCxWord(CxMachWord Data)
{
	if (!CkNullBuffer("WriteCxWord")) return;
	DataBuffer->WriteCxWord(Data);
}


CxMachWord DfNodeLink::ReadCxWord(int Index)
{
	if (!CkNullBuffer("ReadCxWord")) return 0 ;
	return DataBuffer->ReadCxWord(Index) ; 
}


void DfNodeLink::WriteBinary(BinMachWord Data)
{
	if (!CkNullBuffer("WriteBinary")) return ;
	DataBuffer->WriteBinary(Data);
}

void DfNodeLink::WriteWord(MachWord Data)
{
	if (!CkNullBuffer("WriteWord")) return ;
	DataBuffer->WriteWord(Data);
}


BinMachWord DfNodeLink::ReadBinary(int Index)
{
	if (!CkNullBuffer("ReadWord")) return (BinMachWord) (int16) 0 ;
	return DataBuffer->ReadBinary(Index) ; 
}

MachWord DfNodeLink::ReadWord(int Index)
{
	if (!CkNullBuffer("ReadWord")) return (MachWord) (int16) 0 ;
	return DataBuffer->ReadWord(Index) ; 
}



int DfNodeLink::GetSpace()
{
	if (!CkNullBuffer("GetSpace")) return 0 ;
	return DataBuffer->GetSpace();
}

int DfNodeLink::GetContiguousSpace()
{
	if (!CkNullBuffer("GetContiguousSpace")) return 0 ;
	return DataBuffer->GetContiguousSpace();
}

int DfNodeLink::GetAvailableData(int Ix)
{
	if (!CkNullBuffer("GetAvailableData")) return 0;
	return DataBuffer->GetAvailableData(Ix);
}

int DfNodeLink::GetContiguousAvailableData(int Ix)
{
	if (!CkNullBuffer("GetContiguousAvailableData")) return 0;
	return DataBuffer->GetContiguousAvailableData(Ix);
}

int DfNodeLink::CheckComplete(const char * nm)
{
	int Return = OutputLinks->CheckComplete(nm);
	ErrCode Err = CheckArithType();
	if (Err == OK) return Return ;
	return 0 ;
}

int DfNodeLink::Size()
{
	return OutputLinks->Size() ;
}

int DfNodeLink::AppendOut(DfNode * node, int InIndex, int SelfLink) 
{
	DfNodeOut * tmp = new DfNodeOut(node,InIndex, SelfLink);
	int ret = OutputLinks->Append(tmp);
	return ret ;
}

int DfNodeOut::CheckComplete()
{
	if (!NullNodeCk("CheckComplete")) return 0;
	if (SelfLink) return 1 ;
	return TheNode->CheckComplete() ;
}

void DfNodeOut::Display()
{
	if (!NullNodeCk("Display")) return ;
	if (SelfLink) NameDisplay() ;
	else {
		TheLog << "Displaying tree from this node." <<
			" Output goes to input channel " << Index << ".\n" ;
		TheNode->Display();
	}
}

void DfNodeOut::NameDisplay()
{
	if (!NullNodeCk("NameDisplay")) return ;
	TheNode->NameDisplay();
	TheLog << "This output goes to input channel " << Index <<".\n" ;
	if (SelfLink) TheLog << "This is a self linked output.\n" ;
}

void DfNodeOut::ClearTraverse()
{
	if (SelfLink) return ;
	if (!TheNode) return ;
	TheNode->ClearTraverse();
}

TraverseResult DfNodeOut::Traverse(TraverseObject& Obj)
{
	if (SelfLink) return TraverseOK ;
	if (!TheNode) return TraverseError ;
	return TheNode->Traverse(Obj);
}


DfNode * DfNodeOut::FindNode(const DfNode * node)
{
	if (!TheNode) return 0 ;
		// DbgError("DfNodeOut::FindNode", " NULL Node");
	if (SelfLink) return 0;
	return TheNode->FindNode(node);
}

DfNode * DfNodeOut::FindNode(const char * Name)
{
	if (!TheNode) return 0 ;
		// DbgError("DfNodeOut::FindNode", " NULL Node");
	if (SelfLink) return 0;
	return TheNode->FindNode(Name);
}

DfNodeOutLink * DfNodeIn::GetOutLink() const
{
	if (!TheNode) DbgError("DfNodeIn::GetOutLink","null node");
	return TheNode->GetOutLink(Index);
}

void DfNodeIn::Unlink()
{
/*
 *	LogOut << "DfNodeIn::Unlink, Index = " << Index << ",  TheNode = "
 *		<< (void *) TheNode << "\n" ;
 */
	if (!TheNode) return ;
	DfNode * node = TheNode ;
	TheNode = 0 ;
	node->UnlinkOutputChannel(Index);
	// LogOut << "DfNodeIn::Unlink exit\n" ;
}

DfNodeOutLink * DfNodeOut::GetFeederLink() const
{
	if (!TheNode) DbgError("DfNodeOut::GetFeederLink","null node");
	return TheNode->GetDriverOutputChannel(Index) ;
}

ErrCode DfNodeOut::Reset()
{
	if (!TheNode) return OK ;
	return TheNode->Reset() ;
}

DfNodeInLink * DfNodeOut::GetConsumerLink() const
{
	if (!TheNode) DbgError("DfNodeOut::GetConsumerLink","null node");
	return TheNode->GetInLink(Index) ;
}

void DfNodeOut::GraphDisplay(GraphInfo& Graph)
{
	OutTokens& Out = *(Graph.GetOut());
	char Buf[32] ;
	if (!Graph.DoCpp()) {
		Out.NextOut("->");
		strcpy(Buf,"(");
		strcat(Buf,dec(Index));
		strcat(Buf,")");
		Out.NextOut(Buf) ;
	}
	if (SelfLink) {
		if (!TheNode) Graph.OutErrorEnd("NULL node name!");
		else {
			Out.NextOut(TheNode->GetName());
			if (Graph.DoCpp()) {
				Out.NextConcat(".LinkIn(");
				Out.NextWrite(strcpy(Buf,
					dec(Index)));
				Out.NextWrite(") ;") ;
			}
		}
		Out.NewLine();
	} else {
		if (!TheNode) {
			Graph.OutErrorEnd("NULL node name!");
			Out.NewLine();
		} else TheNode->GraphDisplay(Graph,Index);
	}
}

void DfNodeIn::NameDisplay()
{
	if (!TheNode) TheLog << "DfNodeIn: NULL Node\n" ;
	else TheNode->NameDisplay() ;
	TheLog << "DfNodeIn: This is output channel " << GetIndex() << ".\n" ;
}


int32 DfNodeIn::GetEltSize()
{
	if (!TheNode) {
		State.Error("null node");
		return 0;
	}
	return TheNode->GetEltSize(Index);
}

void DfNodeLink::UpdateRead(int32 Size,int chan)
			// Add Size words to read pointer
{
	if (!CkNullBuffer("UpdateRead")) return;
	DataBuffer->UpdateRead(Size,chan);
}

MachWord * DfNodeLink::GetBase()
{
	if (!CkNullBuffer("GetBase")) return 0;
	return DataBuffer->GetBase();
}


MachWord * DfNodeLink::GetEnd()
{
	if(!CkNullBuffer("GetEnd")) return 0;
	return DataBuffer->GetEnd();
}

const MachWord * DfNodeLink::GetReadPtr(int chan)
{
	if (!CkNullBuffer("GetReadPtr")) return 0;
	return DataBuffer->GetReadPtr(chan);
}

void DfNodeLink::UpdateWrite(int32 Size)
		// Add Size words to read pointer
{
	if (!CkNullBuffer("UpdateWrite")) return ;
	DataBuffer->UpdateWrite(Size);
}

MachWord * DfNodeLink::GetWritePtr()
{
	if (!CkNullBuffer("GetWritePtr")) return 0;
	return DataBuffer->GetWritePtr();
}

void DfNodeLink::GraphDisplay(GraphInfo& Graph)
{
	OutTokens& Out = *(Graph.GetOut());
	char Buf[32] ;
	if (!OutputLinks) {
		Graph.OutErrorEnd("NULL OutputLinks!");
		Out.NewLine();
	} else {
		DfNodeOutIterator Next(*OutputLinks);
		DfNodeOut * NodeOut;
		int i = 0;
		while (NodeOut=Next()) {
			if (!Graph.DoCpp()) {
				if (i) {
					Out.NewLine();
					Out.NextConcat("  ...");
					Out.NextOut(Graph.NodeName) ;
				}
				strcpy(Buf,"[");
				strcat(Buf,dec(i++));
				strcat(Buf,"]");
				Out.NextOut(Buf) ;
				NodeOut->GraphDisplay(Graph);
			} else {
				if (i) {
					Out.NextOut(Graph.NetName);
					Out.NextConcat(".Link(");
					Out.NextWrite(Graph.NodeName);
					Out.NextWrite(",");
					Out.NextWrite(strcpy(Buf,
						dec(Graph.OutChannel)));
					Out.NextWrite(")");
					Out.NextFillOut(">>");
				}
				NodeOut->GraphDisplay(Graph);
			}
			if (i++>1) Out.NewLine() ;
		}
	}
}

void DfNodeLink::ClearTraverse()
{
	DfNodeOutIterator Next(*OutputLinks);
	DfNodeOut * Out;
	while (Out=Next()) Out->ClearTraverse() ;
}

TraverseResult DfNodeLink::Traverse(TraverseObject& Obj)
{
	DfNodeOutIterator Next(*OutputLinks);
	DfNodeOut * Out;
	TraverseResult Result = TraverseOK ;
	while (Out=Next()) {
		TraverseResult Temp = Out->Traverse(Obj) ;
		if (Temp >= TraverseError) return Temp ;
		if (Temp > Result) Result = Temp;
	}
	return Result;
}


DfNode * DfNodeLink::FindNode(const DfNode * node)
{
	DfNodeOutIterator Next(*OutputLinks);
	DfNodeOut * Out;
	DfNode * Found ;
	while (Out=Next()) if (Found = Out->FindNode(node)) return Found ;
	return 0;
}

DfNode * DfNodeLink::FindNode(const char * Name)
{
	DfNodeOutIterator Next(*OutputLinks);
	DfNodeOut * Out;
	DfNode * Found ;
	while (Out=Next()) if (Found = Out->FindNode(Name)) return Found ;
	return 0;
}


ErrCode DfNodeLink::Reset()
{
	ErrCode Ret = OK ;
	if (DataBuffer) Ret = DataBuffer->Reset();
	ErrCode Loc = OK ;
	if (OutputArray) for (int i = 0 ; i < OutSize ; i++) {
		if (OutputArray[i]) Loc = OutputArray[i]->Reset();
		if (Loc > Ret) Ret = Loc ;
	}
	return Ret ;
}

int DfNodeLink::CkNullBuffer(const char * name)
{
	if (State.IsError()) return 0;
	if (!DataBuffer) {
		State.Error("no data buffer from",name);
		return 0 ;
	}
	return 1 ;
}

void DfNodeLink::NameDisplay()
{
	TheLog << "DfNodeLink: Input is " ;
	if (InputLink) if (InputLink->GetNode())
		InputLink->GetNode()->NameDisplay() ;
	TheLog << "\nOutput names are:\n" ;
	if (OutputLinks) OutputLinks->NameDisplay() ;
	
}

void DfNodeLink::Display()
{
	TheLog << "DfNodeLink: Input name is " ;
	if (InputLink) {
		if (!InputLink) TheLog << "Null.\n" ;
		else InputLink->NameDisplay() ;
	}
	TheLog << "\nDfNodeLink: Outputs are:\n" ;
	if (OutputLinks) OutputLinks->Display() ;
	
}



DfNode * DfNodeLink::FindTail() 
{
	DfNodeOutIterator Next(*OutputLinks);
	DfNodeOut * ret ;
	DfNode * found ;
	while (ret = Next()) if (ret->GetNode())
		if (found = ret->GetNode()->FindTail(0))
		return found ;
	return 0;
}

int DfNodeLink::DoTail()
{
	if (!this) DbgError("DfNodeLink::DoTail", "NULL this");
	if (!OutputArray) return 0 ;
	int MadeProgress = 0;
	for (int i = 0; i < OutSize ; i++) {
		if (State.IsError()) return 0 ;
		if (!OutputArray[i]) {
			State.Error("null output link");
			return 0;
		}
		int Progress = OutputArray[i]->DoTail();
		if (State.IsError()) return 0 ;
		if (Progress < 0) return Progress ;
		MadeProgress = MadeProgress || Progress ;
	}
	return MadeProgress ;
}

ErrCode DfNodeOut::CheckArithType(const StreamStr& Driver) const
{
	if (!TheNode) DbgError("DfNodeOut::CheckAritghType","no node");
	const StreamStr * str = TheNode->GetInStream(Index);
	if (!str) DbgError("DfNodeOut::CheckArithType","no in str");
	return str->CheckReportArithType(Driver,TheNode,Index);
}

ErrCode DfNodeLink::CheckArithType() const
{
	if (!OutputLinks) return OK ;
	DfNodeOutIterator Next(*OutputLinks);
	const StreamStr* DrvStr = GetDriverStreamStr();
	if (!DrvStr) {
		// HelpOut << "Missing input link in checking arithmetic type.\n" ;
		return FatalError ;
		// DbgError("DfNodeLink::CheckArithType","no driver str");
	}
	const StreamStr& DriverStr = *DrvStr ;
        DfNodeOut * NodeOut;
	ErrCode MaxErr = OK ;
	while (NodeOut=Next()) {
		ErrCode LocErr = NodeOut->CheckArithType(DriverStr);
		if (LocErr > MaxErr) MaxErr = LocErr ;
	}
	return MaxErr ;
}

ProcessNet& ProcessNet::AddThread(DfNode& node)
{
	Threads->Append(&node);
	UpdateAccess(&node);
	return *this ;
}

int ProcessNet::CheckNodeNetwork(DfNode& node)
{
	if (!node.SetActiveNet(this)) {
		State.Error("Node `",node.GetName(),
		"' is alread linked into network `",
		node.GetActiveNet()->GetName(),"'");
		return 0;
	}
	return 1 ;
}

ProcessNet& ProcessNet::operator+(DfNode& node)
{
	// LogOut << "ProcessNet::operator+\n" ;
	if (!CheckNodeNetwork(node)) {
		// LogOut << "returning\n" ;
		return *this ;
	}

	ThreadListIterator Next(*Threads) ;
    Thread * Elt;
	int already_linked = 0 ;
    while (Elt = Next()) if (Elt->GetFirstNode() == &node) {
		already_linked = 1 ;
		for (int i = 0 ; i < node.GetOut(); i++)
			if (node.GetOutLink(i)->is_linked()) {
				already_linked = 2 ;
				break ;
		}
		if (already_linked == 2) {
			State.Error("Node `", node.GetName(),
				"' is alredy a signal node in network `",GetName(),"'");
			return *this ;
		}
	}




	if (TheController) TheController->EditNetwork("thread");
	ProcessNet & Return = *this ;
	if (!already_linked) AddThread(node);
	// LogOut << "Returning\n" ;
	remove_unlinked_node(&node);
	return Return ;
}

void ProcessNet::Display()
{
	TheLog << "ProcessNet:" << GetName() <<"\nThreads in net:\n" ;
	Threads->Display();
	TheLog << "\n Last node accessed:\n" ;
	LastAccessed->NameDisplay();
}

static OutTokens * dum_tok ;

void ProcessNet::list_replacements(NodeReplacementList& list)
{
	// LogOut << "ProcessNet::list_replacements\n" ;
	TraverseParameters * params = new TraverseParameters(list);
	if (!dum_tok) dum_tok = new OutTokens(OutputCppHelp) ;
	TraverseObject obj(*dum_tok,"target replacement list",
		*(dum_tok->GetStream()),params);
	obj.SetForwardAction(&(DfNode::replacement_list));
	Traverse(obj);
}

int ProcessNet::independent_thread(Thread* a, Thread *b)
{
    if (a == b) return 0 ;
	if (!dum_tok) dum_tok = new OutTokens(OutputCppHelp) ;
    DfNode * node_a = a->GetFirstNode();
    DfNode * node_b = b->GetFirstNode();
    TraverseParameters * params = new TraverseParameters(*node_b);
    TraverseObject obj(*dum_tok,"independent thread",
        *(dum_tok->GetStream()),params);
	obj.SetForwardAction(&(DfNode::independent_thread_check));
	Traverse(obj);
	return !params->flag();

}


void ProcessNet::X_graph_display(int16 x, int16 y)
{
	if (x > -1 || y > -1) {
		int16 ar[2] ;
		ar[0] = x ;
		ar[1] = y ;
		PacketHeader head(PacketNetworkDescription,
			GenericArrayElement::window_size,sizeof(ar));
		WriteSeg->WritePacket(head,(char *)ar);
	}
	set_displayed();
	// LogOut << "X_graph_display\n" ;
	// TestAlloc("X_graph_display");
	TransmitNetworkDescription desc(*this) ;
	NetworkDescription this_net(*this);
	desc.send(this_net);
	
	DfNodeIterator Next(unlinked_nodes);
	DfNode * node ;
	while (node = Next()) {
		// TestAlloc(node->GetName());
		NetNodeDescription nd(*node);
		// TestAlloc("desc.send");
		desc.send(nd);
	}
	// TestAlloc("sent");

	TraverseParameters * params = new TraverseParameters(desc);
	if (!dum_tok) dum_tok = new OutTokens(OutputCppHelp) ;
	TraverseObject obj(*dum_tok,"X-windows graph display",
		*(dum_tok->GetStream()), params);
	obj.SetForwardAction(&(DfNode::X_graph_display));
	Traverse(obj);
	desc.send_network_terminator();
}


void ProcessNet::GraphDisplay(char FullDisplay, int16 x, int16 y)
{
/*
 *	*Output+OutputCppHelp<< "In ProcessNet::GraphDisplay, this = 0x"
 *		<< hex((int) this) <<	"\n" ;
 */
	// if (State.IsX()) if (FullDisplay == 4) {
	State.ClearError();
	if (State.IsX()) {
		if (!GetTheController()) CreateController();
		if (!GetTheController()) {
			*Output +OutputCppHelp << "Network `" << GetName() <<
                "' has no controller.\n" ;
            *Output + OutputCppHelp << "It cannot be displayed.\n" ;
			return ;

		}
/*
 *		if (!GetTheController()->AssignBuffers()) {
 *			*Output +OutputCppHelp << "Assign buffers for network `" <<
 *				GetName() << "' failed.\n" ;
 *			*Output + OutputCppHelp << "It cannot be displayed.\n" ;
 *			return ;
 *		}
 */
		X_graph_display(x,y) ;
		return ;
	} 
	char buf[BufSize];
	OutTokens GraphOut(OutputCppHelp) ;
	GraphInfo Graph(GraphOut);
	Graph.NetName = GetName();
	Graph.ClearFlags();
	if (FullDisplay==1) Graph.SetFull();
	if (FullDisplay==2) Graph.SetTime();
	ThreadListIterator Next(*Threads) ;
	int i = 0;
	Thread * Elt;
	while (Elt = Next()) {
		Graph.NodeName = Elt->GetName();
		Graph.ClearContinuation();
		GraphOut.NextConcat("Input{");
		GraphOut.NextConcat(strcpy(buf,dec(i++)));
		GraphOut.NextConcat("}:");
		DfNode * TheNode = Elt->GetFirstNode();
		if (TheNode) TheNode->GraphDisplay(Graph);
		else GraphOut.NextOut("NULL BASE NODE!");
		GraphOut.NewLine() ;
	}
}

void ProcessNet::UpdateAccess(DfNode * node)
{
	LastAccessed = node ;
	if (node->MultipleOutputs()) LastMultipleAccessed = node ;
	NextOut = 0 ;
	TheFlag = 1 ; // Disable ability to have multiple links
}

ProcessNet& ProcessNet::operator>>(DfNode& node)
{
/*
 *	LogOut<<"ProcessNet::operator>> for node `"<< node.GetName() << "'.\n" ;
 *	if (LastAccessed) LogOut << "LastAccessed: " << LastAccessed->GetName()
 *		<< "\n" ;
 */
	if (!node.GetIn()) {
		State.Error("No input channels in node `",
			node.GetName(), "' to link to");
		return *this ;
	}
	if (!CheckNodeNetwork(node)) return *this ;
	if (!LastAccessed) {
		State.Error("no last access pointer in node `",
			node.GetName(), "'");
		return *this;
	}
	if (!LastAccessed->GetOut()) {
		State.Error("No output channels in node `",
			LastAccessed->GetName(), "' to link from");
		return *this ;
	}
	if (TheFlag) {
		NextOut = LastAccessed->GetFreeOutLink();
		if (NextOut < 0) if (LastMultipleAccessed) 
			NextOut = LastMultipleAccessed->GetFreeOutLink();
		else LastAccessed = LastMultipleAccessed ;
		if (NextOut < 0) {
			LastMultipleAccessed = 0 ;
			State.Error( "it is unclear where to append node `",
				node.GetName(), "' in network `",GetName(),",");
			return *this ;
		} 
	}
	LastAccessed->Append(&node,NextOut,-1,TheFlag);
	if (State.IsError()) {
		add_unlinked_node(&node);
		// restore association which may have been deleted
		return (*this);
	}
	UpdateAccess(&node);	
	remove_unlinked_node(&node);
	return (*this) ;
}

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

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


ProcessNet& ProcessNet::Link(DfNode& node, int OutChannel)
{
/*
 *	LogOut << "ProcessNet::Link(" <<  node.GetName() << ", " << OutChannel <<
 *		")\n" ;
 */
	if (!CheckNodeNetwork(node)) return *this ;
	NextOut = OutChannel ;
	DfNode * TheNode = this->FindNode(&node);
	if (!TheNode) {
		State.Error("there is no node `", node.GetName(),
			"' in this network") ;
	}
/*
 *	LogOut << "TheNode `" << TheNode->GetName() << "', Out = " <<
 *		TheNode->GetOut() << ", OutChannel = " << OutChannel << "\n" ;
 */
	if (TheNode->GetOut() <= OutChannel) {
		State.Error("nonexistent output channel referenced in `",
			TheNode->GetName(), "'") ;
		NextOut = 0 ;
		return *this;
	}
	if (TheController) TheController->EditNetwork("node");
	LastAccessed = TheNode ;
	TheFlag = 0 ; // allow multiple taps from a buffer
	return *this ;
}

ProcessNet& ProcessNet::Link(const char * NodeName, int OutChannel)
{
	NextOut = OutChannel ;
	DfNode * TheNode = this->FindNode(NodeName);
	if (!TheNode) {
		State.Error("there is no node `",NodeName,
			"' in this network") ;
		return * this ;
	}
	if (TheNode->GetOut() <= OutChannel) {
		State.Error("nonexistent output channel referenced in `",
			TheNode->GetName(), "'") ;
		NextOut = 0 ;
		return *this;
	}
	if (!CheckNodeNetwork(*TheNode)) return *this ;
	if (TheController) TheController->EditNetwork("node");
	LastAccessed = TheNode ;
	TheFlag = 0 ; // allow multiple taps from a buffer
	return *this ;
}


ProcessNet& ProcessNet::SelfLink(DfNode& SourceLink, DfNode& DestLink,
	int SourceChannel, int DestChannel)
{
	DfNode * SourceNode = FindNode(&SourceLink) ;
	DfNode * DestNode = FindNode(&DestLink);
	if (!SourceNode || !DestNode) {
		State.Error(
		"cannot find source and/or destination for self link");
		return *this;
	}
	if (!CheckNodeNetwork(*SourceNode)) return *this ;
	if (!CheckNodeNetwork(*DestNode)) return *this ;
	SourceNode->Append(DestNode,SourceChannel,DestChannel,2);
	// 2 sets flag for self link
	return *this ;
}

ProcessNet& ProcessNet::SelfLink(char * SourceLink, char * DestLink,
	int SourceChannel, int DestChannel)
{
	DfNode * SourceNode = FindNode(SourceLink) ;
	DfNode * DestNode = FindNode(DestLink);
	if (!SourceNode || !DestNode) {
		State.Error(
		"cannot find source and/or destination for self link");
		return *this;
	}
	if (!CheckNodeNetwork(*SourceNode)) return *this ;
	if (!CheckNodeNetwork(*DestNode)) return *this ;
	SourceNode->Append(DestNode,SourceChannel,DestChannel,2);
	// 2 sets flag for self link
	return *this ;
}


void ProcessNet::add_unlinked_node(DfNode * node)
{
	// LogOut << "ProcessNet::add_unlinked_node\n" ;
	if (node->CheckLinked()) return ;
	// LogOut << "not linked\n" ;
	ProcessNet * net = node->GetActiveNet() ;
	if (net) {
		net->remove_unlinked_node(node);
		node->SetActiveNet(0);
	}
	if (!unlinked_nodes.InList(node)) {
		// LogOut << "appending to unlinked nodes\n" ;
		unlinked_nodes.Append(node);
	}
	node->SetActiveNet(0);
	node->SetActiveNet(this);
}

void ProcessNet::remove_unlinked_node(DfNode * node)
{
	unlinked_nodes.RemoveEntry(node);
}

void ThreadList::Display()
{
	if (!this) return ;
	ThreadListIterator Next(*this);
	Thread * Curr;
	while (Curr = Next()) Curr->Display();
}

void ThreadList::NameDisplay()
{
	if (!this) return ;
	ThreadListIterator Next(*this);
	Thread * Curr;
	while (Curr = Next()) Curr->NameDisplay();
}

void ThreadList::Describe(OutTokens& Out, ListEntity Option)
		// interactive execution list
{
	if (!this) {
		State.Error("null node");
		return ;
	}
	ThreadListIterator Next(*this) ;
	Thread * TheNode;
	while(TheNode = Next()) TheNode->Describe(Out,Option) ;
}

ErrCode ThreadList::Remove(DfNode * node)
{
	ThreadListIterator Next(*this);
	Thread * check;
	while (check=Next())
		if(node == check->GetFirstNode()) {
			return Remove(check);
		}
	return FatalError ;
}

int ThreadList::CheckComplete(const char * Name )
{
	if (State.IsError()) return 0 ;
	int Complete = 1;
	ThreadListIterator Next(*this);
	Thread * Elt ;
	while (Elt = Next()) if (!Elt->CheckComplete()) {
		Complete = 0;
		if (State.IsError()) break ;
		State.Error("incomplete thread list from `", Name,
			"' in thread `", Elt->GetName(), "'") ;
		break ;
	}
	return Complete;
}

int ThreadList::DoInitAfterLinked(const char * Name )
{
	if (State.IsError()) return 0 ;
	int Complete = 1;
	ThreadListIterator Next(*this);
	Thread * Elt ;
	while (Elt = Next()) if (!Elt->DoInitAfterLinked()) {
		Complete = 0;
		if (State.IsError()) break ;
		State.Error("post link initialization error for network `", Name,
			"' for thread starting with `", Elt->GetName(), "'") ;
		break ;
	}
	return Complete;
}

void ProcessNetsInit()
{
	ProcessNetList = new EntityList ;
	IntEntProcessNet = new InteractiveEntity("ProcessNet",
		ProcessNetList, 0,InteractiveNet,"network.h", 0, "Networks");

	TheNets->Append(IntEntProcessNet) ;
}

static InitObj LocalInit(ProcessNetsInit, "ProcessNet", "Networks");


