implementation module EdProject;

import StdClass,StdBool, StdInt, StdString,StdArray,StdList,deltaFont;
import EdTypes, EdConstants, EdLists, EdPath, EdProjectAux, EdText, EdProgramState, EdMyIO;

DclMod					:== True;
IclMod					:== False;

WinOpen					:== True;
WinClosed				:== False;
	
Modified				:== True;
Unmodified				:== False;

::	Def_and_Imp				:== Bool;
::	WindowOpen_and_Closed	:== Bool;
::	Modification			:== Bool;

//	The Project: A list of constituent modules and their mutual dependencies
::	Project	= {
		built			:: !Bool,
		saved			:: !Bool,
		exec			:: !Bool,
		inflist			:: !InfList,
		codegenopt		:: !CodeGenOptions,
		code_gen_options_unchanged :: !Bool,
		applicationopt	:: !ApplicationOptions,
		projectopt		:: !ProjectOptions,
		linkOptions		:: !LinkOptions,
		prjpaths		:: !List Pathname,
		abcLinkPathsCache :: !ABCLinkPathsCache	// MW++
	};
	//	Was dependency list generated?, list with constituent modules, code generator options,
	//	application options, project paths, executable up to date?, use abc info, use timestamps.


PR_InitProject :: Project;
PR_InitProject
	= {	built			= True,
		saved			= True,
		exec			= True,
		inflist			= Nil,
		codegenopt		= DefCodeGenOptions,
		code_gen_options_unchanged
						= True,
		applicationopt	= DefApplicationOptions,
		projectopt		= DefProjectOptions,
		linkOptions		= DefaultLinkOptions,
		prjpaths		= Nil,
		abcLinkPathsCache = []	// MW++
	  };
	
PR_ProjectSet :: !Project -> Bool;
PR_ProjectSet project=:{inflist=Nil}	=  False;
PR_ProjectSet project=:{inflist}		=  True;

PR_NewProject :: !Pathname !EditWdOptions !CompilerOptions !CodeGenOptions !ApplicationOptions !ProjectOptions
				!(List Pathname) !LinkOptions -> Project;
PR_NewProject main_module_file_name eo compilerOptions cgo ao po prjpaths linkOptions
	=  {	built			= True,
			saved			= False,
			exec			= True,
			inflist			= {	mn		= modname,
								info	= {	dir		= dirname,
											defeo	= eo,
											impeo	= eo,
											compilerOptions		= compilerOptions,
											defopen	= False,
											impopen = True,
											date	= NoDate,
											deps	= modname :! Nil,
											abcLinkInfo	= { linkObjFileNames=Nil, linkLibraryNames=Nil }	// MW++
										  },
								src		= True,
								abc		= True } :! Nil,
			codegenopt		= cgo,
			code_gen_options_unchanged
							= True,
			applicationopt	= ao,
			projectopt		= po,
			prjpaths		= if (StringOccurs dirname prjpaths) prjpaths (dirname:!prjpaths),
			linkOptions		= linkOptions,
			abcLinkPathsCache = []	// MW++
		};
	where {
		modname	= GetModuleName main_module_file_name;
		dirname	= RemoveFilename main_module_file_name;
	};
	
PR_SetBuilt	:: !Project -> Project;
PR_SetBuilt project=:{inflist=Nil} = {project & built = True};
PR_SetBuilt prj=:{built,inflist=infl=:({mn,info}:!rest),saved}
	| built
		=	prj;
		#! len = LLength infl;
		# unchanged	= len == len`;
		=	{prj & built=True,saved=saved && unchanged,inflist=infl`};
	with {
		infl`		= RemoveUnusedModules infl;
		len`		= LLength infl`;
	};
			
PR_AddNewDependencies :: !Pathname !(List Pathname) !Project -> Project;
PR_AddNewDependencies importer importees project=:{inflist=Nil}
	= project;
PR_AddNewDependencies importer importees project=:{inflist,built}
	= {project & saved=False,inflist=infl`, built=built && not_removed};
	where {
		(infl`,not_removed)	= TryInsertInList importer importees inflist;
	};

