/*
** ELF File Dumper
*/

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

#include "mr4th_elf.dump.h"

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

////////////////////////////////
// ELF DUMP Arguments Functions

MR4TH_SYMBOL_STATIC ELFDUMP_Arguments*
elfdump_args(Arena *arena, String8List *command_line_args){
    // parse
    CMDLN *cmdln = cmdln_from_args(arena, command_line_args);
    
    // setup args
    ELFDUMP_Arguments *args = push_array(arena, ELFDUMP_Arguments, 1);
    
    // get input file name
    args->input_file_name = cmdln_input_from_idx(cmdln, 0);
    
    return(args);
}

////////////////////////////////
// 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();
    ELFDUMP_Arguments *args = elfdump_args(arena, &command_line_args);
    
    // load input file
    String8 input_data = os_file_read(arena, args->input_file_name);
    
    // parse
    ELF_Parse *elf = elfp_begin(arena, input_data);
    
    // begin dump
    if (elf == 0){
        m4_printf("Unsuccessful parse\n");
    }
    else{
        ArenaTemp scratch = arena_get_scratch(0, 0);
        
        // elf header
        {
            ELF_Header64 hdr = {0};
            elfp_header_read(elf, &hdr);
            
            for (U32 i = 0; i < ELF_NUM_IDENT; i += 1){
                String8 ident_string = elf_str8_from_identification_idx(i);
                
                String8 val_string = {0};
                switch (i){
                    case ELF_IdentificationIdx_CLASS:
                    {
                        val_string = elf_str8_from_class(hdr.ident[i]);
                    }break;
                    
                    case ELF_IdentificationIdx_DATA:
                    {
                        val_string = elf_str8_from_encoding(hdr.ident[i]);
                    }break;
                    
                    case ELF_IdentificationIdx_OSABI:
                    {
                        val_string = elf_str8_from_osabi(hdr.ident[i]);
                    }break;
                    
                    default:
                    {
                        val_string = str8_pushf(scratch.arena, "0x%02x", hdr.ident[i]);
                    }break;
                }
                
                m4_printf("elf_header.ident[%10S]    = %S\n", &ident_string, &val_string);
            }
            
            String8 type_string = elf_str8_from_file_type(hdr.type);
            String8 machine_string = elf_str8_from_machine_type(hdr.machine);
            
            m4_printf("elf_header.type                 = %S\n", &type_string);
            m4_printf("elf_header.machine              = %S\n", &machine_string);
            m4_printf("elf_header.version              = %u\n", hdr.version);
            m4_printf("elf_header.entry                = 0x%08llx\n", hdr.entry);
            m4_printf("elf_header.segment_table_foff   = 0x%08llx\n", hdr.segment_table_foff);
            m4_printf("elf_header.section_table_foff   = 0x%08llx\n", hdr.section_table_foff);
            // TODO(allen): flags pneumonics
            m4_printf("elf_header.flags                = 0x%08x\n", hdr.flags);
            m4_printf("elf_header.header_size          = %u\n", hdr.header_size);
            m4_printf("elf_header.segment_size         = %u\n", hdr.segment_size);
            m4_printf("elf_header.segment_count        = %u\n", hdr.segment_count);
            m4_printf("elf_header.section_size         = %u\n", hdr.section_size);
            m4_printf("elf_header.section_count        = %u\n", hdr.section_count);
            m4_printf("elf_header.string_section_index = %u\n", hdr.string_section_index);
            
            m4_printf("\n");
        }
        
        // sections
        U32 section_count = elfp_section_count(elf);
        if (section_count > 0){
            ELF_Section64 section;
            for (U32 i = 0; i < section_count; i += 1){
                String8 name = elfp_section_name(elf, i);
                elfp_section_read(elf, i, &section);
                
                String8 type_string = elf_str8_from_section_type(section.type);
                String8 flags_string =
                    elf_str8_from_section_flags(scratch.arena, section.flags);
                
                m4_printf("section[%u] = {\n"
                          " name      = \"%S\",\n"
                          " type      = %S (0x%x),\n"
                          " flags     = %S,\n"
                          " addr      = 0x%08llx,\n"
                          " size      = %$llu,\n"
                          " link      = %u,\n"
                          " info      = %u,\n"
                          " addralign = %llu,\n"
                          " entsize   = %$llu,\n"
                          "}\n",
                          i, &name,
                          &type_string, section.type,
                          &flags_string,
                          section.addr,
                          section.size,
                          section.link,
                          section.info,
                          section.addralign,
                          section.entsize);
                
            }
            
            m4_printf("\n");
            
        }
        
        // segments
        U32 segment_count = elfp_segment_count(elf);
        if (segment_count > 0){
            ELF_Segment64 segment;
            for (U32 i = 0; i < segment_count; i += 1){
                elfp_segment_read(elf, i, &segment);
                
                String8 type_string = elf_str8_from_segment_type(segment.type);
                String8 flags_string =
                    elf_str8_from_segment_flags(scratch.arena, segment.flags);
                
                m4_printf("segment[%u] = {\n"
                          " type        = %S (0x%x),\n"
                          " flags       = %S,\n"
                          " offset      = 0x%08llx,\n"
                          " vaddr       = 0x%08llx,\n"
                          " paddr       = 0x%08llx,\n"
                          " file_size   = %$llu,\n"
                          " memory_size = %$llu,\n"
                          " align       = %llu,\n"
                          "}\n",
                          i,
                          &type_string, segment.type,
                          &flags_string,
                          segment.offset,
                          segment.vaddr,
                          segment.paddr,
                          segment.file_size,
                          segment.memory_size,
                          segment.align);
            }
            
            m4_printf("\n");
        }
        
        // symbol tables
        String8 symbol_table_name[ELFP_SYMBOL_TABLE_COUNT];
        symbol_table_name[ELFP_SYMBOL_TABLE_symtab] = str8_lit("symtab");
        symbol_table_name[ELFP_SYMBOL_TABLE_dynsym] = str8_lit("dynsym");
        
        for (U32 k = 0; k < ELFP_SYMBOL_TABLE_COUNT; k += 1){
            U32 symbol_count = elfp_symbol_count(elf, k);
            ELF_Symbol64 symbol;
            for (U32 i = 0; i < symbol_count; i += 1){
                String8 name = elfp_symbol_name(elf, k, i);
                elfp_symbol_read(elf, k, i, &symbol);
                
                ELF_SymbolBinding bind = ELF_Symbol_BindFromInfo(symbol.info);
                ELF_SymbolType    type = ELF_Symbol_TypeFromInfo(symbol.info);
                
                ELF_SymbolVis vis = ELF_Symbol_VisFromOther(symbol.other);
                
                String8 bind_string = elf_str8_from_symbol_binding(bind);
                String8 type_string = elf_str8_from_symbol_type(type);
                String8 vis_string  = elf_str8_from_symbol_vis(vis);
                
                m4_printf("%S[%u] = {\n"
                          " name          = \"%S\",\n"
                          " info          = bind: %S, type: %S,\n"
                          " other         = vis: %S,\n"
                          " section_index = %u,\n"
                          " value         = %llu,\n"
                          " size          = %$llu,\n"
                          "}\n",
                          &symbol_table_name[k], i,
                          &name,
                          &bind_string, &type_string,
                          &vis_string,
                          symbol.section_index,
                          symbol.value,
                          symbol.size);
            }
            
            m4_printf("\n");
        }
        
        // relocations
        ELF_RelocationAdd64 reloc;
        for (U32 i = 0; i < section_count; i += 1){
            U32 reloc_count = elfp_relocs_count(elf, i);
            if (reloc_count > 0){
                m4_printf("relocations(%u) = {\n", i);
                for (U32 j = 0; j < reloc_count; j += 1){
                    elfp_relocs_read(elf, i, j, &reloc);
                    m4_printf(" { offset = %llu,\n"
                              "   info   = %llu,\n"
                              "   addend = %lld,\n"
                              " },\n", reloc.offset, reloc.info, reloc.addend);
                }
                m4_printf("}\n\n");
            }
        }
        
        arena_release_scratch(&scratch);
    }
    
    return(0);
}
