// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// vector_view.hpp
//
// Definitions of the vector view classes
//
// This file defines the vector view classes of the MKL algebra library.
// View classes provide references to part or all of an existing vector,
// allowing access to data without copying.
//
// Main features:
// - Reference view onto an existing vector
// - Partial view (subvector) of a vector
// - Read-only view
// - Mutable view

#ifndef SANGI_VECTOR_VIEW_HPP
#define SANGI_VECTOR_VIEW_HPP

#include "../common.hpp"

#include <cassert>
#include <cstddef>
#include <iterator>
#include <type_traits>

namespace sangi {
namespace detail {

//-----------------------------------------------------------------------------
// Vector view class
//-----------------------------------------------------------------------------

template<typename VectorType>
class VectorView {
public:
    // Borrow type definitions from the host type
    using value_type = typename std::remove_reference_t<VectorType>::value_type;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using reference = value_type&;
    using const_reference = const value_type&;
    using pointer = value_type*;
    using const_pointer = const value_type*;
    using iterator = value_type*;
    using const_iterator = const value_type*;

    // Reference type to the underlying vector
    using vector_reference = VectorType&;

    // Create a view over the entire vector
    explicit VectorView(vector_reference vector)
        : vector_(vector), offset_(0), size_(vector.size()) {}

    // Create a partial view of the vector
    VectorView(vector_reference vector, size_type offset, size_type size)
        : vector_(vector), offset_(offset), size_(size) {
        // Range check
        if (offset + size > vector.size()) {
            throw IndexError("VectorView: view range exceeds vector bounds");
        }
    }

    // Copy constructor
    VectorView(const VectorView&) = default;

    // Copy assignment operator
    VectorView& operator=(const VectorView&) = delete;  // Copying the view itself is forbidden

    // Assignment that copies the contents of another view
    template<typename OtherVectorType>
    VectorView& operator=(const VectorView<OtherVectorType>& other) {
        assign(other.begin(), other.end());
        return *this;
    }

    // Assignment that copies the contents of a vector
    template<typename OtherVectorType>
    VectorView& operator=(const OtherVectorType& other) {
        assign(other.begin(), other.end());
        return *this;
    }

    // Assignment from an initializer list
    VectorView& operator=(std::initializer_list<value_type> ilist) {
        assign(ilist.begin(), ilist.end());
        return *this;
    }

    // Element access

    // Subscript access (non-const)
    reference operator[](size_type pos) {
        assert(pos < size_ && "VectorView::operator[]: index out of range");
        return vector_[offset_ + pos];
    }

    // Subscript access (const)
    const_reference operator[](size_type pos) const {
        assert(pos < size_ && "VectorView::operator[]: index out of range");
        return vector_[offset_ + pos];
    }

    // Range-checked subscript access (non-const)
    reference at(size_type pos) {
        if (pos >= size_) {
            throw IndexError("VectorView::at: index out of range");
        }
        return vector_[offset_ + pos];
    }

    // Range-checked subscript access (const)
    const_reference at(size_type pos) const {
        if (pos >= size_) {
            throw IndexError("VectorView::at: index out of range");
        }
        return vector_[offset_ + pos];
    }

    // Front element access (non-const)
    reference front() {
        if (empty()) {
            throw IndexError("VectorView::front: empty view");
        }
        return vector_[offset_];
    }

    // Front element access (const)
    const_reference front() const {
        if (empty()) {
            throw IndexError("VectorView::front: empty view");
        }
        return vector_[offset_];
    }

    // Back element access (non-const)
    reference back() {
        if (empty()) {
            throw IndexError("VectorView::back: empty view");
        }
        return vector_[offset_ + size_ - 1];
    }

    // Back element access (const)
    const_reference back() const {
        if (empty()) {
            throw IndexError("VectorView::back: empty view");
        }
        return vector_[offset_ + size_ - 1];
    }

    // Data pointer access (non-const)
    pointer data() noexcept {
        return &vector_[offset_];
    }

    // Data pointer access (const)
    const_pointer data() const noexcept {
        return &vector_[offset_];
    }

    // Iterators

    // Begin iterator (non-const)
    iterator begin() noexcept {
        return &vector_[offset_];
    }

    // Begin iterator (const)
    const_iterator begin() const noexcept {
        return &vector_[offset_];
    }

    // Begin iterator (always const)
    const_iterator cbegin() const noexcept {
        return &vector_[offset_];
    }

    // End iterator (non-const)
    iterator end() noexcept {
        return &vector_[offset_ + size_];
    }

    // End iterator (const)
    const_iterator end() const noexcept {
        return &vector_[offset_ + size_];
    }

    // End iterator (always const)
    const_iterator cend() const noexcept {
        return &vector_[offset_ + size_];
    }

    // Capacity

    // Check whether the view is empty
    bool empty() const noexcept {
        return size_ == 0;
    }

    // Get the size
    size_type size() const noexcept {
        return size_;
    }

    // Size of the underlying vector
    size_type parent_size() const noexcept {
        return vector_.size();
    }

    // Get the offset
    size_type offset() const noexcept {
        return offset_;
    }

    // Create a subview
    VectorView subview(size_type sub_offset, size_type sub_size) const {
        if (sub_offset + sub_size > size_) {
            throw IndexError("VectorView::subview: subview range exceeds view bounds");
        }
        return VectorView(vector_, offset_ + sub_offset, sub_size);
    }

    // Set every element to a scalar value
    void fill(const value_type& value) {
        std::fill(begin(), end(), value);
    }

