implementation module WriteMapFile;

import State;
import ExtFile;
import ExtInt;
import pdExtInt;
import LinkerMessages;
import StdEnv;
import NamesTable;

:: MapRecord = {
		offset		:: !Int
	,	symbol_name	:: !String
	,	file_name	:: !String
	};
	
:: ColumnWidths	= {
		symbol_name_column	:: !Int
	,	file_name_column	:: !Int
	};
	
DefaultColumnWidths :: ColumnWidths;
DefaultColumnWidths 
	= { ColumnWidths |
			symbol_name_column	= 0
		,	file_name_column	= 0
	};		
	
generate_map_file :: !*State !*Files -> (!*State,!*Files);
generate_map_file state=:{application_name,xcoff_a}  files
	// create map file
	#! map_file_name
		= fst (ExtractPathFileAndExtension application_name) +++ ".map";
	#! (ok,map_file,files)
		= fopen map_file_name FWriteText files;
	| not ok
		# msg
			= "could not open map file '" +++ map_file_name +++ "'";
		= (AddMessage (LinkerError msg) state,files);
		
	#! map_file
		= fwrites ("Map file for " +++ application_name +++ "\n\n") map_file;
		
	// generate map
	#! column_widths 
		= { ColumnWidths |
			symbol_name_column	= s_symbol_name_column
		,	file_name_column 	= s_file_name_column
		};
		
	#! (names_table,state)
		= acc_namestable (\names_table -> (names_table,{})) state;
	#! (map_records,column_widths,names_table,state)
		= generate_map_records [] 0 column_widths names_table state;
	#! map_records
		= sortBy (\{offset=offset1} {offset=offset2} -> offset1 < offset2) map_records;
	#! state
		= { state & namestable = names_table };

	// write map to disk; calculate max widths of columns
	#! tab_size
		= 4;
	#! column_widths
		= { ColumnWidths |
			symbol_name_column	= (roundup_to_multiple column_widths.symbol_name_column tab_size)
		,	file_name_column 	= (roundup_to_multiple column_widths.file_name_column tab_size)
		};
	
	// write header
	#! map_file
		= format "Offset:" max_offset_column_size map_file;
	#! map_file
		= format symbol_name_column column_widths.symbol_name_column map_file;
	#! map_file
		= format file_name_column column_widths.file_name_column map_file;
	#! map_file
		= fwritec '\n' map_file;

	#! map_file
		= foldl (f column_widths) map_file map_records;
		
	// close map file
	#! (_,files)
		= fclose map_file files;
	= (state,files);

where {
	// constants
	max_offset_column_size	= 14;
	
	// 2nd column
	symbol_name_column		= "Symbol name:";
	s_symbol_name_column	= size symbol_name_column;
	
	// 3th column
	file_name_column		= "File:";
	s_file_name_column		= size file_name_column;
	
	// write table
	f column_witdhs=:{symbol_name_column,file_name_column} map_file {offset,symbol_name,file_name}
		// write offset from base
		#! map_file
			= format (hex_int offset) max_offset_column_size map_file;

		// write symbol name
		#! map_file
			= format symbol_name symbol_name_column map_file;

		// write name of defining file
		#! map_file
			= fwrites file_name map_file;
		#! map_file
			= fwritec '\n' map_file;
		= map_file;
		
	format s max_s map_file
		#! map_file
			= fwrites s map_file;
		#! map_file
			= fwrite_tabs (max_s - size s) map_file;
		= map_file;
	where {
		fwrite_tabs n map_file
		| n == 0
			= map_file;
			= fwrite_tabs (dec n) (fwritec ' ' map_file);
	}
	
	generate_map_records map_records i column_widths names_table state
		| i == SYMBOL_TABLE_SIZE
			= (map_records,column_widths,names_table,state);
			
			#! (names_table_element,names_table)
				= names_table![i];
			#! (column_widths,map_records,state) 
				= generate_more_map_records column_widths map_records names_table_element state;
			= generate_map_records map_records (inc i) column_widths names_table state;

	where {
		generate_more_map_records column_widths map_records EmptyNamesTableElement state
			= (column_widths,map_records,state);
			
		generate_more_map_records column_widths=:{symbol_name_column,file_name_column} map_records (NamesTableElement symbol_name symbol_n file_n names_table_elements) state
			| file_n < 0
				= generate_more_map_records column_widths map_records names_table_elements state;
				
				#! (first_symbol_n,state) 
					= selacc_marked_offset_a file_n state;
				#! (symbol_marked,state)
					= selacc_marked_bool_a (first_symbol_n + symbol_n) state;
				| symbol_marked 
					#! (offset,state)
						= address_of_label3 file_n symbol_n state;
					#! (file_name,state)
						= select_file_name file_n state;
					#! (_,file_name)
						= ExtractPathAndFile file_name;
			
					#! map_record = { MapRecord |
							offset		= offset
						,	symbol_name	= symbol_name
						,	file_name	= file_name
						};
						
					# max_symbol_name_column
						= max (size symbol_name) symbol_name_column;
					# max_file_name_column
						= max (size file_name) file_name_column;
						
					#! column_widths
						= if ((max_symbol_name_column > symbol_name_column) || (max_file_name_column > file_name_column))
							{symbol_name_column = max_symbol_name_column,file_name_column=max_file_name_column}
							column_widths;
						
					= generate_more_map_records column_widths [map_record:map_records] names_table_elements state;
					
					= generate_more_map_records column_widths map_records names_table_elements state;
		where {
			max_symbol_name_column = max 
		}
	}
}

address_of_label3 :: !Int !Int !State -> (!Int,!State);
address_of_label3 file_n symbol_n state
	= address_of_label2 file_n symbol_n state;
