/*  netcnt.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <stream.h>

#include "intfc.h"
#include "user.h"
#include "outtok.h"
#include "usercom.h"

#include "net.h"
#include "netcnt.h"
#include "thread.h"
#include "cgidbg.h"
#include "thread.h"
#include "timing.h"
#include "netlnk.h"
#include "buffer.h"
#include "yacintfc.h"
#include "cbuf.h"
#include "help.h"
#include "interinit.h"
#include "dspe_app.h"


// void TestAlloc(const char * msg);

Thread * NetControl::first_strand(Thread * thread)
{
	ThreadListIterator next(*(Net->GetThreads()));
	Thread  * thr ;
	while (thr = next()) {
		if (thr == thread) return thread ;
		if (!Net->independent_thread(thread,thr)) return thr ;
	}
	return thread ;
}

int NetControl::AssignResampling(ThreadList * BaseNodes)
{
	// LogOut << "static ::AssignResampling\n" ;
	// start computing resampling at each thread - work forwards
	// and backwards until complete or an inconcistency occurs
	ThreadListIterator Next(*BaseNodes) ;
	Thread * Base ;
	int AssignsOk = 1 ;
	while (Base = Next()) {
		int Temp = Base->AssignTiming(Base != first_strand(Base)) ;
		AssignsOk = Temp && AssignsOk ;
	}
	// LogOut << "AssignTimingDone\n" ;
	return AssignsOk ;
}

static void InitSampleRate(ThreadList * BaseNodes)
{
	ThreadListIterator Next(*BaseNodes) ;
	Thread * Base ;
	while (Base = Next()) {
		double InitialRate = 1.0 ;
		DfNode * First = Base->GetFirstNode();
		if (!First) DbgError("InitSampleRate","null node");
		TimingDescription * Timing = First->GetTiming(0);
/*
 *		LogOut << "InitSampleRate for `" << First->GetName()
 *			<< "', Timing = 0x" << hex << (void *) Timing
 *			<< dec << "\n" ;
 */
		if (Timing) {
			if (Timing->GetSampleRate() > 0.0) 
				InitialRate = Timing->GetSampleRate();
			else Timing->SampleRate = InitialRate ;
/*
 *			LogOut << "Timing->GetSampleRate() = " <<
 *				Timing->GetSampleRate() << "\n" ;
 */
		}
		First->SetSampleRate( InitialRate ,0);
	}
	// LogOut << "Exit InitSampleRate\n" ;
}

NetControl::~NetControl()
{
	// LogOut << "~NetControl for `" << GetName() << "'\n" ;
	DeassignBufferDescriptor();
	if (Net) Net->clear_controller();
}

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

int NetControl::clear_buffer_descriptor(BufferDescript * to_clear)
{
/*
 *	LogOut << "clear_buffer_descriptor for `" << GetName() << "'.\n" ;
 *	if (to_clear) LogOut << "clearing `" << to_clear->GetName() << "'.\n" ;
 */
	if (to_clear) if (to_clear != TheBufferDescript) {
		State.Error("attempt to clear descriptor\n`", to_clear->GetName(),
			"', from net `", GetName() , "'\nwhich is using descriptor `",
			TheBufferDescript ? TheBufferDescript->GetName() :
			"NONE" );
		return 0 ;
	}
	if (!TheBufferDescript) return 1 ;
	if (CheckExecuting("clear buffer descriptor")) return  0;
	AssignOk = InitAfterLinkedOk = TimingOk = NetComplete = 0;
	if (!ClearBuffers()) return 0 ;
	TheBufferDescript = 0 ;
	return 1 ;
}

int NetControl::check_safe_delete()
{
	return 1;
}

void NetControl::EditNetwork(const char * EditType)
{
	// LogOut << "NetControl::EditNetwork(" << EditType << ")\n" ;
	if (AssignOk) {
		ClearBuffers();
		if (!strcmp(EditType,"disconnect")) HelpDo.ConfirmAction(
            "Clearing buffers to disconnect a node from the network.") ;
		else HelpDo.ConfirmAction(
			"Clearing buffers to add a ",EditType,
			" to the network.") ;
	}
	NetComplete = 0;
}