    // Zero-clear
    void zero() {
        fill(numeric_traits<value_type>::zero());
    }

    // Assign from an iterator range
    template<typename InputIt>
    void assign(InputIt first, InputIt last) {
        const auto count = std::distance(first, last);
        if (count > static_cast<difference_type>(size_)) {
            throw DimensionError("VectorView::assign: source range too large for view");
        }

        std::copy(first, last, begin());
    }

    // Get a reference to the underlying vector
    vector_reference parent() noexcept {
        return vector_;
    }

    const vector_reference parent() const noexcept {
        return vector_;
    }

private:
    vector_reference vector_;  // Reference to the underlying vector
    size_type offset_;         // Offset into the underlying vector
    size_type size_;           // Size of the view
};

//-----------------------------------------------------------------------------
// Read-only vector view class
//-----------------------------------------------------------------------------

template<typename VectorType>
class ConstVectorView {
public:
    // Borrow type definitions from the host type
    using value_type = typename std::remove_reference_t<VectorType>::value_type;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using reference = const value_type&;  // const reference
    using const_reference = const value_type&;
    using pointer = const value_type*;    // const pointer
    using const_pointer = const value_type*;
    using iterator = const value_type*;   // const iterator
    using const_iterator = const value_type*;

    // Reference type to the underlying vector (always const)
    using vector_reference = const VectorType&;

    // Create a view over the entire vector
    explicit ConstVectorView(vector_reference vector)
        : vector_(vector), offset_(0), size_(vector.size()) {}

    // Create a partial view of the vector
    ConstVectorView(vector_reference vector, size_type offset, size_type size)
        : vector_(vector), offset_(offset), size_(size) {
        // Range check
        if (offset + size > vector.size()) {
            throw IndexError("ConstVectorView: view range exceeds vector bounds");
        }
    }

    // Converting constructor from VectorView
    explicit ConstVectorView(const VectorView<VectorType>& view)
        : vector_(view.parent()), offset_(view.offset()), size_(view.size()) {}

    // Copy constructor
    ConstVectorView(const ConstVectorView&) = default;

    // Copy assignment operator
    ConstVectorView& operator=(const ConstVectorView&) = delete;  // Copying the view itself is forbidden

    // Element access

    // Subscript access (always const)
    const_reference operator[](size_type pos) const {
        assert(pos < size_ && "ConstVectorView::operator[]: index out of range");
        return vector_[offset_ + pos];
    }

    // Range-checked subscript access (always const)
    const_reference at(size_type pos) const {
        if (pos >= size_) {
            throw IndexError("ConstVectorView::at: index out of range");
        }
        return vector_[offset_ + pos];
    }

    // Front element access (always const)
    const_reference front() const {
        if (empty()) {
            throw IndexError("ConstVectorView::front: empty view");
        }
        return vector_[offset_];
    }

    // Back element access (always const)
    const_reference back() const {
        if (empty()) {
            throw IndexError("ConstVectorView::back: empty view");
        }
        return vector_[offset_ + size_ - 1];
    }

    // Data pointer access (always const)
    const_pointer data() const noexcept {
        return &vector_[offset_];
    }

    // Iterators (all const)

    // Begin iterator
    const_iterator begin() const noexcept {
        return &vector_[offset_];
    }

    const_iterator cbegin() const noexcept {
        return &vector_[offset_];
    }

    // End iterator
    const_iterator end() const noexcept {
        return &vector_[offset_ + size_];
    }

    const_iterator cend() const noexcept {
        return &vector_[offset_ + size_];
    }

    // Capacity

    // Check whether the view is empty
    bool empty() const noexcept {
        return size_ == 0;
    }

    // Get the size
    size_type size() const noexcept {
        return size_;
    }

    // Size of the underlying vector
    size_type parent_size() const noexcept {
        return vector_.size();
    }

    // Get the offset
    size_type offset() const noexcept {
        return offset_;
    }

    // Create a subview
    ConstVectorView subview(size_type sub_offset, size_type sub_size) const {
        if (sub_offset + sub_size > size_) {
            throw IndexError("ConstVectorView::subview: subview range exceeds view bounds");
        }
        return ConstVectorView(vector_, offset_ + sub_offset, sub_size);
    }

    // Get a reference to the underlying vector
    vector_reference parent() const noexcept {
        return vector_;
    }

private:
    vector_reference vector_;  // Reference to the underlying vector (const)
    size_type offset_;         // Offset into the underlying vector
    size_type size_;           // Size of the view
};

// Helper functions to create views

// Create a view over the entire vector
template<typename VectorType>
VectorView<VectorType> make_vector_view(VectorType& vector) {
    return VectorView<VectorType>(vector);
}

// Create a partial view of the vector
template<typename VectorType>
VectorView<VectorType> make_vector_view(VectorType& vector,
                                       std::size_t offset,
                                       std::size_t size) {
    return VectorView<VectorType>(vector, offset, size);
}

// Create a read-only view over the entire vector
template<typename VectorType>
ConstVectorView<VectorType> make_const_vector_view(const VectorType& vector) {
    return ConstVectorView<VectorType>(vector);
}

// Create a partial read-only view of the vector
template<typename VectorType>
ConstVectorView<VectorType> make_const_vector_view(const VectorType& vector,
                                                  std::size_t offset,
                                                  std::size_t size) {
    return ConstVectorView<VectorType>(vector, offset, size);
}

} // namespace detail
} // namespace sangi

#endif // SANGI_VECTOR_VIEW_HPP
