// -*- C++ -*-
//===----------------------------------------------------------------------===//
// C++23 Overlay Header: stacktrace
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP_STACKTRACE
#define _LIBCPP_STACKTRACE

#if __cplusplus >= 202100L

#include <string>
#include <vector>
#include <memory>

#define __cpp_lib_stacktrace 202011L

namespace std {

//===----------------------------------------------------------------------===//
// stacktrace_entry: Represents a single stack frame
//===----------------------------------------------------------------------===//

class stacktrace_entry {
public:
    using native_handle_type = void*;

    constexpr stacktrace_entry() noexcept
        : _M_handle(nullptr), _M_index(0) {}

    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept = default;
    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept = default;

    constexpr ~stacktrace_entry() = default;

    constexpr native_handle_type native_handle() const noexcept {
        return _M_handle;
    }

    constexpr explicit operator bool() const noexcept {
        return _M_handle != nullptr;
    }

    string description() const {
        if (!_M_handle) return "";
        // Stub: Return empty string; real implementation would symbolicate
        return "";
    }

    string source_file() const {
        if (!_M_handle) return "";
        // Stub: Return empty string; real implementation would use debug info
        return "";
    }

    uint_least32_t source_line() const {
        if (!_M_handle) return 0;
        // Stub: Return 0; real implementation would use debug info
        return 0;
    }

    constexpr bool operator==(const stacktrace_entry& rhs) const noexcept {
        return _M_handle == rhs._M_handle && _M_index == rhs._M_index;
    }

    constexpr bool operator!=(const stacktrace_entry& rhs) const noexcept {
        return !(*this == rhs);
    }

private:
    friend class stacktrace_entry;
    template <typename Allocator> friend class basic_stacktrace;

    constexpr stacktrace_entry(void* handle, size_t index) noexcept
        : _M_handle(handle), _M_index(index) {}

    void* _M_handle;
    size_t _M_index;
};

//===----------------------------------------------------------------------===//
// basic_stacktrace: A collection of stack frames
//===----------------------------------------------------------------------===//

template <typename Allocator = allocator<stacktrace_entry>>
class basic_stacktrace {
public:
    using value_type = stacktrace_entry;
    using const_reference = const value_type&;
    using reference = const_reference;
    using const_iterator = typename vector<value_type, Allocator>::const_iterator;
    using iterator = const_iterator;
    using reverse_iterator = typename vector<value_type, Allocator>::const_reverse_iterator;
    using const_reverse_iterator = reverse_iterator;
    using difference_type = typename vector<value_type, Allocator>::difference_type;
    using size_type = typename vector<value_type, Allocator>::size_type;
    using allocator_type = Allocator;

    basic_stacktrace() noexcept(noexcept(Allocator()))
        : _M_frames() {}

    explicit basic_stacktrace(const allocator_type& alloc) noexcept
        : _M_frames(alloc) {}

    basic_stacktrace(const basic_stacktrace& other)
        : _M_frames(other._M_frames) {}

    basic_stacktrace(basic_stacktrace&& other) noexcept
        : _M_frames(std::move(other._M_frames)) {}

    basic_stacktrace& operator=(const basic_stacktrace& other) {
        if (this != &other) {
            _M_frames = other._M_frames;
        }
        return *this;
    }

    basic_stacktrace& operator=(basic_stacktrace&& other) noexcept {
        _M_frames = std::move(other._M_frames);
        return *this;
    }

    ~basic_stacktrace() = default;

    // Current stacktrace (stub implementation - returns empty stacktrace)
    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept {
        basic_stacktrace st(alloc);
        // Stub: Real implementation would capture the call stack
        // For now, return an empty stacktrace
        return st;
    }

    static basic_stacktrace current(size_type skip, const allocator_type& alloc = allocator_type()) noexcept {
        basic_stacktrace st(alloc);
        // Stub: Real implementation would capture the call stack, skipping 'skip' frames
        (void)skip; // Unused in stub
        return st;
    }

    static basic_stacktrace current(size_type skip, size_type max_frames,
                                    const allocator_type& alloc = allocator_type()) noexcept {
        basic_stacktrace st(alloc);
        // Stub: Real implementation would capture the call stack
        (void)skip;
        (void)max_frames; // Unused in stub
        return st;
    }

    // Iteration
    const_iterator begin() const noexcept { return _M_frames.begin(); }
    const_iterator end() const noexcept { return _M_frames.end(); }
    const_iterator cbegin() const noexcept { return _M_frames.cbegin(); }
    const_iterator cend() const noexcept { return _M_frames.cend(); }

    const_reverse_iterator rbegin() const noexcept { return _M_frames.rbegin(); }
    const_reverse_iterator rend() const noexcept { return _M_frames.rend(); }
    const_reverse_iterator crbegin() const noexcept { return _M_frames.crbegin(); }
    const_reverse_iterator crend() const noexcept { return _M_frames.crend(); }

    // Capacity
    size_type size() const noexcept { return _M_frames.size(); }
    bool empty() const noexcept { return _M_frames.empty(); }

    // Element access
    const_reference operator[](size_type pos) const { return _M_frames[pos]; }
    const_reference at(size_type pos) const { return _M_frames.at(pos); }

    allocator_type get_allocator() const noexcept { return _M_frames.get_allocator(); }

private:
    vector<value_type, Allocator> _M_frames;
};

// Deduction guides
template <typename Allocator>
basic_stacktrace(const Allocator&) -> basic_stacktrace<Allocator>;

// Typedefs
using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;

//===----------------------------------------------------------------------===//
// to_string: Convert stacktrace to string
//===----------------------------------------------------------------------===//

template <typename Allocator>
string to_string(const basic_stacktrace<Allocator>& st) {
    if (st.empty()) {
        return "";
    }

    string result;
    for (size_t i = 0; i < st.size(); ++i) {
        const auto& entry = st[i];
        string desc = entry.description();
        string file = entry.source_file();
        uint_least32_t line = entry.source_line();

        result += "#";
        result += to_string(i);
        result += " ";

        if (!desc.empty()) {
            result += desc;
        }
        if (!file.empty()) {
            result += " at ";
            result += file;
            if (line != 0) {
                result += ":";
                result += to_string(line);
            }
        }
        if (i < st.size() - 1) {
            result += "\n";
        }
    }
    return result;
}

} // namespace std

#endif // __cplusplus >= 202100L
#endif // _LIBCPP_STACKTRACE
