/*
** Symbol Set Link Script Generator for ld
*/

#include "mr4th_base.h"
#include "mr4th_cmdln.h"
#include "exefmt/elf/mr4th_elf.h"
#include "exefmt/elf/mr4th_elf.parse.h"

#include "mr4th_stdio.h"

#include "mr4th_base.c"
#include "mr4th_cmdln.c"
#include "exefmt/elf/mr4th_elf.c"
#include "exefmt/elf/mr4th_elf.parse.c"

#include "mr4th_stdio.c"

////////////////////////////////
// Entry Point

int main(int argc, char **argv){
  os_main_init(argc, argv);
  
  Arena *arena = arena_alloc();
  
  // arguments
  String8List command_line_args = os_command_line_arguments();
  CMDLN *cmdln = cmdln_from_args(arena, &command_line_args);
  
  String8 output_file_name = cmdln_get_str8(cmdln, str8_lit("out"), 'o');
  if (output_file_name.size == 0){
    output_file_name = str8_lit("sy.ld");
  }
  
  // symbol set names
  String8List symbol_set_names = {0};
  BUFFER buckets;
  buffer_alloc(&buckets, GB(64));
  hash_buckets_init(&buckets, 512);
  
  for (U32 input_idx = 0;; input_idx += 1){
    // load input file
    String8 input_file_name = cmdln_input_from_idx(cmdln, input_idx);
    if (input_file_name.size == 0){
      break;
    }
    String8 input_data = os_file_read(arena, input_file_name);
    
    // parse
    ELF_Parse *parse = elfp_begin(arena, input_data);
    if (parse != 0){
      
      // insert symbol sets into symbol_set_names
      U32 section_count = elfp_section_count(parse);
      for (U32 i = 0; i < section_count; i += 1){
        String8 secname = elfp_section_name(parse, i);
        if (secname.size >= 4 &&
            str8_match(secname, str8_lit(".sy."), StringMatchFlag_PrefixMatch)){
          String8 syname = str8_skip(secname, 4);
          U64 hash = str8_hash(syname);
          B32 match = 0;
          for (HASH_Node *hash_node = hash_buckets_first(&buckets, hash);
               hash_node != 0;
               hash_node = hash_node->next){
            if (hash_node->hash == hash){
              String8Node *node = (String8Node*)PtrFromInt(hash_node->val);
              if (str8_match(node->string, syname, 0)){
                match = 1;
                break;
              }
            }
          }
          
          if (!match){
            str8_list_push(arena, &symbol_set_names, syname);
            hash_buckets_expand(&buckets, symbol_set_names.node_count);
            hash_buckets_insert(arena, &buckets, hash, IntFromPtr(symbol_set_names.last));
          }
        }
      }
      
    }
    
  }
  
  // print output linker script
  STREAM *stream = stream_new();
  
  stream_printf(stream,
                "SECTIONS {\n"
                " .sy : {\n");
  
  for (String8Node *node = symbol_set_names.first;
       node != 0; node = node->next){
    stream_printf(stream, "  syfirst__%S = .; *(.sy.%S); syopl__%S = .;\n",
                  &node->string, &node->string, &node->string);
  }
  
  stream_printf(stream,
                " }\n"
                "}\n"
                "INSERT AFTER .data;\n");
  
  os_file_write_stream(output_file_name, stream);
  
  return(0);
}
