/******************************************************************************
 *
 * Copyright (c) 2017, the Perspective Authors.
 *
 * This file is part of the Perspective library, distributed under the terms of
 * the Apache License 2.0.  The full license can be found in the LICENSE file.
 *
 */

#ifdef __APPLE__
#include <perspective/first.h>
#include <perspective/tracing.h>
#include <perspective/tracing_impl_linux.h>

extern "C" {

__attribute__((__constructor__)) void
th_trace_init() {
    using namespace std;
    using namespace perspective;
    if (!th_file) {
        pid_t pid = getpid();
        pid_t tid = syscall(__NR_gettid);
        std::stringstream ss;
        ss << "psptrace_" << pid << "_" << tid << ".trace";
        th_file = fopen(ss.str().c_str(), "wb");
        PSP_VERBOSE_ASSERT(th_file != 0, "Unable to open trace file");
    } else {
        PSP_COMPLAIN_AND_ABORT("Thread file unexpectedly non null");
    }
}

__attribute__((__destructor__)) void
th_trace_fini() {
    using namespace std;
    using namespace perspective;
    flush_thbuffer(th_traceidx);
    fclose(th_file);

    pid_t pid = getpid();
    pid_t tid = syscall(__NR_gettid);

    std::stringstream sso;
    sso << "psptrace_" << pid << "_" << tid << ".syms";
    std::ofstream of(sso.str());

    std::stringstream ssi;
    ssi << "psptrace_" << pid << "_" << tid << ".trace";

    int ifd = open(ssi.str().c_str(), O_RDONLY);
    PSP_VERBOSE_ASSERT(ifd != -1, "Error opening file");

    struct stat fb;
    fstat(ifd, &fb);

    uint64_t ifsize = fb.st_size;

    if (ifsize == 0)
        return;

    void* iptr = mmap(0, ifsize, PROT_READ, MAP_SHARED, ifd, 0);

    PSP_VERBOSE_ASSERT(iptr != MAP_FAILED, "Error in mmap");
    PSP_VERBOSE_ASSERT(
        ifsize % sizeof(t_instrec) == 0, "Partial record encountered");

    std::int64_t ndrecs = ifsize / sizeof(t_instrec);

    t_instrec* irecs = static_cast<t_instrec*>(iptr);

    tsl::hopscotch_set<void*> fptrs;

    for (t_index idx = 0; idx < ndrecs; ++idx) {
        t_instrec* irec = irecs + idx;
        fptrs.emplace(irec->t_fntrace.m_fn);
    }

    for (tsl::hopscotch_set<void*>::const_iterator iter = fptrs.begin();
         iter != fptrs.end(); ++iter) {

        of << *iter << " ";
        char** mangled;
        void* code_arr[1];
        code_arr[0] = *iter;
        char** stack_strings = backtrace_symbols(code_arr, 1);

        size_t sz = THR_MAX_FUNCNAME_LEN;

        char* function = static_cast<char*>(malloc(sz));
        char *begin = 0, *end = 0;
        for (char* j = stack_strings[0]; *j; ++j) {
            if (*j == '(') {
                begin = j;
            } else if (*j == '+') {
                end = j;
            }
        }
        if (begin && end) {

            *begin++ = ' ';
            *end = '\0';
            int status;
            char* ret = abi::__cxa_demangle(begin, function, &sz, &status);
            if (ret) {
                function = ret;
            } else {
                std::strncpy(function, begin, sz);
                std::strncat(function, "()", sz);
                function[sz - 1] = ' ';
            }

            of << function << std::endl;
        } else {
            of << stack_strings[0] << std::endl;
        }
        free(function);
        free(stack_strings);
    }

    close(ifd);
    munmap(iptr, ifsize);
}

void
flush_thbuffer(perspective::std::int32_t elemidx) {
    using namespace std;
    using namespace perspective;
    t_instrec* mbuf = th_trace_buffer;
    fwrite(mbuf, sizeof(t_instrec), elemidx, th_file);
}
} // end extern c

#endif // end _WIN32
