/*  networki.C   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved					*/

#include <fstream.h>
#include <stdlib.h>
#include <unistd.h>
#include "ObjProShare/shared.h"
#include "ObjProGen/makedir.h"
#include "ObjProBison/yacoper.h"
#include "ObjProGui/yacintfc.h"
#include "ObjProGui/mktarget.h"
#include "ObjProComGui/cgidbg.h"
#include "ObjProUsr/network.h"
#include "ObjProUsr/netcnt.h"
#include "ObjProUsr/cbuf.h"
#include "ObjProGen/mkstr.h"
#include "ObjProGui/destrgt.h"
#include "ObjProUsr/node.h"
#include "ObjProGui/strmstr.h"
#include "ObjProGui/shrbuf.h"
#include "ObjProGui/dspe_app.h"
#include "ObjProUsr/buffer.h"
#include "ObjProUsr/node.h"
#include "ObjProUsr/sigbase.h"
#include "ObjProUsr/net.h"
#include "noderep.h"
#include "travparm.h"
#include "thread.h"
#include "ObjProGen/makedir.h"
#include "outtokf.h"
#include "intfc.h"

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

class UserEntity ;

/*
 * static Node DefaultNode("DefaultNode",1,1,
 *	new StreamStr(StreamNotInitialized),
 *	new StreamStr(StreamNotInitialized),0,1,1,0,0,TimingTypeLinear);
 */

const char * DefaultNotLegal(UserEntity *)
{
	return 0 ;
}

void Network::Raise()
{
	if (!is_displayed()) return ;
	int name_size = strlen(GetName());
    if (name_size >= MaxPacketSize) return ;
    PacketHeader head(PacketNetworkEdit,CppEnums::raise_network_window,
        name_size+1);
    WriteSeg->WritePacket(head,GetName());
}

void Network::GraphDisplay()
{
	ProcessNet::GraphDisplay(0) ;
}

void Network::GraphDisplayWindow(int16 x, int16 y)
{
	ProcessNet::GraphDisplay((char) 0,x,y) ;
}

Network & Network::Link(Node& TheNode, int16 Channel)
{
	ProcessNet::Link(TheNode,Channel);
	check_redisplay();
	return * this ;
}

Network & Network::SelfLink(Node& OutNode, Node& InNode, int16 OutChannel,
	int16 InChannel)
{
	ProcessNet::SelfLink(OutNode, InNode,OutChannel,InChannel);
	check_redisplay();
	return * this ;
}

void Network::ReplaceNode(Node& ToReplace, Node& Replacement)
{
	ProcessNet::ReplaceNode(ToReplace,Replacement);
	check_redisplay();
}

void Network::Execute(int32 InputSamples)
{
	ProcessNet::Execute(InputSamples);
}

void Network::AssignBuffers( BufferDescript& Desc)
{
	ProcessNet::AssignBuffers(Desc);
}

void Network::SetBufferDescriptor( BufferDescript& Desc)
{
	ProcessNet::set_buffer_descriptor(Desc);
}

void Network::AssociateNode(Node& node)
{
	add_unlinked_node(&node);
	check_redisplay();
}

void Network::ClearBuffers()
{
	ProcessNet::ClearBuffers();
}

void Network::ClearNetwork()
{
	ProcessNet::ClearNetwork();
	check_redisplay();
}

void Network::check_redisplay()
{
	if (!is_displayed()) return ;
	if (do_not_redisplay) return ;
	if (!State.IsError() && !State.IsNetEdit()) GraphDisplay();
	if (need_redisplay()) if (!State.IsInParse())
		DspApplication::check_redisplay();
}

Network & Network::operator>>(Node& TheNode)
{
	(*(ProcessNet *) this) >> TheNode ;
	check_redisplay();
	return * this ;
}

Network & Network::operator+(SignalStr& SigNode)
{
	ProcessNet& Gbg = (*(ProcessNet *) this) + SigNode ;
	if (need_redisplay()) if (!State.IsInParse())
		DspApplication::check_redisplay();
	return * (Network *) this ;
}


