implementation module WriteState

// pc: from ExtString import ILONG;
//from ExtFile import ExtractPathFileAndExtension, ExtractPathAndFile;
import xcoff;
import ExtFile;
import
	SymbolTable,
	State,
	ReadWriteState,
	LinkerOffsets,
	CommonObjectToDisk 
	
	
import
	ExtArray,
	ExtFile;

import pdWriteState;

invert_marked_bool_a :: !*State -> (!*{#Bool},!*State)
invert_marked_bool_a state 
	#! (marked_bool_a, state)
		= select_marked_bool_a state
	#! (size, marked_bool_a)
		= usize marked_bool_a
	#! inverted_marked_bool_a
		= { False \\ i <- [1..size] }
	#! (inverted_marked_bool_a, marked_bool_a)
		= invert 0 size inverted_marked_bool_a marked_bool_a
	= (inverted_marked_bool_a,{state & marked_bool_a = marked_bool_a})
	
	where
		invert :: !Int !Int !*{#Bool} !*{#Bool} -> (!*{#Bool},!*{#Bool})
		invert i limit inverted_marked_bool_a marked_bool_a
			| i == limit
				= (inverted_marked_bool_a,marked_bool_a)
				
			#! (element,marked_bool_a)
				= marked_bool_a![i]
			= invert (inc i) limit {inverted_marked_bool_a & [i] = not element} marked_bool_a
	
import 
	DebugUtilities,
	ExtString;

ExtractPathAndFile2 :: !String -> (!String,!String);
ExtractPathAndFile2 path_and_file 
	#! (dir_delimiter_found,i)
		= CharIndexBackwards path_and_file (size path_and_file - 1) path_separator;
	| dir_delimiter_found
		# file_name_with_extension
			= path_and_file % (i+1,size path_and_file - 1);
		= (if (i == 0) "\\" (path_and_file % (0,i-1)),file_name_with_extension);
		= ("",path_and_file);
	
WriteState :: !*State !*Files -> (!*State,!*Files)
WriteState state=:{n_libraries, n_xcoff_files, n_xcoff_symbols, n_library_symbols, library_list, application_name,library_file_names} /*, marked_bool_a, marked_offset_a, module_offset_a, xcoff_a, namestable}*/ files
	#! (path, file_name_with_extension)
		= ExtractPathAndFile2 application_name
	#! (file_name, _)
		= ExtractPathFileAndExtension file_name_with_extension

	#! state_file_name
		= construct_path path (file_name +++ ".dat");
	#! (ok, output, files)
		= fopen state_file_name FWriteData files
	|  not ok
		#! state 
			= AddMessage (LinkerWarning ("could not write complement " +++ state_file_name)) state;
		=  (state,files);
		
	/*
	** Compute offset of unmarked modules in .dat--file to be written
	*/
	// +4 voor de size
	#! (inverted_marked_bool_a,state)
		= invert_marked_bool_a state


	#! (marked_bool_a,state)
		= select_marked_bool_a state
		
	// write header
	#! output
		= WriteComplementVersion ComplementVersion output;
		
	// first free offset after raw data
	#! (fp_after_header,output)
		= fposition output;
	#! output
		= fwritei 0 output;
		
	// mac
	#! (state,output,files)
		= write_raw_data {state & marked_bool_a = inverted_marked_bool_a} output files;
		
	// update offset at beginning of complement
	# (current_fp,output)	
		= fposition output;
	# (_,output)
		= fseek output fp_after_header FSeekSet ;
	# output
		= fwritei current_fp output;
	# (_,output)
		= fseek output current_fp FSeekSet;
	
		
	#! state =
		{ state &
			marked_bool_a = marked_bool_a
		}
		
	/*
	** Pas op: je kunt niet zonder meer bytes voor de code/data gaan schrijven 
	** omdat de offsets vastliggen in de symbols voor elke module
	*/
		
	/*
	** Write counters
	*/
	#! output
		= fwritei n_libraries output 
	
	#! output
		= fwritei n_xcoff_files  output
	#! output
		= fwritei n_xcoff_symbols output
	#! output
		= fwritei n_library_symbols output
	
	// dynamic libraries
	#! output
		= WriteLibraryList library_list output
	#! output
		= WriteLibraryFileNames library_file_names output
		
	/*
	** Write marked_bool_a
	*/ 
	#! (marked_bool_a,state)
		= select_marked_bool_a state
//	#! (s_marked_bool_a,marked_bool_a)
//		= usize marked_bool_a
		
	// new
//	#! output
//		= fwritei s_marked_bool_a output;
	#! (marked_bool_a,output)
		= loopAonOutput (\bool output -> fwritec (if bool 'T' 'F') output) marked_bool_a output;

/*
	// old
	#! marked_bool_s
		= { if (is_true) ('T') ('F') \\ is_true <-: marked_bool_a }		
	#! output
		= fwritei s_marked_bool_a output
	#! output
		= fwrites marked_bool_s output
*/
				
	/*
	** Write marked_offset_a
	*/
	#! (marked_offset_a,state)
		= select_marked_offset_a state
	// new
/*
	#! (s_marked_offset_a,marked_offset_a)
		= usize marked_offset_a;
	#! output
		= fwritei s_marked_offset_a output
*/
	#! (marked_offset_a,output)
		= loopAonOutput fwritei marked_offset_a output;
	
	/*
	** Write module_offset_a
	*/
	#! (module_offset_a,state)
		= select_module_offset_a state
/*	
	#! (s_module_offset_a,module_offset_a)
		= usize module_offset_a;
	#! output
		= fwritei s_module_offset_a output
*/
	#! (module_offset_a,output)
		= loopAonOutput fwritei module_offset_a output;

	/*
	** Write xcoff_a
	** The total size in characters of the encoded array does not precede
	** the encoded array.
	*/ 
	#! (xcoff_a,state)
		= select_xcoff_a state
	#! (xcoff_a,output)
		= loopAurOnOutput write_xcoff xcoff_a output

	/*
	** write namestable
	*/
	#! (state,output)
		= WriteNamesTable state output
				
	/*
	** Close file
	*/
	#! (ok, files)
		= fclose output files		
		
	#! state 
		= { state &
			marked_bool_a	= marked_bool_a
		,	marked_offset_a	= marked_offset_a
		,	module_offset_a	= module_offset_a
		,	xcoff_a			= xcoff_a
		};		
	= ( state, files)	

/*
	,	application_name	:: !String
	,	n_libraries			:: !Int
	,	n_xcoff_files 		:: !Int
	,	n_xcoff_symbols		:: !Int
	,	n_library_symbols	:: !Int
	,	library_list 		:: !LibraryList
	,	marked_bool_a		:: !*{#Bool}
	,	marked_offset_a		:: !*{#Int}
	,	module_offset_a		:: !*{#Int}
	,	xcoff_a 			:: {#*Xcoff}
	,	namestable			:: *NamesTable
	
	//  macOS; only used by dynamic linker
	,	toc_p				:: !Int
};
*/			
// PLATFORM INDEPENDENT
// mac: WriteXCoffArray :: !{#/*PC S*/xXcoff} !Int !*File -> !*File

/*
WriteXCoffArray :: !{#SXcoff} !Int !*File -> !*File
WriteXCoffArray xcoff_a i output
	| size xcoff_a == i 
		= output
		
		#! output
			= WriteXCoff xcoff_a.[i] output
		= WriteXCoffArray xcoff_a (inc i) output
*/

/*
// WriteXCoffArray :: *(a b) .c .d -> (.(a b),.d) | Array .a & replace_u , update_u , usize_u b;
//WriteXCoffArray :: *(a v:SXcoff) .b -> (u:(a w:SXcoff),.b) | Array .a, [u v <= w];
WriteXCoffArray xcoff_a output
	#! (xcoff_a,output)
		= loopAur write_xcoff xcoff_a output;
	= (xcoff_a,output);
	
	
	/*
		#! (marked_bool_a,output)
		= loopA (\bool output -> fwritec (if bool 'T' 'F') output) marked_bool_a output;
		*/
*/		

	
			

/*
class DefaultElem .e
where
	DefaultElem :: .e

instance DefaultElem Int
where DefaultElem  = 42

instance DefaultElem (*SXcoff)
where DefaultElem = empty_xcoff
*/

WriteNamesTable :: !*State !*File -> (!*State,!*File)
WriteNamesTable state output 
	#! (namestable,state)
		= select_namestable state
	#! (size_names_table,namestable)
		= usize namestable
	#! n_elements
		= CountNamesTableElements 0 size_names_table 0 namestable
		
	#! (namestable,output)
		= loopAonOutput write_names_table_elements namestable output;
		
	// update state
	#! state
		= { state &
			namestable = namestable
		}

	= (state,/*write_names_table 0 size_names_table namestable output*/ output)	
	where		
		
		/*
		write_names_table i limit namestable file
			| i == limit
				= file
				
				#! file
					= write_names_table_elements namestable.[i] file
				=  write_names_table (inc i) limit namestable file
	
		where
	*/
		write_names_table_elements EmptyNamesTableElement file
			= file
		write_names_table_elements (NamesTableElement s i0 i1 ntes) file
			#! file
				= fwrites s file
			#! file
				= fwritec '\n' file
			#! file
				= fwritei i0 file
			#! file
				= fwritei i1  file
			= write_names_table_elements ntes file
		
		/*
		** If the complement is extended, the amount of names table elements
		** need to be counted. Uncomment the comments above and in ReadState
		*/		
		CountNamesTableElements i limit n_names_table_elements namestable
			| i == limit
				=  n_names_table_elements
				
				#! n_names_table_elements
					= count n_names_table_elements namestable.[i]
				= CountNamesTableElements (inc i) limit n_names_table_elements namestable
			where
				count n_names_table_elements EmptyNamesTableElement
					=  n_names_table_elements
				count n_names_table_elements (NamesTableElement _ _ _ ntes)
					=  count (inc n_names_table_elements) ntes 
	
// ---------------------------------------------------------------------------------
WriteLibraryList :: !LibraryList !*File -> !*File
WriteLibraryList EmptyLibraryList output
	=  output
WriteLibraryList (Library s /* mac */ i0 lsl i1 ll) output
	#! output
		= fwrites s output
	#! output
		= fwritec '\n' output

	// PC
	#! output
		= fwritei i0 output
	#! output
		= WriteLibrarySymbolsList lsl output
	#! output 
		= fwritei i1 output
	= WriteLibraryList ll output
where
	WriteLibrarySymbolsList lsl output
		#! output
			= fwritei (count lsl 0) output
		#! output
			= write_library_symbols_list lsl output
		= output
	where
		count EmptyLibrarySymbolsList i 
			= i
		count (LibrarySymbol _ lsl) i
			= count lsl (inc i)
	
		write_library_symbols_list EmptyLibrarySymbolsList output 
			= output
		write_library_symbols_list (LibrarySymbol s lsl) output
			#! output 
				= fwrites s output
			#! output
				= fwritec '\n' output
			= write_library_symbols_list lsl output

WriteLibraryFileNames library_file_names output
	// length = n_libraries
	#! output
		= foldl write_name output library_file_names;
	= output;
where
	write_name output s
		#! output
			= fwritei (size s) output;
		#! output
			= fwrites s output;
		= output;

		
// PLATFORM DEPENDENT
// platform specific
/*
instance Output (!{#Char},!*File)
where
//	WriteOutput :: !WriteOutputRecord /*!Int !Int !{#Char}*/ (!*{#Char},!*File) -> (!*{#Char},!*File);
	WriteOutput {file_or_memory,offset,string} /*0 _ string*/ (data,file)
		= case file_or_memory of {
			0
				-> (data, fwrites string file);
			1
				-> (data +++ string, file);
				
			_
				-> abort "WriteState: internal error";
		};
			
	ChangeState {file_n,module_n,state} pe_file
		#! (Module i0 i1 i2 i3 offset i5 s, state)
			= sel_symbol file_n module_n state	
		/*
		** Retrieve the computed offset of module_n in the file
		*/	
		#! (first_symbol_n,state)
			= selacc_marked_offset_a file_n state
		#! (module_n_offset, state)
			= selacc_module_offset_a (first_symbol_n+module_n) state;	
		
	 	#! state
			= update_symbol (Module i0 i1 i2 i3 (module_n_offset+4) i5 s) file_n module_n state 
		= (state,pe_file);
	*/	
	
/*
INSERT ME HERE
	#! (xcoff_a,state)
		= select_xcoff_a state
	#! xcoff_list 
		= xcoff_array_to_list 0 xcoff_a
 
	#! (module_offset_a,state)
		= select_module_offset_a state

	// mac
	#! (text_end,data_end)
		= (0,0);
/*
	// pc
	#! (inverted_marked_bool_a,text_end,module_offset_a,xcoff_list) 
		= compute_module_offsets Text 0 /* base */ xcoff_list 0 	   0 inverted_marked_bool_a module_offset_a
	#! (inverted_marked_bool_a,data_end,module_offset_a,xcoff_list)
		= compute_module_offsets Data 0 /* base */ xcoff_list text_end 0 inverted_marked_bool_a module_offset_a
*/
	#! state = 
		{ state &
			xcoff_a = xcoff_list_to_array n_xcoff_files xcoff_list, // PC xcoff_list_to_xcoff_array xcoff_list n_xcoff_files,
			module_offset_a = module_offset_a,
			marked_bool_a = inverted_marked_bool_a
			
		}
		// xcoff_list_to_array
	#! output
		= fwritei data_end output

	// moved	
	#! alignment
		= 2;
	#! alignment_mask
		= dec (1 << alignment);
	#! aligned_text_end
		= (text_end + alignment_mask) bitand (bitnot alignment_mask);
	#! delta
		= aligned_text_end - text_end;

	// inserted
	#! nop_byte
		= toChar 0x90;
	#! s_data_section
		= data_end - aligned_text_end;
	
	// mac
	#! data
		= "";	
	/*
	PC
//	#! ((_,data,output),state,files)
//		= write_code_to_pe_filesD n_xcoff_files /*True*/ False 0 0 (0,0) state (0,createArray s_data_section nop_byte,output) files;

	#! ((data,output),state,files)
		= write_code_to_pe_files n_xcoff_files False 0 0 (0,0) state True ("",output) files
	*/  

	#! nop_byte
		= toChar 0x90;
	# output
		= fwrites (createArray delta nop_byte) output	
		
	#! (i,output)
		= fposition output
	| i <> (4 + text_end + delta)
		= abort ("WriteState: computed text size does not correspond with file offset" +++ (toString i))
	
	#! output
		= fwrites data output
	#! (i,output)
		= fposition output
	#! required_offset
		= 4 + data_end
	| i <> required_offset
		= abort ("Real: " +++ (toString required_offset) +++ " - " +++ (toString i))
*/
		