int NetControl::is_complete()
{
	if (!Net->GetThreads()) return 0 ;
	if (!Net->GetThreads()->Size()) return 0 ;
	if (!NetComplete) return 0 ;
	if (!TimingOk) return 0 ;
	if (!InitAfterLinkedOk) return 0 ;
	return 1 ;
}


int NetControl::CheckComplete()
{
	// LogOut << "NetControl::CheckComplete for `" << GetName() << "'\n" ;
	
	if (!Net) {
		State.Error("null network parameter for controller `",
			GetName(), "'");
		return 0;
	}
	for (;;) {
		if (Net->GetThreads()) if (Net->GetThreads()->Size()) break ;
		State.Error("no threads to process in controller `",
			GetName(), "'");
		return 0;
	}
	// LogOut << "After check threads\n" ;
	if (!NetComplete) if (!(NetComplete =
		Net->GetThreads()->CheckComplete(Net->GetName()))) return 0;

	// LogOut << "After check threads\n" ;

	if (!TimingOk) {
		timing_error = 0 ;
		TimingOk = AssignResampling(Net->GetThreads()) ;
	}

	// LogOut << "After AssignResampling\n" ;


	if (!TimingOk) {
		*Output + OutputCppHelp  <<
			"There is no way to assign consistent timing to this network.\n"  ;
		if (Net->is_exact_timing()) {
			State.Error("bad timing aborted execution") ;
			return 0 ;
		}
		*Output + OutputCppHelp <<
			"It may may still be executed, but it may block.\n" ;
	}
	
	// LogOut << "Exit CheckComplete\n" ;
	return 1 ;
}

void NetControl::ClearNetwork()
{
	// LogOut << "NetControl::ClearNetwork\n" ;
	if (!Net) return ;
	Net->ClearNetwork();
}

void NetControl::set_buffer_descriptor()
{
	if (TheBufferDescript) return ;
	BufferDescript * des = 0 ;
	const char * BufName = "CircBufDesDef" ;
	InteractiveEntity * ent =
		AllEntityLists->GetInteractiveEntityFromObj(BufName);
	if (ent) if (ent->IsClassMember("BufferDescript")) {
		UserEntity * def = AllEntityLists->GetObject( BufName);
		des = (BufferDescript *) def ;
	}
	if (!des) {
		BufName = AllEntityLists->make_valid_name(BufName);
		des = new CircBufDes( BufName, 2048);
	}
	set_buffer_descriptor(*des);
}

void NetControl::set_buffer_descriptor(BufferDescript& des)
{
	DeassignBufferDescriptor();
	TheBufferDescript = &des ;
}

void NetControl::DeassignBufferDescriptor()
{
	if (TheBufferDescript) {
		if (Net) Net->DeassignBufferDescriptor(TheBufferDescript);
		TheBufferDescript->DeAssign(this);
	}
	TheBufferDescript = 0;
}

int NetControl::AssignBuffers(BufferDescript& Des)
{
	// TestAlloc("NetControl::In AssignBuffers");
	if (!CheckComplete()) {
		*Output + OutputCppHelp << "Network is incomplete or has a fatal timing error.\n" ;
		return 0;
	}
	node_reset();
	if (AssignOk) if (TheBufferDescript == &Des) return AssignOk ;
	DeassignBufferDescriptor();
	// TestAlloc("AssignBuffers - CheckComplete done");
	AssignOk = Net->DoAssignBuffers(&Des);
	// TestAlloc("bk1");
	if (AssignOk) {
		TheBufferDescript = &Des ;
		Des.AssignTo(this);
		InitSampleRate(Net->GetThreads());
		if (!InitAfterLinkedOk) {
			Net->ClearTraverse();
			// LogOut << "Doint InitAfterLink\n" ;
			InitAfterLinkedOk =
				Net->GetThreads()->DoInitAfterLinked(Net->GetName());
			// LogOut << "Doint InitAfterLink done\n" ;
			Net->ClearTraverse();
		}
		if (!InitAfterLinkedOk) {
			*Output + OutputCppHelp  << "Post linking initialization failed.\n" ;
			*Output << "This network cannot be executed.\n" ;
			AssignOk = 0 ;
		}
	}
	// LogOut << "Exit AssignBuffers - status is " << AssignOk << "\n" ;
	return AssignOk;
}

