#include <stream.h>
#include <fstream.h>
#ifdef __NT_VC__
#include <io.h>
#include <process.h>
extern int fork();
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "yacintfc.h"
#include "intfc.h"
#include "usercom.h"
#include "network.h"
#include "destrgt.h"
#include "dosname.h"
#include "mktarget.h"
#include "cbuf.h"
#include "netcnt.h"
#include "makedir.h"
#include "mkstr.h"
#include "cgidbg.h"
#include "network.h"
#include "dirfil.h"
#include "flist.h"
#include "shrdary.h"
#include "tarparm.h"
#include "noderep.h"
#include "environ.h"
#include "copyright.h"

#ifdef __linux__
static const char * shell = "bash" ;
#else
static const char * shell = "ksh" ;
#endif




struct ArithTypeParams {
	const char * MakeName ;
	const char * Subdirectory ;
	const char * TailFile ;
};

struct SupportedTarget {
	const char * Name ;
	const ArithTypeParams * TheArithTypeParams ;
	int DosNameFlag ;
	int DoNotCreateMain ;
	const char * LibraryName ;
};


class SupportedTargetList {
	const SupportedTarget * TheSupportedTargets ;

public:
	SupportedTargetList();
	const SupportedTarget * GetTarget(const char * Name) const ;
	void ExplainLegal() const ;
};

const SupportedTarget * SupportedTargetList::GetTarget(const char * name) const
{
	for (const SupportedTarget * Check = TheSupportedTargets; Check->Name ;
		Check++) if (!strcmp(Check->Name,name)) return Check ;
	return 0 ;
}

static ArithTypeParams * CreateMakeMakeArray()
{
	ArithTypeParams * Return =
		new ArithTypeParams[ArithType::MaxArithTypes+1] ;
	for (int i = 0 ; i <= ArithType::MaxArithTypes; i++) {
		Return[i].MakeName = 0;
		Return[i].Subdirectory = 0;
		Return[i].TailFile = 0;
	}
	return Return ;
}

const NumberTargets = 4 ;
static SupportedTarget * InitTargets()
{
	SupportedTarget * TheSupportedTargets =
		new SupportedTarget[NumberTargets+1] ;
	ArithTypeParams * MakeArith ;

	TheSupportedTargets[0].Name = generic_cpp_name ;
	MakeArith = CreateMakeMakeArray();
	MakeArith[ArithType::ArithInt16].MakeName = "makemake.i16" ;
	MakeArith[ArithType::ArithInt16].Subdirectory = "tarint" ;
	MakeArith[ArithType::ArithInt16].TailFile = "Makefile.ti16" ;

	MakeArith[ArithType::ArithFloat].MakeName = "makemake.flt" ;
	MakeArith[ArithType::ArithFloat].Subdirectory = "tarflt" ;
	MakeArith[ArithType::ArithFloat].TailFile = "Makefile.tflt" ;

	TheSupportedTargets[0].TheArithTypeParams = MakeArith ;
	TheSupportedTargets[0].DosNameFlag = 0 ;
	TheSupportedTargets[0].DoNotCreateMain = 0 ;
	TheSupportedTargets[0].LibraryName = "OPD_LIB_LNX" ;
	
	TheSupportedTargets[1].Name = 0; // TIC30_name ;
	MakeArith = CreateMakeMakeArray();

	MakeArith[ArithType::ArithFloat].MakeName = "makemake.c30" ;
	MakeArith[ArithType::ArithFloat].Subdirectory = "tarc30" ;
	MakeArith[ArithType::ArithFloat].TailFile = "Makefile.tc30" ;

	TheSupportedTargets[1].TheArithTypeParams = MakeArith ;
	TheSupportedTargets[1].DosNameFlag = 1 ;
	TheSupportedTargets[1].DoNotCreateMain = 1 ;
	TheSupportedTargets[1].LibraryName = "OPD_LIB_TIC30" ;

	TheSupportedTargets[2].Name = "TIC30c" ;
	MakeArith = CreateMakeMakeArray();

	MakeArith[ArithType::ArithFloat].MakeName = "makemake.c30c" ;
	MakeArith[ArithType::ArithFloat].Subdirectory = "tarc30" ;
	MakeArith[ArithType::ArithFloat].TailFile = "Makefile.tc30c" ;

	TheSupportedTargets[2].TheArithTypeParams = MakeArith ;
	TheSupportedTargets[2].DosNameFlag = 1 ;
	TheSupportedTargets[2].DoNotCreateMain = 1 ;
	TheSupportedTargets[2].LibraryName = "target.lib" ;

	TheSupportedTargets[3].Name = "TIC30b" ;
	MakeArith = CreateMakeMakeArray();

	MakeArith[ArithType::ArithFloat].MakeName = "makemake.c30b" ;
	MakeArith[ArithType::ArithFloat].Subdirectory = "tarc30" ;
	MakeArith[ArithType::ArithFloat].TailFile = "Makefile.tc30b" ;

	TheSupportedTargets[3].TheArithTypeParams = MakeArith ;
	TheSupportedTargets[3].DosNameFlag = 1 ;
	TheSupportedTargets[3].DoNotCreateMain = 1 ;
	TheSupportedTargets[3].LibraryName = "OPD_LIB_TIC30" ;


	TheSupportedTargets[NumberTargets].Name = 0 ;
	TheSupportedTargets[NumberTargets].TheArithTypeParams = 0 ;
	return TheSupportedTargets ;
}

