/*  feedbck.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */

#include "network.h"
#include "netlnk.h"
#include "feedbck.h"
#include "usercom.h"
#include "cgidbg.h"



class LoopMemberList: public SingleList {
public:
	ErrCode Insert(LoopMember *nt) {return SingleList::Insert(nt);}
	ErrCode Append(LoopMember *nt) {return SingleList::Append(nt);}
	LoopMember * Get()   {return (LoopMember *) SingleList::Get();}
	LoopMember * Pop() {return (LoopMember *) SingleList::Pop();}
	LoopMember * GetNFromTop(int N) ;
	LoopMember * GetNthEntry(int N) ;
	LoopMember * GetFirst() {return (LoopMember *) SingleList::GetFirst();}
	LoopMemberList(){;}
	void Clear() ;
	~LoopMemberList(){Clear();}
	int Size(){return SingleList::Size();}
	int CheckLoopTiming() ;
} ;

class LoopMemberListIterator: public SingleListIterator {
public:
	LoopMemberListIterator(LoopMemberList& df):
		SingleListIterator((SingleList&) df){}
	LoopMember * operator()()
		{return (LoopMember *) Next();}
};

void LoopMemberList::Clear()
{
	LoopMemberListIterator Next(*this) ;
	LoopMember * Member ;
	while (Member = Next()) delete Member ;
}


class LoopList: public SingleList {
public:
	ErrCode Insert(LoopMemberList *nt) {return SingleList::Insert(nt);}
	ErrCode Append(LoopMemberList *nt) {return SingleList::Append(nt);}
	LoopMemberList * Get()   {return (LoopMemberList *) SingleList::Get();}
	LoopMemberList * Pop() {return (LoopMemberList *) SingleList::Pop();}
	LoopMemberList * GetNFromTop(int N) ;
	LoopMemberList * GetNthEntry(int N) ;
	LoopMemberList * GetFirst() {return (LoopMemberList *)
			SingleList::GetFirst();}
	void RemoveEntry(LoopMemberList * Item) {SingleList::RemoveEntry(
		(Entity) Item);}
	LoopList(){;}
	~LoopList(){/* Clear();*/ }
	int Size(){return SingleList::Size();}
} ;

class LoopListIterator: public SingleListIterator {
public:
	LoopListIterator(LoopList& df):
		SingleListIterator((SingleList&) df){}
	LoopMemberList * operator()()
		{return (LoopMemberList *) Next();}
};

FeedbackLoop::FeedbackLoop()
{
	AllLoops = new LoopList ;
}

FeedbackLoop::~FeedbackLoop()
{
	if (!AllLoops) return ;
/*
 *	LoopListIterator Next(*AllLoops) ;
 *	LoopMemberList * List ;
 *	while (List = Next()) List->Clear() ;
 */
	delete AllLoops ;
	
}

void FeedbackLoop::AppendNode(DfNodeInLink *In, DfNodeOutLink *Out)
{
	if (!AllLoops) AddLoop();
	LoopListIterator Next(*AllLoops) ;
	LoopMemberList * List ;
	while (List = Next()) {
		List->Append(new LoopMember(In,Out));
		// LogOut << "Added 0x" << hex((long)Out) << " to 0x" <<
		// 	hex((long)List) << "\n" ;
	}
}

void FeedbackLoop::AddLoop()
{
	if(!AllLoops) AllLoops = new LoopList ;
	LoopMemberList * temp ;
	AllLoops->Append(temp = new LoopMemberList) ;
	// LogOut << "Created new list 0x" << hex((long)temp) << "\n" ;
}

int FeedbackLoop::CheckEndFeedback(DfNodeOutLink * CheckEnd)
{
	// LogOut << "CheckEndFeedback called(0x" << hex((long)CheckEnd) <<")\n" ;
	LoopMemberList * LoopList ;
	if (!AllLoops) return 1 ;
	LoopListIterator Next(*AllLoops) ;
	int Return = 1 ;
	while (LoopList = Next()) {
		// LogOut << "Checking list 0x" << hex((long)LoopList) << "\n" ;
		LoopMember * Member = LoopList->GetFirst();
		if (!Member) {
			// LogOut << "Null member.\n" ;
			continue ;
		}
		// LogOut << "Checking against 0x " <<
		// 	hex((long) Member->Out) << ".\n" ;
		if (CheckEnd != Member->Out) continue ;
		int Results = LoopList->CheckLoopTiming() ;
		AllLoops->RemoveEntry(LoopList) ;
		delete LoopList ;
		Return = Return && Results ;
		if (!AllLoops->Size()) {
			delete AllLoops ;
			AllLoops = 0;
		}
	}
	return Return ;
}