int NetControl::CheckExecuting(const char * Msg)
{
	if (!Executing) return 0;
	if (State.IsVerbose()) *Output + OutputCppHelp <<
		"The network controlled by `" << GetName()
		<< "' is currently executing.\nIt is not possible to "
		<< Msg << " until execution has been completed.\n" ;
	// LogOut << "Currently executing cannot `" << Msg << "'\n" ;
	State.Error("network currently executing");
	return 0;
}


void NetControl::node_reset()
{
	Thread * thread ;
	DfNode * node ;
	ThreadListIterator Next(*(Net->GetThreads())) ;
	while (thread  = Next()) if (node = thread->GetFirstNode())
		node->DoReset();
}

int NetControl::ClearBuffers()
{
	// LogOut << "NetControl::ClearBuffers\n" ;
	if (!Net) return 1 ;
	if (CheckExecuting("clear buffers")) return  0;
	node_reset();
	AssignOk = 0;
	TimingOk = 0;
	InitAfterLinkedOk = 0 ;
	NetComplete = 0;
	Net->ClearBuffers();
	return !State.IsError();
}

int NetControl::DeleteAllLinks(DfNodeList * List)
{
	// LogOut << "NetControl::DeleteAllLinks\n" ;
	if (!Net) return  1;
	if (CheckExecuting("delete network")) return 0 ;
	int DeleteOk = 1 ;
	int BufferOk = ClearBuffers();
	// ThreadListIterator Next(*(Net->GetThreads())) ;
	DeleteOk = Net->DeleteAllLinks(List);
	// LogMsg("DeleteAllLinks - exiting");
	return DeleteOk && BufferOk ;
}

int NetControl::DeleteNetwork()
{
	int DeleteOk=1;
	DfNodeList ToDelete ;
	int LinkOk = DeleteAllLinks(&ToDelete);

	DfNode * Node ;
	DfNodeList AlreadyDeleted ;
	while (Node=ToDelete.Get()) {
		if (AlreadyDeleted.InList(Node)) continue;
		AlreadyDeleted.Append(Node);
		delete Node ;
	}
	AlreadyDeleted.Clear();
	// LogMsg("DeleteNetwork - exiting");
	return LinkOk && DeleteOk;
}


static void NetworkIncomplete(NetControl * Con,ProcessNet * Net, const char *m)
{
	if (!Net) {
		*Output + OutputCppHelp << "Controller `" << Con->GetName() <<
			" was not initialize properly and cannot be used.\n" ;
		return ;
	}
	
	*Output + OutputCppHelp << "Network `" << Net->GetName() <<
		"'\nis incomplete or the timing could not be resolved and exact timing is set.\n";
}

void NetControl::GraphDisplay(int Option)
{
	if (!Net) return ;
	if (Option == 2) if (!CheckComplete()) {
			NetworkIncomplete(this,Net,
				"analysing and displaying the timing");
			return ;
		}
	Net->GraphDisplay(Option);
}




int NetControl::AssignBuffers()
{
	if (AssignOk) return 1 ;
	if (!TheBufferDescript) set_buffer_descriptor();
	if (TheBufferDescript)  return AssignBuffers(*TheBufferDescript) ;
	return 0 ;
}