SupportedTargetList::SupportedTargetList():TheSupportedTargets(InitTargets())
{
}

void SupportedTargetList::ExplainLegal() const
{
	
} 

static SupportedTargetList TheSupportedTargetList ;

TargetDescription::TargetDescription(ProcessNet* the_net,
		TargetOptions options, const char * target_proc,
		const char * the_dir,
		const char * target_name,
		const char * state_file_name,
		const char * topology_name,
		const char * node_list_name,
		const char * state_name):
		TheNet(the_net),
		TheOptions(options),
		TargetProcessor(target_proc),
		Directory(the_dir),
		TargetName(target_name),
		StateFileName(state_file_name),
		TopologyFileName(topology_name),
		NodeListName(node_list_name),
		TheSharedArrays(0),
		StateName(state_name),
		ThisTarget(TheSupportedTargetList.GetTarget(TargetProcessor)),
		ThisMakeMake(ThisTarget?
			&(ThisTarget->TheArithTypeParams[TheArithType]):0)
{
/*
 *	LogOut << "TargetDescription::TargetDescription, TheOptions = 0x" 
 *		<< hex << TheOptions << dec << "\n" ;
 */
	if (!ThisTarget) {
		State.Error("Target name `", TargetProcessor,
			"' is not supported.\n") ;
		TheSupportedTargetList.ExplainLegal();
		return ;
	}
	if (!ThisMakeMake->MakeName) {
		State.Error("Target `", TargetProcessor ,
			"' is not available on this simulator");
		TheSupportedTargetList.ExplainLegal();
		return ;
	}
	SetDirectory() ;
	// LogOut << "Directory = " << Directory << "\n" ;
	if (!TargetName) TargetName = Concatenate(Directory,".out");
	// LogOut << "TargetName = " << TargetName << "\n" ;
}

TargetDescription::~TargetDescription()
{
	delete TheSharedArrays ;
}

int CheckFile (ostream * ToCheck, const char * FileName)
{
	if (! ToCheck) {
		State.Error("Cannot create `", FileName, "'");
		return  0;
	}
	return 1 ;
}