//	MW..
// insert link information fond in abc files	
PR_AddABCLinkInfo :: !Pathname !(List LinkObjFileName) !(List LinkLibraryName) !Project -> Project;
PR_AddABCLinkInfo abcFilePathName linkObjFileNamesNew linkLibraryNamesNew
				project=:{inflist}
	#	module_name	= GetModuleName abcFilePathName;
		(inflist,_)	= UpdateList module_name update inflist;
	= { project & inflist=inflist }
  where {
	update infListItem=:{info}
		= ({ infListItem &	info.abcLinkInfo.linkObjFileNames=linkObjFileNamesNew,
							info.abcLinkInfo.linkLibraryNames=linkLibraryNamesNew  }, True)
	};

// retrieve the names of all object files and libraries in the project 
PR_GetABCLinkInfo	:: !Project -> ABCLinkInfo;
PR_GetABCLinkInfo project=:{inflist}
	#	allLinkInfoRecords	= map (\{info={abcLinkInfo}} -> abcLinkInfo) (StrictListToList inflist);
		oneLinkInfoRecord	= foldl mergeTwoRecords emptyRecord allLinkInfoRecords;
	= oneLinkInfoRecord;
	where {
		mergeTwoRecords { linkObjFileNames=linkObjFileNames1, linkLibraryNames=linkLibraryNames1}
						{ linkObjFileNames=linkObjFileNames2, linkLibraryNames=linkLibraryNames2}
			= { linkObjFileNames	= UnionStringList linkObjFileNames2 linkObjFileNames1,
				linkLibraryNames	= UnionStringList linkLibraryNames2 linkLibraryNames1};
		emptyRecord
			= { linkObjFileNames = Nil, linkLibraryNames	= Nil};
		};

PR_GetABCLinkPathsCache	::	!Project -> ABCLinkPathsCache;
PR_GetABCLinkPathsCache {abcLinkPathsCache}
	= abcLinkPathsCache;

PR_SetABCLinkPathsCache	::	!ABCLinkPathsCache !Project -> Project;
PR_SetABCLinkPathsCache abcLinkPathsCache project
	= { project & abcLinkPathsCache=abcLinkPathsCache };

MbHead Nil = "NIL";
MbHead l = Head l;
//..MW

PR_SetRoot :: !Pathname !EditWdOptions !CompilerOptions !Project -> Project;
PR_SetRoot root eo co project=:{inflist=Nil}
	= project;
PR_SetRoot newroot eo compilerOptions project=:{prjpaths}
	=	{project &	saved	= False,
					built	= False,
					inflist	= {	mn		= modname,
								info	= {	dir		= dirname,
											defeo	= eo,
											impeo	= eo,
											compilerOptions		= compilerOptions,
											defopen	= False,
											impopen = True,
											date	= NoDate,
											deps	= modname :! Nil,
											abcLinkInfo = { linkObjFileNames=Nil,linkLibraryNames=Nil }	// MW++
											},
								src		= True,
								abc		= True
								} :! Nil,
					prjpaths= if (StringOccurs dirname prjpaths) prjpaths (dirname:!prjpaths)
		  };
	where {
		modname	= GetModuleName newroot;
		dirname	= RemoveFilename newroot;
	};

PR_SetCompiled	:: !Modulename !Project -> Project;
PR_SetCompiled modname project=:{inflist}
	=  {project & inflist = inf`}
	where {
	(inf`, _)	= UpdateList modname setcompiled inflist;
	
	setcompiled	:: !InfListItem -> (!InfListItem,!Bool);
	setcompiled itm=:{src,abc}
		= ({itm & src = True},True);
	};
		
PR_SetCodeGenerated	:: !Modulename !Project -> Project;
PR_SetCodeGenerated modname project=:{inflist}
	= {project & inflist = inf`};
	where {
	(inf`,_)	= UpdateList modname setcode inflist;
	
	setcode :: !InfListItem -> (!InfListItem, !Bool);
	setcode itm=:{InfListItem | src}
		= ({InfListItem | itm & abc = True}, True);
	};
	
