// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// BaseVector.hpp
//
// Common base class for vectors. A direct translation of the sangi
// Vector<T> + Vector_DIM<T, N> pattern. Designed to mirror BaseMatrix<T>.
//
// Design:
//   - No virtual functions (operator[], size, data are inline; SIMD is unimpeded).
//   - shared_storage_ may own dynamic storage (for the dynamic Vector subclass).
//     For the fixed subclass (StaticVector) and non-owning views, shared_storage_ is
//     nullptr and data_ points to external storage or a stack array.
//   - Holds stride_, allowing strided views (row/column views, etc.).
//   - The derived classes Vector / StaticVector configure data_, size_, stride_ in
//     their constructor via set_view() / set_view_nonowning().

#ifndef SANGI_BASE_VECTOR_HPP
#define SANGI_BASE_VECTOR_HPP

#include <cstddef>
#include <concepts>
#include <memory>
#include <type_traits>

namespace sangi {

template<typename T>
class BaseVector {
public:
    // Compile-time size trait.
    // -1 = dynamic (size known only at runtime). Derived classes with statically
    // known size (StaticVector) shadow this with their template parameter.
    // Used by LinAlg algorithms to enable static_assert dimension checks.
    static constexpr std::ptrdiff_t static_size = -1;

protected:
    // Ownership mode:
    //   shared_storage_ != nullptr: BaseVector owns dynamic storage (via shared)
    //   shared_storage_ == nullptr: data_ points to external or subclass-owned storage (non-owning view)
    std::shared_ptr<T[]> shared_storage_;

    // Pointer for element access (always valid, used regardless of shared_storage_)
    T*          data_;

    // Shape
    std::size_t size_;

    // stride (element address = data_[i * stride_])
    //   contiguous: stride_ = 1
    //   For column views of a matrix (BaseMatrix::col_view), stride_ = row stride
    std::size_t stride_;

public:
    using value_type = T;
    using size_type = std::size_t;
    using reference = T&;
    using const_reference = const T&;

    // Empty construction (configure the view later by assignment; default = empty view)
    BaseVector() noexcept
        : shared_storage_(), data_(nullptr), size_(0), stride_(0) {}

    // === public: ctor for non-owning views ===
    // Build a view that references external data directly
    BaseVector(T* data, std::size_t size, std::size_t stride = 1) noexcept
        : shared_storage_(), data_(data), size_(size), stride_(stride) {}

    // Implicit conversion: implicit construction (as a non-owning view) from a contiguous vector type (Vector, StaticVector, etc.)
    template<typename V>
        requires std::is_same_v<typename V::value_type, T>
              && (!std::is_same_v<std::remove_cvref_t<V>, BaseVector<T>>)
              && requires(const V& v) {
                     { v.data() } -> std::convertible_to<const T*>;
                     { v.size() } -> std::convertible_to<std::size_t>;
                 }
    BaseVector(const V& v) noexcept
        : shared_storage_()
        , data_(const_cast<T*>(v.data()))
        , size_(v.size())
        , stride_(1) {}

protected:
    // Derived-class only: allocate and own dynamic storage
    BaseVector(std::size_t size)
        : shared_storage_(size > 0 ? new T[size]() : nullptr)
        , data_(shared_storage_.get())
        , size_(size), stride_(1) {}

    // Derived-class only: view-configuration setter (so Vector can synchronize its storage with the base)
    void set_view(std::shared_ptr<T[]> storage, T* data,
                  std::size_t size, std::size_t stride = 1) noexcept {
        shared_storage_ = std::move(storage);
        data_ = data;
        size_ = size;
        stride_ = stride;
    }

    // Derived-class only: lightweight sync that updates only the data pointer (no shared_storage)
    //   (used by StaticVector to pass the address of its std::array to the base)
    void set_view_nonowning(T* data, std::size_t size, std::size_t stride = 1) noexcept {
        shared_storage_.reset();
        data_ = data;
        size_ = size;
        stride_ = stride;
    }

public:
    // === access ===
    [[nodiscard]] size_type size()   const noexcept { return size_; }
    [[nodiscard]] size_type stride() const noexcept { return stride_; }
    [[nodiscard]] bool      empty()  const noexcept { return size_ == 0; }

    [[nodiscard]] const T& operator[](size_type i) const noexcept {
        return data_[i * stride_];
    }
    [[nodiscard]] T& operator[](size_type i) noexcept {
        return data_[i * stride_];
    }

    [[nodiscard]] const T* data() const noexcept { return data_; }
    [[nodiscard]] T*       data()       noexcept { return data_; }

    // === Contiguity test (for the fast path) ===
    [[nodiscard]] bool is_contiguous() const noexcept {
        return stride_ == 1;
    }

    // === Storage-ownership query (for debugging) ===
    [[nodiscard]] bool owns_storage() const noexcept {
        return static_cast<bool>(shared_storage_);
    }
};

} // namespace sangi

#endif // SANGI_BASE_VECTOR_HPP