int TargetDescription::Emit()
{
/*
 *	LogOut << "TargetDescription::Emit, TheOptions = 0x" 
 *		<< hex << TheOptions << dec << "\n" ;
 *	LogOut << "TheSharedArrays = " << (void *) TheSharedArrays << "\n" ;
 */
	if (!ThisTarget) return 0 ;
	if (!ThisMakeMake->MakeName) return 0 ;
	int ok = 0 ;
	NodeReplacementList replacement_list(*TheNet,TargetProcessor) ;
	// LogOut << "creating replacement list\n" ;
	TheNet->list_replacements(replacement_list);
	// LogOut << "doing replace\n" ;
	replacement_list.replace();
	TheNet->CreateController() ;
	// LogOut << "replace done\n" ;
	if (TheNet->CheckComplete()) {
		// LogOut << "Is complete\n" ;
		if (TheNet->GetTheController()) {
			// const char * BufName = "CircBufDesDef" ;
			// CircBufDesDef=(CircBufDes *)AllEntityLists-> GetObject( BufName);
			// if (!CircBufDesDef) LogOut << "Creating buffer des\n";
			// if (!CircBufDesDef) CircBufDesDef = new CircBufDes(BufName,4096);
			// ok=TheNet->GetTheController()-> AssignBuffers(*CircBufDesDef);
			ok = TheNet->GetTheController()->AssignBuffers();
		}
	}
	int Return = 0 ;
	if (!ok) State.Error("cannot make target for `", TheNet->GetName(), "'");
	// else if (SetDirectory()) {
	else {
		// create state description
/*
 *		LogOut << "EmitTargetCode(" << TopologyFileName << ", "
 *			<< NodeListName << ",, " << Directory << ", "
 *			<< StateName << ")\n" ;
 *		LogOut << "Net name is '" << TheNet->GetName() << "'\n" ;
 */
		EmitTargetCode NewEmit(TopologyFileName, StateFileName,
			NodeListName, *TheNet,Directory,StateName,
			TheSharedArrays);
		if (NewEmit.IsGood())  {
/*
 *			LogOut << "ThisTarget->Name = " <<
 *				ThisTarget->Name << "\n" ;
 *			LogOut << "ThisTarget->DoNotCreateMain = " <<
 *				ThisTarget->DoNotCreateMain << "\n" ;
 */
			DoWriteDriver(TheOptions) ;
			Return = 1 ;
		}
	}
	// LogOut << "before restore\n" ;
	replacement_list.restore();
	// LogOut << "after restore\n" ;
	return Return ;
}

int TargetDescription::SetDirectory()
{
	if (!Directory) Directory = TheNet->GetName() ;
	if (ThisTarget->DosNameFlag) {
		const char * SaveDirectory = Directory ;
		Directory= ToDosName(Directory);
		if (strcmp(Directory,SaveDirectory)) HelpOut <<
			"Dircectory name `" << SaveDirectory <<
			"'\n" ;
		HelpOut << "converted to DOS compatible name `" <<
			Directory << "'.\n" ;
	}
	// check if directory exists. - create it if it does not
	if (!CheckSubDir(Directory)) {
		State.Error("cannot create directory");
		return 0 ;
	}
	return 1 ;
}

int TargetDescription::DoWriteDriver(TargetOptions options)
{
/*
 *	LogOut <<
 *		"TargetDescription::DoWriteDriver before write d, options = 0x" 
 *		<< hex << options << dec << "\n" ;
 */
	if (options&WriteDriver &&
		!(ThisTarget->DoNotCreateMain && options&OmitCppDriver)) {

		// LogOut << "Writing driver\n" ;
		const char * FileName = "netdrv.cxx" ;
		char * FullFileName = Concatenate(Directory,"/",FileName);
		ofstream FileOut(FullFileName);
		if (!CheckFile(&FileOut,FullFileName)) return 0 ;
		AddCopyright& add = AddCopyright::add_copyright();
		add.write_short(FileOut,FileName,AddCopyright::default_user_flag,
			AddCopyright::created_by);
		if (!(options&OmitCppDriver)) {
			FileOut << "#include \"ObjProGui/exec.h\"\n" ;
			char * first_dir = 0 ; 
			// char * first_dir = GetLowestDirectory(Directory) ;
			if (!first_dir) FileOut << "#include \"nodehds.h\"\n\n" ;
			else FileOut << "#include \"" << first_dir << "/nodehds.h\"\n\n" ;
			delete first_dir ;
		}
		if (!ThisTarget->DoNotCreateMain) {
			FileOut << "#include <stdlib.h>\n" ;
			FileOut << "#include \"ObjProGen/cpyrght_exe.h\"\n\n" ;
			FileOut <<
			  "extern void cppmain(int, char **);\n\n" ;
			FileOut << "main(int argc, char ** argv)\n" ;
			FileOut << "{\n" ;
			FileOut << "\tcppmain(argc,argv);\n" ;
			FileOut << "\texit(0);\n" ;
			FileOut << "}\n\n" ;
		}
		if (!(options&OmitCppDriver)) {
			FileOut <<
			"/* main driver for the stand alone DSP network */\n";
			FileOut <<
			"void cppmain(int argc, char ** argv)\n" ;
			FileOut << "{\n" ;
			FileOut << "\tExecuteState::process_args(argc,argv);\n" ;
			FileOut << "\tTheState = &TheNetworkState;\n" ;
			FileOut << "\tTheState->ExecuteNetwork();\n" ;
			FileOut << "}\n\n" ;
		}
		delete FullFileName ;
	}
	if (TheOptions&WriteMake) DoWriteMake(
		TheOptions&Create?Create:SystemElement);
	return 1 ;
}