PR_SetSysCodeGenerated :: !Project -> Project;
PR_SetSysCodeGenerated project = {project & code_gen_options_unchanged = True};
	
PR_SetLinked	:: !Project -> Project;
PR_SetLinked project=:{inflist}
	=  {project & exec = True};
	
PR_SetSaved	:: !Project -> Project;
PR_SetSaved project = {Project | project & saved = True};
		
PR_SetCodeGenOptions	:: !CodeGenOptions !Project -> Project;
PR_SetCodeGenOptions options project=:{inflist,saved,codegenopt,code_gen_options_unchanged}
	=  {project & inflist = infl` ,saved=saved && unchanged, code_gen_options_unchanged=code_gen_options_unchanged && cg_unchanged, codegenopt=options};
	where {
	(infl`,_)				= infl`_;
	infl`_ | cg_unchanged	= (inflist,True);
							= P_MapR setcode inflist;
	unchanged				= cg_unchanged && options.kaf == codegenopt.kaf;
	cg_unchanged			= options == codegenopt;
	
	setcode :: !InfListItem -> (!InfListItem, !Bool);
	setcode itm=:{InfListItem | src}
		= ({InfListItem | itm & abc = False}, True);
	};
	
PR_SetApplicationOptions :: !ApplicationOptions !Project -> Project;
PR_SetApplicationOptions options project=:{saved,exec,applicationopt}
	= {project & applicationopt = options, exec = exec && unchanged,saved=saved && unchanged};
	where {
		unchanged	= options == applicationopt;
	};
	
PR_SetProjectOptions	:: !ProjectOptions !Project -> Project;
PR_SetProjectOptions options project=:{projectopt,saved}
	=  {project &	saved		= saved && unchanged,
					projectopt	= options };
	where {
		unchanged	= options == projectopt;
	};

PR_SetLinkOptions	:: !Project !LinkOptions -> Project;
PR_SetLinkOptions project linkOptions
	| linkOptions == project.Project.linkOptions
		=	project;
	| otherwise
		=	{Project | project & linkOptions = linkOptions, exec = False, saved = False};

PR_GetLinkOptions	:: !Project -> LinkOptions;
PR_GetLinkOptions project
	=  project.Project.linkOptions;

PR_SetPaths	:: !Bool !(List Pathname) !(List Pathname) !Project -> Project;
PR_SetPaths def defs new project=:{inflist=Nil} = project;
PR_SetPaths def defs new project=:{built,inflist=(infl=:({info={dir}}:!rest)),prjpaths,saved}
	| def	= {project &	built				= built && olddirs,
							saved				= saved && olddirs,
							inflist				= inflist1 };
			= {project &	built 				= built && olddirs,
							saved				= saved && unchanged && olddirs,
							inflist				= inflist1,
							prjpaths			= prjpaths1 };
	where {
		(inflist1,olddirs)	= P_MapR SetDcl_and_Icl_and_ABCModified infl;
		unchanged			= EQStrings (SortStrings prjpaths) (SortStrings prjpaths1);
		prjpaths1 	| def	= prjpaths;
					| StringOccurs dir new
							= new;
							= dir:!new;
						
		SetDcl_and_Icl_and_ABCModified :: !InfListItem -> (!InfListItem,!Bool);
		SetDcl_and_Icl_and_ABCModified itm=:{info=minfo=:{dir}}
//			= ({itm & info = {minfo & dir="", date=NoDate}},False);
		| unchanged	= ({itm & src=False}, True);
					= ({itm & info = {minfo & dir="", date=NoDate}, src=False},False);
			where {
				unchanged	= StringOccurs dir defs || StringOccurs dir prjpaths1;
			};
	};	
		
PR_GetCodeGenOptions :: !Project -> CodeGenOptions;
PR_GetCodeGenOptions project=:{codegenopt} =  codegenopt;

PR_GetProcessor :: !Project -> Processor;
PR_GetProcessor project=:{codegenopt={tp}} = tp;
	
PR_GetApplicationOptions :: !Project -> ApplicationOptions;
PR_GetApplicationOptions project=:{applicationopt} =  applicationopt;
	
