
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "buffer.h"
#include "tarnod.h"
#include "netlnk.h"
#include "cgidbg.h"
#include "errcode.h"
#include "exec.h"
#include "yacintfc.h"
#include <strstream.h>


// #define DEBUG_CHECK_INTEGRITY

void CheckIntegrity(const char *);

ExecuteState * TheState = 0;

ExecuteState::ExecuteState(TargetNode ** node_thread,
	TargetNode ** feedback_nodes, int Init_Length, int Exec_Length,
	NetworkStateControl ** state):
	TheExecState(ExecStateInit),
	GlobalSequenceIndex(0),
	GlobalExecutionIndex(0),
	StateControl(state),
	AnyProgress(0),
	MadeProgress(0)

{
	// TheLog << "ExecuteState::ctor\n" ;
	TheNodes = node_thread ;
	TheFeedbackNodes = feedback_nodes ;
	InitLength = Init_Length ;
	ExecLength = Exec_Length ;
}

void ExecuteState::ClearFeedbackFlags()
{
	if (!TheFeedbackNodes) return ;
	for (TargetNode ** FeedbackNode = TheFeedbackNodes; *FeedbackNode ;
		FeedbackNode++) (*FeedbackNode)->ClearFeedbackFlag();
}


TargetNode * ExecuteState::GetFirstNode(int n)
{
	if (n) DebugCodeError(49,"ExecuteState::GetFirstNode","(n>0) not implmented",0);
	if (!TheNodes) return 0 ;
	return *TheNodes ;
}

TargetNode * ExecuteState::GetLastNode(int n) 
{
	if (n) DebugCodeError(49,"ExecuteState::GetFirstNode","(n>0) not implmented",0);
	if (!TheNodes) return 0 ;
	TargetNode * Return = 0 ;
	for (TargetNode ** CurrentNode = TheNodes ; *CurrentNode ;
		CurrentNode++) {
			Return = *CurrentNode ;
			if (!Return->GetOut()) break ;
	}
	if (!Return) DebugCodeError(50,"ExecuteState::GetLastNode","none found",0);
	// else TheLog << "Last node is `" << Return->GetName() << "\n" ;
	return Return ;
}

ErrCode ExecuteState::ExecuteNetworkDynamicKernel()
{
/*
 *	TheLog << "ExecuteState::ExecuteNetworkDynanicKernel, Progress = "
 *		<< AnyProgress << "\n" ;
 */
	ErrCode ExecutionStatus = OK ;
	ClearFeedbackFlags();
	int32 Count ;
	MadeProgress = 0 ;
	DEBUGNodeIndex = 0 ;
	int32 count_dec = 0 ;
	for (TargetNode ** CurrentNode = TheNodes ; *CurrentNode ;
		CurrentNode++) {
		DEBUGNodeIndex++ ;
#ifdef DEBUG_CHECK_INTEGRITY
		char Buf[100];
		strcpy(Buf,(*CurrentNode)->GetName());
		CheckIntegrity(Buf);
#endif
		ErrCode LocExecutionStatus =
			(*CurrentNode)->ExecuteDynamicNode(Count);
		State.end_node_exec();
		if (Count > count_dec) count_dec = Count ;
#ifdef DEBUG_CHECK_INTEGRITY
		strcat(Buf," after");
		CheckIntegrity(Buf);
#endif
		if (LocExecutionStatus > ExecutionStatus)
			ExecutionStatus = LocExecutionStatus ;
/*
 *		TheLog << "Back from ExecuteNode, Status = " <<
 *			ExecutionStatus << ", Progress = " <<
 *			AnyProgress << "\n" ;
 */
		switch(LocExecutionStatus) {
case OutputBuffersFull:
			AnyProgress = MadeProgress = 0 ;
			break ;
case OK:
case Warning:
			if (Count) AnyProgress = MadeProgress = 1 ;
			continue ;
case EndOfData:
			continue ;
case ExecutionComplete:
			continue ;
			// will quit when MadeProgress is false
case FatalError:
			
				DebugCodeError(51,"ExecuteState::ExecuteDynamicKernel","FatalError",0);
		} // end switch
		break ;
	} // end loop on threads
	count_left -= count_dec ;
	// TheLog<<"Exit dyn thread loop- staus = " << ExecutionStatus << "\n" ;
	return ExecutionStatus ;
}

ErrCode ExecuteState::ExecuteNetworkKernel(int TableLimit)
{
/*
 *	TheLog << "ExecuteStateLLExecuteNetworkKernel(" << TableLimit <<
 *		"), AnyProgress = " << AnyProgress << "\n" ;
 */
	ErrCode ExecutionStatus = OK ;
	int32 Count ;
	MadeProgress = 0 ;
	for (; GlobalExecutionIndex < TableLimit; GlobalExecutionIndex++) {
		// ClearFeedbackFlags();
			
		DEBUGNodeIndex = 0 ;
/*
 *		TheLog << "GlobalExecutionIndex = " << GlobalExecutionIndex
 *			<< "\n" ;
 */
		int32 count_dec = 0 ;
		for (; *CurrentNode ; CurrentNode++) {
			DEBUGNodeIndex++ ;
			ErrCode LocExecutionStatus =
				(*CurrentNode)->ExecuteNode(Count);
			if (Count > count_dec) count_dec = Count ;
			State.end_node_exec();
			DEBUGNodeCount = Count ;
			if (LocExecutionStatus > ExecutionStatus)
				ExecutionStatus = LocExecutionStatus ;
			switch(LocExecutionStatus) {
case OutputBuffersFull:
				AnyProgress = MadeProgress = 0 ;
				break ;
case OK:
case Warning:
				if (Count) AnyProgress = MadeProgress = 1;
				continue ;
case EndOfData:
				// continue ;
case ExecutionComplete:
				return ExecutionStatus ;
case FatalError:
				return ExecutionStatus ;
			} // end switch
			break ;
		} // end loop on threads
		count_left -= count_dec ;
		CurrentNode = TheNodes ;
	}
	GlobalExecutionIndex = 0 ;
/*
 *	TheLog << "Exit ExecuteNetworkKernel - staus = " << ExecutionStatus
 *		<< "\n" ;
 */
	return ExecutionStatus ;
}

