//
// Created by koncord on 25.10.17.
//

#include "stacktrace.hpp"
#include <components/openmw-mp/Log.hpp>

#ifndef _WIN32

#include <execinfo.h>

void stacktrace()
{
    LOG_MESSAGE_SIMPLE(Log::LOG_FATAL, "Stacktrace:");

    void *array[50];
    int size = backtrace(array, 50);

    auto messages = backtrace_symbols(array, size);

    size_t funcnamesize = 256;
    auto funcname = (char *) malloc(funcnamesize);
    //skip first stack frame (points here)
    for (int i = 1; i < size && messages != nullptr; ++i)
    {
        char *beginName = nullptr, *beginOffset = nullptr, *endOffset = nullptr;
        for (char *p = messages[i]; *p; ++p)
        {
            if (*p == '(')
                beginName = p;
            else if (*p == '+')
                beginOffset = p;
            else if (*p == ')' && beginOffset)
            {
                endOffset = p;
                break;
            }
        }

        if (beginName && beginOffset && endOffset && beginName < beginOffset)
        {
            *beginName++ = '\0';
            *beginOffset++ = '\0';
            *endOffset = '\0';

            // mangled name is now in [beginName, beginOffset) and caller offset in [beginOffset, endOffset).

            int status;
            char *ret = abi::__cxa_demangle(beginName, funcname, &funcnamesize, &status);
            if (status == 0)
            {
                funcname = ret; // use possibly realloc()-ed string
                LOG_APPEND(Log::LOG_FATAL, "\t%s : %s+%s", messages[i], funcname, beginOffset);
            }
            else // demangling failed.
                LOG_APPEND(Log::LOG_FATAL, "\t%s : %s()+%s", messages[i], beginName, beginOffset);
        }
        else
            LOG_APPEND(Log::LOG_FATAL, "\t%s", messages[i]);
    }

    free(messages);
    free(funcname);
}

#else

#include "stackwalker/StackWalker.h"

class StackWalkerClr: public StackWalker
{
    std::string out;
    virtual void OnOutput(LPCSTR szText)
    {
        out.append(szText);
    }

public:
    std::string &getData()
    {
        ShowCallstack();
        return out;
    }
};


void stacktrace()
{
    LOG_MESSAGE_SIMPLE(Log::LOG_FATAL,  "Stacktrace:");
    StackWalkerClr swc;
    LOG_APPEND(Log::LOG_FATAL, swc.getData().c_str());
}

#endif