void NetControl::DoExecute(int32 k)
{
	// If buffers are not assigned than assign buffers
	
	// Get initial nodes and append to list if not done so already
	// LogOut << "Before NetworkIncomplete\n" ;
	if (!CheckComplete()) {
		NetworkIncomplete(this,Net,"executing");
		return ;
	}
	if (!AssignBuffers()) return ;
	// LogOut << "Network complete\n" ;
	NodeExecuteList NodeExLst ;
	ThreadListIterator Next(*(Net->GetThreads())) ;
	DfNode * Node ;
	Thread * TheThread ;
	TimingDescription * BaseTiming = 0 ;
	Executing = 1;
	while (TheThread=Next()){ 
		Node = TheThread->GetFirstNode();
		// LogOut << "First node in thread is `" << GetName() << "'\n" ;
		if (!Node) {
			State.Error("null node at the beginning of a thread");
			Executing = 0;
			return ;
		}
		DfNode * node = first_strand(TheThread)->GetFirstNode() ;
		int32 base_chunck_size = node->GetOutBlockSize() * node->GetIncrementOut(0);
		int32 N=k;
		if (TimingOk) {
			// LogMsg("Get timming");
			TimingDescription * ThisTiming = Node->GetTiming(0);
			// LogOut << "Got timing.\n" ;
			if (!BaseTiming) BaseTiming = ThisTiming ;
			else {
			    double K = k *
				(double) ThisTiming->NumeratorSampling *
				(double) BaseTiming->DenominatorSampling / (
				base_chunck_size *
				(double) ThisTiming->DenominatorSampling *
				(double) BaseTiming->NumeratorSampling) ;
			    N = (int32) (K + .5) ;
			    if (!N) N = 1;
			}
			// LogOut << "After divide\n" ;
		}
		NodeExLst.Append( new NodeExecute(*Node,N));
/*
 *		LogOut << "Appended Node `" << Node->GetName() <<
 *			"' to ExeList to execute " << N << " times.\n" ;
 */


	}
	int Result = DoNetNodeList(NodeExLst);
	// LogOut << "AfterDoNetNodeList\n" ;
	if (Result < 0) *Output + OutputCppHelp << "Network execution aborted.\n" ;
	
	Executing = 0 ;
}




int NetControl::ExecuteHeads(NodeExecuteList& ExeList)
{
	// LogOut << "ExecuteHeads\n" ;
	int32 Times ;
	int MadeProgress = 0;
	NodeExecuteIterator Next(ExeList) ;
	NodeExecute * NodeExe;
	int32 times  ;
	while (NodeExe=Next()) if (times = NodeExe->GetTimes()) {
/*
 *		LogOut << "ExecuteNode(" << NodeExe->GetNode()->GetName() <<
 *			") " << times << ".\n" ;
 */
		Times = NodeExe->GetNode()->ExecuteNode(times) ;
		// LogOut << "ExecuteHeads, Times = " << Times << "\n" ;
		if (Times < 0) return Times ;
		MadeProgress = MadeProgress || Times ;
		if (Times) NodeExe->DoneTimes(Times) ;
		if (State.IsError()) return 0 ;
/*
 *		LogOut << "ExecutedNode " << NodeExe->GetNode()->GetName() <<
 *			" " << Times << " times.\n" ;
 */
	}
	return MadeProgress ;
}


int NetControl::ExecuteTails(NodeExecuteList& ExeList)
{
	int MadeProgress = 0;
	NodeExecuteIterator Next(ExeList) ;
	NodeExecute * NodeExe;
	// LogOut <<  "NetControl::ExecuteTails\n" ;
	while (NodeExe=Next()) {
/*
 *		LogOut << "ExecuteTail(" << NodeExe->GetNode()->GetName() <<
 *			").\n" ;
 */ 
		int Progress = NodeExe->GetNode()->ExecuteTail() ;
		if (Progress < 0) return Progress ;
		MadeProgress = MadeProgress || Progress ;
/*
 *		 LogOut <<  "NetControl::ExecuteTails Test IsError = " <<
 *			State.IsError() << "\n" ;
 */
		if (State.IsError()) return 0;
/*
 *		LogOut << "Done ExecuteTail(" <<
 *			NodeExe->GetNode()->GetName() << ").\n" ;
 */
	}
		
	return MadeProgress ;
}

