#include "dwresolve.h"

#include <elfutils/libdwfl.h>
#include <stdio.h>
#include <string.h>

int
pw_dw_resolve_addr(const char *module_path, uint64_t address, char *out, size_t out_len)
{
    if (module_path == NULL || module_path[0] == '\0' || out == NULL || out_len == 0)
        return 0;

    out[0] = '\0';

    Dwfl_Callbacks callbacks = {
        .find_elf = dwfl_build_id_find_elf,
        .find_debuginfo = dwfl_standard_find_debuginfo,
        .section_address = dwfl_offline_section_address,
    };

    Dwfl *dwfl = dwfl_begin(&callbacks);
    if (dwfl == NULL)
        return 0;

    Dwfl_Module *module = dwfl_report_elf(dwfl, module_path, module_path, -1, 0, false);
    if (module == NULL) {
        dwfl_end(dwfl);
        return 0;
    }

    if (dwfl_report_end(dwfl, NULL, NULL) != 0) {
        dwfl_end(dwfl);
        return 0;
    }

    Dwfl_Line *line = dwfl_module_getsrc(module, (Dwarf_Addr) address);
    if (line == NULL && address > 0)
        line = dwfl_module_getsrc(module, (Dwarf_Addr) address - 1);

    if (line == NULL) {
        dwfl_end(dwfl);
        return 0;
    }

    int lineno = 0;
    int column = 0;
    Dwarf_Addr resolved_addr = 0;
    const char *file = dwfl_lineinfo(line, &resolved_addr, &lineno, &column, NULL, NULL);
    if (file == NULL || file[0] == '\0' || lineno <= 0) {
        dwfl_end(dwfl);
        return 0;
    }

    snprintf(out, out_len, "??\n%s:%d\n", file, lineno);
    dwfl_end(dwfl);
    return 1;
}
