#pragma once
// azaraC - src/internal/avr_std/optional
// Minimal std::optional shim for the Arduino AVR toolchain (no libstdc++).
//
// Supports only what azaraC uses:
//   - default / nullopt / value construction (incl. implicit from the
//     return value of lookup functions)
//   - copy / move construction and assignment
//   - has_value(), operator bool, operator*, operator->, value(), emplace,
//     reset(), operator=(nullopt_t)
//
// No exceptions are used (AVR has no runtime); access is unchecked.

#include <stddef.h>
#include "new"

#if defined(AZARAC_AVR_STUB)
#include <optional>
#else
namespace std {

struct nullopt_t {
    explicit constexpr nullopt_t(int) noexcept {}
};
inline constexpr nullopt_t nullopt{0};

template <typename T>
class optional {
public:
    using value_type = T;

    constexpr optional() noexcept : has_(false), storage_{} {}
    constexpr optional(nullopt_t) noexcept : has_(false), storage_{} {}
    optional(const T& v) { construct(v); }
    optional(T&& v) { construct(static_cast<T&&>(v)); }

    optional(const optional& other) : has_(false) {
        if (other.has_) construct(other.value());
    }
    optional(optional&& other) noexcept : has_(false) {
        if (other.has_) construct(static_cast<T&&>(other.value()));
    }

    ~optional() { reset(); }

    optional& operator=(const optional& other) {
        if (this != &other) {
            if (other.has_) emplace(other.value());
            else reset();
        }
        return *this;
    }
    optional& operator=(optional&& other) noexcept {
        if (this != &other) {
            if (other.has_) emplace(static_cast<T&&>(other.value()));
            else reset();
        }
        return *this;
    }
    optional& operator=(nullopt_t) noexcept {
        reset();
        return *this;
    }

    constexpr bool has_value() const noexcept { return has_; }
    constexpr explicit operator bool() const noexcept { return has_; }

    T& value() { return *reinterpret_cast<T*>(storage_); }
    const T& value() const { return *reinterpret_cast<const T*>(storage_); }

    template <typename U>
    T value_or(U&& default_value) const {
        return has_ ? value() : static_cast<T>(static_cast<U&&>(default_value));
    }

    T& operator*() { return value(); }
    const T& operator*() const { return value(); }
    T* operator->() { return &value(); }
    const T* operator->() const { return &value(); }

    template <typename... Args>
    T& emplace(Args&&... args) {
        reset();
        new (static_cast<void*>(storage_)) T(static_cast<Args&&>(args)...);
        has_ = true;
        return value();
    }

    void reset() {
        if (has_) {
            value().~T();
            has_ = false;
        }
    }

private:
    template <typename U>
    void construct(U&& u) {
        new (static_cast<void*>(storage_)) T(static_cast<U&&>(u));
        has_ = true;
    }

    bool has_;
    alignas(T) unsigned char storage_[sizeof(T)];
};

}  // namespace std
#endif