int NetControl::DoNetNodeList(NodeExecuteList& ExeList)
{
// Algorithm is to do each of the base nodes for as many executions
// as possible. Then the tree, starting at each of these nodes is
// executed as much as possible. These two methods are repeated noting
// each time if any progress is made. If no progress is made in
// the initial nodes and the tail then more pass is made through the
// tail. If no progress is made in this second pass no progress is possible.

// First check that list is valid

	NodeExecuteIterator Next(ExeList) ;
	NodeExecute * NodeExe;
	DfNode * DoNode ;
	while (NodeExe=Next()) {
		if (!NodeExe->GetTimes()) 
			State.Warning("request to executed node list 0 times");
		DoNode=NodeExe->GetNode() ;

		if (!DoNode) {
			State.Error("null node in reference list");
			return -FatalError ;
		}

	}

	// Now do execution
/*
 *	LogOut << "Executing thread starting with `" <<
 *		DoNode->GetName() << "'\n" ;
 */ 

	int MadeProgress = 1;
	while (MadeProgress) {
		MadeProgress = 0 ;
		int Progress = ExecuteHeads(ExeList) ;
		if (Progress < 0) return Progress ;
		MadeProgress = MadeProgress || Progress ;
		if (State.IsError()) return -FatalError;
		// LogOut << "ExecuteTails 1\n" ;
		Progress = ExecuteTails(ExeList) ;
		if (Progress < 0) return Progress ;
		MadeProgress = MadeProgress || Progress ;
		if (State.IsError()) return -FatalError ;
		// if (!MadeProgress) LogOut << "ExecuteTails 2\n" ;
		if (!MadeProgress) MadeProgress = ExecuteTails(ExeList) ;
		if (MadeProgress < 0) return MadeProgress ;
		if (State.IsError()) return -FatalError;
	}

	// Complain if we couldn't complete execution

	NodeExecuteIterator LNext(ExeList) ;
	int error = 0 ;
	while (NodeExe=LNext()) if (NodeExe->GetTimes()) if (NodeExe->GetNode())
	{
		error = 1 ;
		*Output + OutputCppHelp << "Left " << NodeExe->GetTimes() <<
			" times unexecuted in node `" <<
			NodeExe->GetNode()->GetName() << "'.\n" ;
		ErrCode st = NodeExe->GetNode()->GetNodeState();
		if (st < EndOfData) {
			if (timing_error) if (Net->GetThreads()->Size() > 1)
			*Output << "This may be due to the inability to fully analyze timing in this network\n" 
				<< "or you may need to increase the buffer size.\n" ;
			else *Output << "You may need to increase the buffer size.\n" ;
		}
		if (st !=  OK) if (st > OK && st <= FatalError)
			*Output + OutputCppHelp << "The state of this node is `" <<
				DspApplication::err_code_names[st] << "'.\n" ;
		else *Output + OutputCppHelp <<
			"This node has an undefined state (" << (int) st << ").\n" ;
	}
	if (error) return -FatalError ;
	return OK ;
}

static EntityList * NetControlList =0 ;
static InteractiveEntity * IntEnt ;


NetControl::NetControl(const char * Name, ProcessNet& net):
	UserEntity(Name)
{
	// LogOut << "NetControl::ctor `" << Name << "'\n" ;
	Net= &net;
	AssignOk = 0;
	TimingOk = 0;
	InitAfterLinkedOk = 0 ;
	Executing = 0;
	NetComplete = 0 ;
	TheBufferDescript = 0 ;
	if (!NetControlList) NetControlsInit() ;
	// NetControlList->Append(MakeDeclaredEntity(this,IntEnt)) ;
	Net->SetTheController(this);
	if (Net->GetTheController() != this) DbgError("NetControl::NetControl",
		"cannot set controller");
}


void NetControlsInit()
{
	NetControlList = new EntityList ;

	IntEnt = new InteractiveEntity("NetControl",
		NetControlList, 0, InteractiveScheduler,
		"netcnt.h", 0);

	TheSchedulers->Append(IntEnt) ;
}

static InitObj LocalInit(NetControlsInit, "NetControl", "Schedulers");

