#pragma once
#include <exception>
#include <vector>
#include <cstdint>
#include "string.h"
#include "refcnt.h"
#include "function.h"

namespace panda {

struct ArgumentsHolder: public Refcnt { };
using ArgumentsHolderSP = iptr<ArgumentsHolder>;

struct Stackframe: public Refcnt {
    string file;
    string library;
    string name;
    string mangled_name;
    std::uint64_t address = 0;
    std::uint64_t offset = 0;
    std::uint64_t line_no = 0;
    ArgumentsHolderSP args;
    virtual ~Stackframe();
};

using StackframeSP = iptr<Stackframe>;
using StackFrames = std::vector<StackframeSP>;

struct BacktraceBackend: Refcnt {
    virtual bool produce_frame(StackFrames& result, size_t i) = 0;
};
using BacktraceBackendSP = iptr<BacktraceBackend>;

struct BacktraceInfo : Refcnt {
    BacktraceInfo(std::vector<StackframeSP>&& frames_) : frames(std::move(frames_)) {}
    virtual ~BacktraceInfo();
    const std::vector<StackframeSP>& get_frames() const noexcept { return frames;}
    virtual string to_string() const noexcept;

    StackFrames frames;
};
using BacktraceInfoSP = iptr<BacktraceInfo>;

struct Backtrace;
using RawTrace = std::vector<void*>;
using RawTraceProducer = int(*)(void**, int);
using BacktraceProducer = BacktraceBackendSP(*)(const Backtrace& raw_traces);

struct Backtrace {
    static const constexpr int max_depth = 150;

    Backtrace () noexcept;
    Backtrace (const Backtrace &other) noexcept;
    virtual ~Backtrace ();
    Backtrace& operator=(const Backtrace& other) = default;

    iptr<BacktraceInfo> get_backtrace_info() const noexcept;
    const RawTrace& get_trace () const noexcept { return buffer; }

    static void install_producer  (const BacktraceProducer& producer) noexcept;
    static void uninstall_producer(const BacktraceProducer& producer) noexcept;
    static string dump_trace() noexcept;

    std::vector<void*> buffer;
};

template <typename T>
struct bt : T, Backtrace {
    template<typename ...Args>
    bt (Args&&... args) noexcept : T(std::forward<Args...>(args...)) {}
};

struct exception : std::exception, Backtrace {
    exception () noexcept;
    exception (const string& whats) noexcept;
    exception (const exception& oth) noexcept;
    exception& operator= (const exception& oth) noexcept;

    const char* what () const noexcept override;

    virtual string whats () const noexcept;

private:
    mutable string _whats;
};


}