ErrCode ExecuteState::SetAllSequences(int Index)
{
	// TheLog << "SetAllSequences(" << Index << ")\n" ;
	GlobalSequenceIndex = Index ;
	GlobalExecutionIndex = 0 ;
	for (TargetNode ** ToSetNode = TheNodes;*ToSetNode;ToSetNode++)
		(*ToSetNode)->SetSequence(Index);
	CurrentNode = TheNodes ;
	return OK ;
}

ErrCode ExecuteState::ResumeExecuteNetwork(int& AnyProgressMade)
{
	// LogOut << "Es::REN\n" ;
	AnyProgress = 0 ;
	ErrCode Return = DoExecuteNetwork();
/*
 *	TheLog << "ExecuteState::ResumeExecuteNetwork, Progress = " <<
 *		AnyProgress << ", Status = " << Return << "\n" ;
 */
	if (!AnyProgress && Return == EndOfData) Return = ExecutionComplete ; 
	AnyProgressMade = AnyProgress ;
	return Return ;
}

ErrCode ExecuteState::ExecuteNetwork()
{
	ErrCode Return = OK ;
	MadeProgress = 1 ;
	count_left = exec_count ;
	while (MadeProgress && ((count_left > 0 ) || !exec_count)) {
		MadeProgress = 0 ;
		Return = DoExecuteNetwork();
	}
	if (Return >= FatalError) 
		DebugCodeError(52,"ExecuteState::ExecuteKernel","FatalError",0);
	return Return ;
}

ErrCode ExecuteState::DoExecuteNetwork()
{
	// TheLog << "DoExecuteNetwork, Progress = " << AnyProgress << "\n" ;
	TheState = this ;
	// TheLog << "First node is " << (*TheNodes)->GetName() << "\n" ;
/*
 *	TheLog << "ExecLength = " << ExecLength << ", InitLength = " <<
 *		InitLength << "\n" ;
 */
	if (InitLength && TheExecState < ExecStateContinuous) {
		if (TheExecState < ExecStateInitDo) SetAllSequences(0);
		TheExecState = ExecStateInitDo ;
		ExecutionState = ExecuteNetworkKernel(InitLength);
		if (ExecutionState >= EndOfData) return ExecutionState ;
	} 
	if (ExecLength) {
		if (TheExecState < ExecStateContinuousDo) SetAllSequences(1);
		TheExecState = ExecStateContinuousDo ;
		return ExecuteNetworkKernel(ExecLength);
	} else return ExecuteNetworkDynamicKernel();
}

ErrCode ExecuteState::Reset()
{
	// TheLog << "ExecuteState::Reset\n" ;
	TheExecState = ExecStateInit ;
	GlobalSequenceIndex = 0 ;
	GlobalExecutionIndex = 0 ;

	ErrCode Return = OK ;
	ErrCode Local ;
	for (TargetNode ** nodes = TheNodes ; *nodes ; nodes++) {
		// TheLog << "Resetting `" << (*nodes)->GetName() << "'\n" ;
		if ((Local = (*nodes)->Reset()) > Return) Return = Local ;
		// TheLog << "Did node reset\n" ;
	}
	// TheLog << "ExecuteState::Reset exit\n" ;
	return Return ;
}

ErrCode ExecuteState::DoCallAtEnd()
{
	// TheLog << "ExecuteState::DoCallAtEnd\n" ;
	if (!*StateControl) return Warning ;
	// TheLog << "Doing call\n" ;
	return (*StateControl)->DoCallAtEnd();
}

ErrCode ExecuteState::CheckProcess()
{
}


const default_count = 8192 ;
int32 ExecuteState::exec_count = default_count ;
int ExecuteState::show_nodes = 1 ;

static void usage(const char * pgm)
{
	cerr << "Usage: " << pgm << " [ -e count ] [ -n ] [ -l log] [ -w warn ]\n" ;
	cerr << "`count' is execute count, default is " << default_count <<
		". 0 runs continuously.\n" ;
	cerr << "`-n' suppresses node name and execute count display.\n";
	cerr << "`log' is log file for validation error summaries.\n" ;
	cerr << "`warn' is the maximum warnings per node execution,\n" ;
	cerr << "Default warnings is " << State.warn_def <<
		", -1 lists all warnings.\n" ;
	exit(1);
}
	

// Routine to process run time arguments
void ExecuteState::process_args(int argc, char ** argv)
{
	int c ;
	while (( c = getopt(argc,argv,"e:nl:w:")) != EOF) switch(c) {
case 'w' :
		{
			istrstream str(optarg) ;
			int max_warn ;
			State.warn(max_warn);
		}
case 'l' :
			State.log_file(optarg);
			break ;
case 'e' :
		{
			istrstream str(optarg) ;
			str >> exec_count ;
			break ;
		}
case 'n' :
		show_nodes = 0 ;
		break ;
default:
		usage(argv[0]);
	}			
}