void Network::abort_message()
{
	CppOut << "`MakeValidate' for `" << GetName() << "' aborted.\n" ;
}

void Network::save_state(const char * name, int exec_count)
{
	OutTokFile * Out = OutTokFile::open_tok_file(name," ",60);
	if (!Out) {
		abort_message();
		return ;
	}
	int err = AllEntityLists->save_state_kernel(*Out);
	if (!err) {
		State.Error("problem in saving state");
		abort_message();
	}
	Out->NextFillOut(GetName());
	Out->NextFillOut(".Execute(");
	Out->NextConcat(exec_count);
	Out->NextConcat(");");
	Out->NewLine();
	OutTokFile::delete_tok_file(Out) ; // Close File
}

void Network::validate_state_out(const char * prefix, const char * user_desc,
	const char * dir_name,int exec_count)
{
	char * orig_base = Concatenate(prefix,GetName(),".dpp");
	char * orig_name = AddSubDirectory(dir_name,orig_base);

	if (State.IsConfirm()) CppOut << "Saving " << user_desc << " state.\n" ;
	save_state(orig_name,exec_count);
	if (State.IsError()) abort_message();
	else if (State.IsConfirm()) CppOut << "The " << user_desc <<
			" state saved as `" << orig_name << "'.\n" ;
	delete orig_name ;
	delete orig_base ;
}

static int st(NodeReplacementList ** x)
{
	if (!x) return 0 ;
	if (!*x) return 0 ;
	return 1;
}

/*
 * static void dbg(const char * msg = 0,
 *	 NodeReplacementList ** first=0, NodeReplacementList ** second=0)
 * {
 *	static recursively_calling = 0 ;
 *	static NodeReplacementList ** f = 0 ;
 *	static NodeReplacementList ** s = 0 ;
 *	if (first) f = first ;
 *	if (second) s = second ;
 *	if (!msg) msg = "Dbg rep" ;
 *	LogOut << msg << "\n" ;
 *	if (!st(f) && !st(s)) {
 *		return ;
 *	}
 *	if (st(f)) {
 *		LogOut << "first\n" ;
 *		(*f)->dump();
 *	}
 *	if (st(s)) {
 *		LogOut << "second\n" ;
 *		(*s)->dump();
 *	}
 *	if (recursively_calling) return ;
 *	recursively_calling = 1 ;
 *	dbg(msg);
 *	dbg(msg);
 *	LogOut << msg << " exit\n" ;
 *	recursively_calling = 0 ;
 *}
 */
void Network::MakeValidate(const char * dir_name,int32 exec_count,int32 extra,
	int32 max_rep, double tolerance,const char * error_file)
{
	validate_kernel(dir_name,exec_count,extra,max_rep,tolerance,error_file);
}


void Network::TargetValidate(const char * Target, int16 Create,
	const char * dir_name, int32 exec_count,int32 extra,
	int32 max_rep, double tolerance,const char * error_file)
{
	validate_kernel(dir_name, exec_count, extra, max_rep, tolerance,
		error_file, Target, Create) ;
}

void Network::write_shell_execute(const char * dir_name,
	const char *file,int count)
{
	char * exe_dir_name = Concatenate(file,"/",
		ArithType::target_sub_dir[TheArithType]);

	char * script_name = Concatenate(dir_name,"/",file,".sh");

	ofstream out(script_name,ios::out,0644|0111);
	if (!out.good()) {
		State.Error("Cannot create shell file `",script_name,"'");
		delete script_name ;
		delete exe_dir_name ;
	}
	out << "#/bin/sh\n" ;
	out << "pushd " << exe_dir_name << "\n" ;
	out << "make\n" ;
	out << "popd\n" ;
	out << exe_dir_name << "/" <<  file << ".out" << " -e " << count <<
		" -n $OPD_TAR_VAL_PARAM \n";
	if (!out.good()) 
		State.Error("Cannot write shell file `",script_name,"'");
	delete script_name ;
	delete exe_dir_name ;
}
 