void TargetDescription::DriverLibraryLinkCmd(ostream& FileOut,
	const char * LibraryDir,const char * CommandDir)
{
	const char * LibOrder = "LIBRARY_ORDER" ;
/*
 *	LogOut << "DriverLibraryLinkCmd(FileOut," << LibraryDir << ", " <<
 *		CommandDir << ")\n" ;
 */
	// Get library name
	ExamineDirectory TheDirectory(LibraryDir,".lib");
	char * ListingFileName = Concatenate(LibraryDir,"/",  LibOrder);
	const char ** ListOfLibrarys = FileToList(ListingFileName);
	if (!TheDirectory.GetNumberFiles() && !ListOfLibrarys) {
		HelpOut << "Warning: directory `" << LibraryDir <<
			"' contains no `.lib' files or `" << LibOrder
			<< "' file.\n";
		HelpOut << "Target linking will fail with no driver.\n" ;
		return ;
	}
	const char * LibraryName ;
	FileOut << "OPD_DRIVE_LIBRARY=\\\"" ;
	int First = 1 ;
	if (ListOfLibrarys) for (const char ** LibName = ListOfLibrarys;
		*LibName; LibName++) {
		if (!First) FileOut << "\\\n " ;
		else First = 0 ;
		if(**LibName != '/') {
			char * FullLibName =
				Concatenate(LibraryDir, "/",*LibName); 
			FileOut << FullLibName ;
			delete FullLibName ;
		} else FileOut << *LibName ;
	}
	char * FullLibraryName = 0 ;
	while (LibraryName = TheDirectory.NextFile()) {
		delete FullLibraryName ;
		char * FullLibraryName = Concatenate(LibraryDir,"/",
			LibraryName);
		if (InList(FullLibraryName,ListOfLibrarys)) continue ;
		if (InList(LibraryName,ListOfLibrarys)) continue ;
		if (!First) FileOut << "\\\n " ;
		else First = 0 ;

		FileOut <<  FullLibraryName ;
	}
	delete FullLibraryName ;
	FileOut << "\"\n" ;
	DeleteStringList(ListOfLibrarys);
}