void FeedbackLoop::Dump()
{
	LoopListIterator Next(*AllLoops) ;
	LoopMemberList * MemList ;
	while (MemList = Next()) {
		char buf[32] ;
		 TheLog << "Contents of list " << (void *) MemList <<
		 	" follow:\n" ;
		LoopMemberListIterator NextMember(*MemList) ;
		LoopMember * Memb ;
		while (Memb = NextMember()) TheLog << (void *)  Memb
			<< " (" << (void *) Memb->In << ", " <<
			(void *)  Memb->Out << ").\n" ;
	}
	TheLog <<"____________\n" ;
}

void TimingDescription::AddFeedbackNode(DfNodeInLink * In, DfNodeOutLink * Out)
{
/*
 *	LogOut << "Adding (0x" << hex((long) In) << ", 0x" << hex((long) Out)
 *		<< ")\n" ;
 *	if (In) LogOut << "Overlap = " << In->GetOverlap() << ", Delay = " <<
 *			In->GetDelay() << "\n" ;
 *	if (Out) {
 *		Out->GetDriverNode()->NameDisplay() ;
 *		Out->NameDisplay() ;
 *	}
 */
	// int Return = CheckEndFeedback(Out) ;
	if (!IsInFeedbackLoop()) SetFeedbackLoop();
/*
 *	LogOut << "Set feedback loop for " <<  Out->GetDriverNodeName()
 *		<< ", IsIn = " << Out->GetDriverNode()->IsInFeedbackLoop()
 *		<< "\n" ;
 *	LogOut << "IsIn (local) = " << IsInFeedbackLoop() << "\n" ;
 *
 *	LogOut << "Before:\n" ;
 *	Loop->Dump() ;
 */
	Loop->AppendNode(In,Out);
/*
 *	LogOut << "After:\n" ;
 *	Loop->Dump() ;
 */
}

void TimingDescription::SetFeedbackLoop()
{
	if (!Loop) {
		Loop = new FeedbackLoop ;
		Loop->AllLoops->Append(new LoopMemberList) ;
	}
}

void TimingDescription::Merge (FeedbackLoop * ToMerge)
{
	if (!Loop) {
		// LogOut << "Initializing loop to Merge.\n" ;
		Loop = ToMerge ;
	} else Loop->Merge(ToMerge);
}

void FeedbackLoop::Merge(FeedbackLoop * ToMerge)
{
	if (!ToMerge) return ;
	// LogOut << "Doing Merge.\n" ;
	if (!ToMerge->AllLoops) {
		// LogOut << "Null AllLoops\n" ;
		return ;
	}
	LoopMemberList * LoopList ;
	while (LoopList = ToMerge->AllLoops->Get()) {
		// LogOut << "Found a list 0x" << hex((long) LoopList) << "\n" ;
		AllLoops->Append(LoopList);
/*
 *		LoopMember * Item ;
 *		LoopMemberListIterator Next(*LoopList) ;
 *		while (Item = Next()) LogOut << "Merging 0x" <<
 *			hex((long) Item->Out) << "\n" ;
 */
	}
	delete ToMerge ;
}


int TimingDescription::CheckEndFeedback(DfNodeOutLink * CheckEnd)
{
	if (!Loop) return 1 ;
	int Return = Loop->CheckEndFeedback(CheckEnd) ;
	if (!Loop->AllLoops) {
		delete Loop ;
		Loop = 0 ;
	}
	return Return ;
}

enum DoneStateOptions {DoneNoInit, DoneWaiting, DoneDoInit, DoneDoCheck} ;

struct NodeChar {
	int IncrementIn;
	int IncrementOut ;
	int Overlap ;
	int Delay ;
	int Inputs ;
	int TotalInputs ;
	int Outputs ;
	int TotalOutputs ;
	int TimeLastOutput ;
	int CurrentTime ;
	int MaxDelay ;
	int OldMaxDelay ;
	int InitDelay ;
	int ReferenceInputs ;
	enum DoneStateOptions DoneState ;
	NodeChar(int IncIn, int IncOut, int Ovlp, int Del) 
		{IncrementIn = IncIn; IncrementOut = IncOut;
		Overlap = Ovlp ; InitDelay = Delay = Del;
		TotalInputs = TotalOutputs = Inputs = Outputs = MaxDelay =
		CurrentTime = TimeLastOutput = 0; OldMaxDelay = -1 ;
		DoneState = DoneNoInit ;}
	void Dump();
	int Simulate(int In, int& Done) ;
};