void Network::validate_kernel(const char * dir_name, int32 exec_count,
	int32 extra, int32 max_rep, double tolerance,const char * error_file,
	const char * Target, int16 Create)
{
	if (State.IsError()) return ;
	char * del_name = 0 ;
	int make_name = 1 ;
	if (dir_name) if (*dir_name) make_name = 0 ;
	if (make_name) dir_name = del_name = Concatenate("val_",GetName()) ;
	if (Target) {
		char * tmp = Concatenate(del_name,"_",
			ArithType::CppNames[TheArithType]);
		delete del_name ;
		dir_name = del_name = tmp ;
	}

	// check to see if directory exists and if it does not create it
	if (!CheckSubDir(dir_name)) {
		State.Error("cannot create directory");
		abort_message();
		delete del_name ;
		return ;
	}
	const char * doing_at_error = 0 ;
	static NodeReplacementList * orig = 0 ;
	static NodeReplacementList * second = 0 ;
	// static for debugging only
	int save_in_validate = in_validate ;
	in_validate = 0 ;
	int save_do_not = do_not_redisplay ;
    do_not_redisplay = 1 ;
	int save_interactive = State.IsInteractive();
	State.ClearInteractive();

	char * create_dir = 0 ;
	const char * create_name = "create" ;
	char * compare_dir = 0 ;
	const char * compare_name = "compare" ;
	if (Target) {
		create_dir = Concatenate(dir_name,"/",create_name);
		compare_dir = Concatenate(dir_name,"/",compare_name);
	}
	for (;;) { // dummy loop so we can branch to end with break
		// save original state
		validate_state_out("ValOrig","original",dir_name,exec_count) ;
		// dbg("orig");
		if (State.IsError()) break ;

		// save create state
		DspApplication::set_make_target_mode();
		// dbg("set_original",&orig);
		replace(NodeReplacement::validate_output_replace,0,&orig);
		// dbg("rep 1",&orig);
		if (State.IsError()) {
			DspApplication::clear_make_target_mode();
			break ;
		}
		
		validate_state_out("ValCreate","create",dir_name,exec_count+extra) ;
		// dbg("state 1");
		if (State.IsError()) {
			doing_at_error = "Writing state that creates validation data" ;
			break ;
		}
		if (Target) {
			MakeTarget(Target,Create,create_dir);
			// dbg("mk tgt 1");
			if (State.IsError()) {
				doing_at_error = "Writing create target code" ;
				break ;
			}
		}
		const char * doing = "Executing network to initialize output files." ;
		if (State.IsConfirm()) CppOut << doing << "\nThis is necessary for the constructors of\n" ;
		if (State.IsConfirm()) CppOut << "the nodes that read these files.\n";
		Execute(exec_count);
		// dbg("exec");
		if (State.IsError()) {
			doing_at_error = doing ;
 	      	break ;
    	}
		// save compare state
		if (!error_file) error_file = Concatenate("err_for_",GetName());
		doing = "Replacing each output node with a compare node" ;
		if (State.IsConfirm()) CppOut << doing << ".\n" ;
		// dbg("set 2",0,&second);
		replace_with_compare( max_rep, tolerance,  error_file, 0, &second) ;
		// dbg("rep 2",0,&second);
		if (State.IsError()) {
			doing_at_error =  doing ;
			break ;
		}
		validate_state_out("ValCompare","compare",dir_name,exec_count) ;
		// dbg("compare out");
		if (State.IsError()) {
			doing_at_error =  "Saving compare state" ;
			break ;
		}
		if (Target) {
			MakeTarget(Target,Create,compare_dir);
			// dbg("make 2");
			if (State.IsError()) {
				doing_at_error = "Writing test target code" ;
				break ;
			}
			// write shell script to execute tests
			write_shell_execute(dir_name,create_name,exec_count+extra);
			if (State.IsError()) {
				doing_at_error = "Writing create shell script" ;
				break ;
			}
			write_shell_execute(dir_name,compare_name,exec_count);
			if (State.IsError()) {
                doing_at_error = "Writing test shell script" ;
                break ;
            }
		}
		break ;
	} // end of dummy loop
	// dbg("end replace start restore");
	if (doing_at_error) {
		CppOut << "MakeValidate aborted because of error(s) while\n" <<	
			doing_at_error << ".\n" ;
		State.ClearError();
	}
	if (orig) {
		if (second) orig->relay(*second);
		// dbg("relay") ;
		delete second ;
		second = 0 ;
		// dbg("delete second");
		if (State.IsConfirm()) CppOut << "Restoring original network.\n" ;
		State.ClearError();
		orig->restore();
		// dbg(" orig restore");
		delete orig ;
		orig = 0 ;
		// dbg("delete orig");
		if (is_displayed()) GraphDisplay();
		// dbg("gr display");
		if (State.IsError()) {
			CppOut << "Error in restoring network.\n" ;
		} else if (doing_at_error) State.SetError();
	}
	DspApplication::clear_make_target_mode();
	delete create_dir ;
	delete compare_dir ;
	delete del_name ;
	in_validate = save_in_validate  ;
	do_not_redisplay = save_do_not ;
	if (save_interactive) State.SetInteractive();
 	// dbg("exit");
}