PR_GetProjectOptions :: !Project -> ProjectOptions;
PR_GetProjectOptions project=:{projectopt} = projectopt;

PR_GetPaths	:: !Project -> List Pathname;
PR_GetPaths project=:{Project | prjpaths} = prjpaths;

PR_GetRootModuleName :: !Project -> Pathname;
PR_GetRootModuleName {inflist=Nil}
	= EmptyPathname;
PR_GetRootModuleName {inflist={mn}:!rest}
	= mn;

PR_GetRootPathName	:: !Project -> Pathname;
PR_GetRootPathName {inflist=Nil}
	= EmptyPathname;
PR_GetRootPathName {inflist={mn,info={dir}}:!rest}
	| size dir==0
		= EmptyPathname;
		= MakeFullPathname dir (MakeImpPathname mn);

PR_GetModulenames	:: !Bool !Def_and_Imp !Project -> List Pathname;
PR_GetModulenames full def project=:{inflist}
	= modnames;
	where {
	(modnames,_)	= P_MapR GetModulenames inflist;
	
	GetModulenames :: !InfListItem -> (!Pathname,!Bool);
	GetModulenames {mn,info={dir}}
		| full && def	= (MakeFullPathname dir (MakeDefPathname mn),True);
		| full			= (MakeFullPathname dir (MakeImpPathname mn),True);
						= (mn,True);
	};
		
PR_Built :: !Project -> Bool;
PR_Built project=:{built} =  built;
		
PR_SrcUpToDate :: !Modulename !Project -> Bool;
PR_SrcUpToDate modname project=:{inflist}
	= item.src;
	where {
		(_,item)	= FindInList modname inflist;
	};
	
PR_ABCUpToDate	:: !Modulename !Project -> Bool;
PR_ABCUpToDate modname project=:{inflist}
	= item.InfListItem.abc
	where {
		(_,item)	= FindInList modname inflist;
	};
	
PR_SysUptoDate :: !Project -> Bool;
PR_SysUptoDate project=:{code_gen_options_unchanged} = code_gen_options_unchanged;
	
PR_ExecUpToDate	:: !Project -> Bool;		
PR_ExecUpToDate project=:{exec} = exec;

PR_Saved :: !Project -> Bool;
PR_Saved {Project | saved} = saved;

PR_AddRootModule ::	!Bool !CodeGenOptions !ApplicationOptions !ProjectOptions !(List Pathname) !LinkOptions
					!Modulename !ModInfo -> Project;
PR_AddRootModule built cg ao po prjs linkOptions mn info=:{dir}
	= {
			built			= built && dir<>"",
			saved			= True,
			exec			= True,
			code_gen_options_unchanged
							= True,
			inflist			= root:!Nil,
			codegenopt		= cg,
			applicationopt	= ao,
			projectopt		= po,
			prjpaths		= prjs,
			linkOptions	 = linkOptions,
			abcLinkPathsCache = []	// MW++
		};
	where {
		root	= {	mn		= mn,info	= info,src		= True,abc		= True };
	};
	
PR_AddModule :: !Modulename !ModInfo !Project -> Project;
PR_AddModule mn info=:{dir} project=:{built,inflist=root:!rest}
	= {project & built = built && dir<>"", inflist = root:!new:!rest};
	where {
	new	= {	mn		= mn,
			info	= info,
			src		= True,
			abc		= True };
	};
	
PR_GetModuleInfo :: !Modulename !Project -> (!Bool,!ModInfo);
PR_GetModuleInfo mn {inflist}
	= (found,item.info);
	where {
	(found, item)	= FindInList mn inflist;
	};
	
PR_UpdateModule :: !Modulename !(ModInfo -> ModInfo) !Project -> Project;
PR_UpdateModule mn update project=:{inflist,saved}
	= {project & inflist = infl`,saved=saved && unchanged};
	where {
	(infl`,unchanged)	= UpdateList mn update` inflist;
	update` itm=:{info}	= ({itm & info = info`}, unchanged);
							where {
							info`		= update info;
							unchanged	= info == info`;
							};
	};