int TargetDescription::DoWriteMake(TargetOptions opt, const char * LibDir)
{
	// create makefile
	// make temporary shell script file to create Makefile
	// LogOut<<"DoWriteMake(0x"<<hex << opt << dec << ", " << LibDir << "\n" ;
	const char * root_env_name = "OPD_ROOT" ;
	
	const char * DspRoot = Environment::get(root_env_name);
	if (!DspRoot) {
		State.Error("Environmental variable `", root_env_name, "' not set.");
		return 0 ;
	}

	const char * SubDir = ThisMakeMake->Subdirectory ;
	char * TempFileName = Concatenate(Directory,"/","OPDmkmkmk");

	{ // block to force closing of FileOut  
		ofstream FileOut(TempFileName);
		if (!CheckFile(&FileOut,TempFileName)) return 0 ;
		FileOut << "(" ; // `(' is closed at end so error messages
			 	// can be redirected.
		FileOut << "OPD_ORIG_DIR=`pwd`\n" ;
		FileOut << "cd " << Directory << " ; $OPD_ROOT/bin/headcnv *.h " << "\n" ;

		char *CheckTail = Concatenate(Directory,"/", "Makefile.tail");
		int Check = open(CheckTail,O_RDONLY);
		if (Check < 1) {
/*
 *			FileOut << "if [ ! -d  " << CheckTail << " ] ;then mkdir " <<
 *				CheckTail << " ;fi\n";
 */
			FileOut << "cp " << DspRoot << "/scripts/" <<
				ThisMakeMake->TailFile << " Makefile_tail \n" ;
		}
		else close(Check);
		delete CheckTail ;

		FileOut << "chmod u+x domakemake\ndomakemake\n" ;

		// if requested add command to do a make after creating	it
		if (opt&Create) {
			int full_path_name = Directory[0] == '/' ;
			if (LibDir) {
				FileOut << "OPD_NET_DIR=" ;
				if (!full_path_name) FileOut << "$OPD_ORIG_DIR/" ;
				FileOut << Directory << "; export OPD_NET_DIRn"; 
				FileOut << "cd " << LibDir << "; make\n" ;
			}
			FileOut << "cd " ;
			if (!full_path_name) FileOut << "$OPD_ORIG_DIR/" ;
			FileOut << Directory << "/" << SubDir << " ; make\n" ;
		}
		char * tar_file = OpdRemoveDirectory(TargetName);
		if (!tar_file) tar_file = Concatenate(" ");
		FileOut << ") > msgs_" << TheNet->GetName() << "_" << tar_file
			<< " 2>& 1\n" ;
		delete tar_file ;
	}

	{ // block to close domakemake
		char * DoMakeName = Concatenate(Directory,"/","domakemake");
		ofstream DoMakeFile(DoMakeName);
		if (! &DoMakeFile ) {
			State.Error("Cannot create `domakemake' file `",
				DoMakeName, "'");
			return 0 ;
		}


		if (LibDir) DriverLibraryLinkCmd(DoMakeFile,LibDir,Directory);
		else DoMakeFile << "OPD_DRIVE_LIBRARY=\n" ;
		const char * LibName = ThisTarget->LibraryName ;
		DoMakeFile << "OPD_SUB_DIR=" << SubDir << "\n" ;
		DoMakeFile << "OPD_LIB_ROOTS=\n"  ;
		DoMakeFile << "OPD_TIMING=" <<((opt & LinkForTiming) ? "1" : "0")<< "\n" ;
		const char * tar_lib_def = "$OPD_ROOT/scripts/tar_lib_def.sh" ;
		DoMakeFile << ". " << tar_lib_def << "\n" ;
		DoMakeFile << "OPD_NODE_LIB=$" << ThisTarget->LibraryName << "\n" ;
		const char * set_kernel_lib = "$OPD_ROOT/scripts/set_kernel_lib" ;
		DoMakeFile << ". " << set_kernel_lib << "\n" ;
		DoMakeFile << "OPD_DO_MAKE_DEP=\"" << tar_lib_def << " " <<
			set_kernel_lib << " " << DspRoot << "/scripts/" << ThisMakeMake->MakeName
				<< "\"\n" ;
		{
			char * target = OpdRemoveDirectory(GetTargetName());
			DoMakeFile << "NET_EXEC=" << (ThisTarget->DosNameFlag ?
				ToDosName(target):target) << "\n" ;
			delete target ;
		}
		DoMakeFile << ". " << DspRoot << "/scripts/" << ThisMakeMake->MakeName
			<< "\n" ;

	}
	// fork to ksh to execute this shell
	int ChildId = fork() ;
	if (ChildId) return 1 ;
	static const char * shell = 0 ;
	if (!shell) {
		shell = getenv("OPD_SHELL");
		if (!shell) shell = "/usr/bin/bash" ;
	}
	execl(shell,shell,TempFileName,0);
	return 0 ; // should never get here
}

int TargetDescription::ChangeNetwork(ProcessNet& the_net)
{
	// LogOut << "TargetDescription::ChangeNetwork\n" ;
	TheNet = &the_net;
	// add code to change appropriate names
	const char * BaseName = TheNet->GetName();
	StateFileName = ToDosCppName(Concatenate(BaseName,".cxx")) ;
	TopologyFileName = ToDosName(Concatenate("tp",BaseName,".h"));
	NodeListName = ToDosName(Concatenate("nl",BaseName,".h")) ;
	StateName = Concatenate("The",BaseName,"State") ;
	// LogOut << "TargetDescription::ChangeNetwork exit\n" ;
	return 1 ;
}