void Network::replace(const char * replace_what, int ret_flag,
		NodeReplacementList ** rep_ret)
{
	// dbg("replace entry");
	NodeReplacementList * rep = 0 ;
	if (State.IsError()) return ;

	if (!CheckComplete()) {
		State.Error("Network `", GetName() , "' is not complete,\n" ,
			"Cannot replace nodes in this network");
		return ;
	}
	rep = new NodeReplacementList(*this, replace_what,ret_flag);
	if (rep_ret) *rep_ret = rep ;
	NodeReplacementList& replacement_list = *rep ;
	TraverseParameters * params = new TraverseParameters(replacement_list);
	static OutTokens * dum_tok ;
	if (!dum_tok) dum_tok = new OutTokens(OutputCppHelp) ;
   	TraverseObject obj(*dum_tok,"target replacement list",
		*(dum_tok->GetStream()), params);
   	obj.SetForwardAction(&(DfNode::replacement_list));
	// dbg("replace before traverse");
   	Traverse(obj);
	// dbg("replace after traverse");
	if (State.IsError()) {
		CppOut << "No replacments made because of the above errors.\n" ;
		*rep_ret = 0 ;
		delete rep ;
		return ;
	}
	CreateController();
	// dbg("replace before replace");
	replacement_list.replace();
	// dbg("replace after replace");
	if (State.IsError()) {
		CppOut << "Error in making replacement.\n" ;
		CppOut << "Will attemp to resotre original nodes.\n" ;
		replacement_list.restore();
		*rep_ret = 0 ;
		delete rep ;
		return ;
	}
	int ok = 0 ;
	if (CheckComplete()) if (GetTheController())
		ok = GetTheController()->AssignBuffers();
	if (!ok) State.Error("cannot replace nodes in `", GetName(), "'");
	if (is_displayed()) GraphDisplay();
	if (!rep_ret) delete rep ;
	// dbg("replace exit");
}

void Network::ReplaceWithOutput()
{
	replace(NodeReplacement::validate_output_replace);
}

void Network::ReplaceWithCompare(int32 max_rep, double tol,
	const char * error_file)
{
	replace_with_compare(max_rep,tol,error_file);
}

void Network::replace_with_compare(int32 max_rep, double tol,
	const char * error_file, int ret_flag, NodeReplacementList ** lst)
{

	DspApplication::compare_disk_max_report(max_rep);
	DspApplication::compare_disk_tolerance(tol);
	DspApplication::compare_disk_error_file(error_file);

	replace(NodeReplacement::validate_compare_replace,ret_flag,lst);

}

static void TargetAbort()
{
	State.Error("MakeTarget aborted");
}

void Network::SetTimingExact(int16 val)
{
	exact_timing = 1 ;
}