int NodeChar::Simulate(int In, int& Done)
{
	CurrentTime++;
	Outputs = 0 ;
	Inputs += In ;
	TotalInputs += In ;
	int temp = Inputs - Overlap + InitDelay ;
	while (temp >= IncrementIn) {
		temp -= IncrementIn ;
		Inputs -= IncrementIn ;
		Outputs += IncrementOut ;
	}
	TotalOutputs  += Outputs ;
	if (Outputs) {
		Inputs += InitDelay ;
		InitDelay = 0 ;
		int Diff = CurrentTime - TimeLastOutput ;
		if (Diff > MaxDelay) MaxDelay = Diff ;
		if (OldMaxDelay < 0) OldMaxDelay = MaxDelay ;
		if ( MaxDelay > OldMaxDelay) {
/*
 *			LogOut << "******************************************\n" ;
 *			LogOut << "OldMaxDelay = " << OldMaxDelay <<
 *				", MaxDelay = " << MaxDelay << ", Time = " <<
 *				CurrentTime << "\n" ;
 *			LogOut << "******************************************\n" ;
 */
			OldMaxDelay = MaxDelay ;
		}
		TimeLastOutput = CurrentTime ;
	}
	if (In && Outputs) switch (DoneState) {
case DoneNoInit:
		DoneState = DoneWaiting ;
		break ;
case DoneWaiting:
		DoneState = DoneDoInit ;
		break ;
case DoneDoInit:
		DoneState = DoneDoCheck ;
case DoneDoCheck:
		break ;
	}
	switch (DoneState) {
case DoneDoInit:
		ReferenceInputs = Inputs ;
case DoneNoInit:
case DoneWaiting:
		Done = 0;
		break ;
case DoneDoCheck:
		if (ReferenceInputs != Inputs) {
			if (CurrentTime > 2000)
/*
 *			if (Done) if (!(CurrentTime % 1000 )) LogOut <<
 *				"Failure " << ReferenceInputs <<
 *				" : " << Inputs << "(" << CurrentTime << ")\n" ;
 */
			Done = 0;
		}
		break ;
	}
	return Outputs ;
}

void NodeChar::Dump()
{
	TheLog << "IncrementIn = " << IncrementIn << ", IncrementOut = "
		<< IncrementOut << ", Overlap = " << Overlap <<
		", Delay = " << Delay <<  ", MaxDelay = " << MaxDelay << "\n" ;
}



static int SimulateThread( NodeChar ** TheThread)
{
	// LogOut << "In Simulate thread.\n" ;
	int PreviousOutput = 0;
	int AvailableOutputs = 0 ;
	int OutputGenerated = 1 ;
	int i = 0;
	while (OutputGenerated) {
		OutputGenerated = 0;
		if (AvailableOutputs) {
			AvailableOutputs-- ;
			PreviousOutput = 1 ;
			OutputGenerated = 1;
		}
		// LogOut << "At time step " << i << " Available = " <<
		// 	AvailableOutputs << ".\n" ;
		i++ ;
		int Number = 0 ;
		int Done = 1 ;
		for (NodeChar ** Test = TheThread ; * Test ; Test++)  {
			PreviousOutput = (*Test)->Simulate(PreviousOutput,Done);
			// if (PreviousOutput) LogOut << "Node " << Number << 
			//	" has " << PreviousOutput << " outputs.\n" ;
			if (PreviousOutput) OutputGenerated = 1;
			Number++ ;
		}
		AvailableOutputs += PreviousOutput ;
		if (Done) {
			// LogOut << "Done at iteration " << i << "\n" ;
			return 1 ;
		}
	}
	// LogOut << "Deadlock.\n" ;
	return 0 ;
}

int LoopMemberList::CheckLoopTiming()
{
	// LogOut << "Checking feedback loop.\n" ;
	LoopMember * Member ;
	NodeChar ** TheThread = new NodeChar * [Size()+1] ;
	int i = 0;
	while (Member = Get()) {
		// LogOut << "Member = 0x" << hex((long)Member) << "\n" ;
		int32 IncrementIn = 1 ;
		int32 IncrementOut = 1 ;
		int32 Overlap = 0 ;
		int32 Delay = 0 ;
		DfNodeInLink * In ;
		if (In = Member->In) {
			// LogOut << "In = 0x" << hex((long)In) << "\n" ;
			IncrementIn = Member->In->GetIncrementIn();
			Overlap = In->GetOverlap();
			Delay = In->GetDelay();
		}
		DfNodeOutLink * Out ;
		if (Out = Member->Out) IncrementOut = Out->GetIncrementOut();
		// LogOut << "Out = 0x" << hex((long)Out) << "\n" ;

		TheThread[i++] = new NodeChar(IncrementIn,IncrementOut,
			Overlap,Delay);
		// TheThread[i-1]->Dump() ;
	}
	TheThread[i] = 0 ;
	int Return = SimulateThread(TheThread) ;
	for (i = 0 ; TheThread[i];i++) delete TheThread[i] ;
	delete TheThread ;
	return Return ;
}