void Network::MakeTarget(const char * Target, int16 Create,  
	const char * Directory)
{

/*
 *	LogOut << "MakeTarget(" << Target << ", " << Create << ", " <<
 *		Directory << ")\n" ;
 */
	// dbg("MakeTarget entry");
	CreateController();
	if (!GetTheController() || State.IsError()) {
		TargetAbort();
		return ;
	}
	int Ok = GetTheController()->AssignBuffers();
	// dbg("Assign");
	if (State.IsError() || !Ok) {
		TargetAbort();
		return ;
	}

	int save_in_validate = in_validate ;
	in_validate = 0 ;
	int save_do_not = do_not_redisplay ;
	do_not_redisplay = 1 ;
	int save_interactive = State.IsInteractive();
	State.ClearInteractive();

	int save_make_target = DspApplication::make_target_mode();
	DspApplication::set_make_target_mode();

	TheSharedBuffer.clear();
	// dbg("Clear");
	char * temp_target = Concatenate(Target);
	char * temp_directory = Concatenate(Directory);
	TargetDescription::TargetOptions Options =
		(TargetDescription::TargetOptions)
		(TargetDescription::WriteDriver | TargetDescription::WriteMake) ;
	// dbg("Tar des");
	if (Create) Options =
			(TargetDescription::TargetOptions)
			(Options | TargetDescription::Create) ;
	// LogOut << "Creating TargetDescription\n" ;
	TargetDescription ToMake(this,Options,temp_target,
		temp_directory);
	// dbg("To make");
	// LogOut << "Created TargetDescription\n" ;
	if (!ToMake.Emit()) TargetAbort() ;
	// dbg("To make emit");
	// LogOut << "Emited Target\n" ;
	delete temp_target ;
	delete temp_directory ;
	if (!GetTheController()->CheckComplete())
		State.Error("the network is no longer complete");
	// dbg("check");

	if (!save_make_target) DspApplication::clear_make_target_mode();
	in_validate = save_in_validate ;
	do_not_redisplay = save_do_not;
	if (save_interactive) State.SetInteractive();
}

static void describe_entity(UserEntity * des)
{
	if (!State.IsInteractive()) return ;
	OutTokens Out(OutputHelp,0,""," ","",78);
	Out.NextFillOut("The following description is for object");
	Out.NextQuoteOut(des->GetName());
	Out.NextConcat(".");
	Out.NewLine();
	des->Describe(Out,ListSingleEntity);
}

BufferDescript& Network::GetBufferDescriptor()
{
	CreateController();
	NetControl * control = GetTheController();
	if (!control) DbgError("Network::GetBufferDescriptor",
		"cannot create controller");
	if (!control->GetTheBufferDescript()) control->set_buffer_descriptor();
	BufferDescript * des = control->GetTheBufferDescript() ;
	if (!des) DbgError("Network::GetBufferDescriptor","no descriptor");
	describe_entity(des);
	DspApplication::send_gui(CppEnums::buffer_descriptor_for_net,
		GetName(),des->GetName());
	return *des ;
}

NetControl& Network::GetNetController()
{
	CreateController();
	if (!GetTheController()) DbgError("Network::GetNetController",
        "cannot create controller");
	describe_entity(GetTheController());
	return *(GetTheController()) ;
}

void Network::DisplayNames()
{
	if (!GetTheController()) {
		*Output + OutputHelp << "No controller assigned to network `"
			<< GetName() << "'.\n";
		return ;
	}
	*Output + OutputHelp << "The controller for network `" << GetName()
		<< "' is `" << GetTheController()->GetName() << "'.\n" ;
	BufferDescript* Des = GetTheController()->GetTheBufferDescript();
	if (!Des) *Output + OutputHelp <<
		"There is no buffer descriptor assigned.\n" ;
	else *Output + OutputHelp << "The buffer descriptor is `" <<
		Des->GetName() << "'.\n" ;
}

const char * GetFreeNodeIn(UserEntity * TheNet)
{
	if (!TheNet) return
		"the first node in the network with input channel 1 unused" ;
	return ((ProcessNet *) TheNet)->GetFreeNodeIn();
}

const char * GetFreeNodeOut(UserEntity * TheNet)
{
	if (!TheNet) return
		"the first node in the network with output channel 0 unused" ;
	return ((ProcessNet *) TheNet)->GetFreeNodeOut();
}



