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

// vector_storage.hpp
//
// Definitions of vector storage classes
//
// This file defines the different storage strategies used by the vector
// classes of the MKL algebra library. This enables efficient memory management
// for both dynamic and fixed-size vectors.
//
// Main features:
// - Dynamic-size vector storage (based on std::vector)
// - Fixed-size vector storage (stack-allocated)
// - Optimized small-size vector storage
// - Alignment control

#ifndef SANGI_VECTOR_STORAGE_HPP
#define SANGI_VECTOR_STORAGE_HPP

#include "../common.hpp"

#include <algorithm>
#include <array>
#include <cstddef>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <vector>

namespace sangi {
namespace detail {

//-----------------------------------------------------------------------------
// Dynamic-size vector storage (std::vector based)
//-----------------------------------------------------------------------------

template<typename T>
class DynamicVectorStorage {
public:
    using value_type = T;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using reference = T&;
    using const_reference = const T&;
    using pointer = T*;
    using const_pointer = const T*;
    using iterator = typename std::vector<T>::iterator;
    using const_iterator = typename std::vector<T>::const_iterator;
    
    // Default constructor
    DynamicVectorStorage() = default;
    
    // Constructor with size
    explicit DynamicVectorStorage(size_type size) : data_(size) {}
    
    // Constructor with size and initial value
    DynamicVectorStorage(size_type size, const T& value) : data_(size, value) {}
    
    // Iterator-range constructor
    template<typename InputIt>
    DynamicVectorStorage(InputIt first, InputIt last) : data_(first, last) {}
    
    // Initializer-list constructor
    DynamicVectorStorage(std::initializer_list<T> init) : data_(init) {}
    
    // Copy/move constructors and copy/move assignment operators
    DynamicVectorStorage(const DynamicVectorStorage&) = default;
    DynamicVectorStorage(DynamicVectorStorage&&) noexcept = default;
    DynamicVectorStorage& operator=(const DynamicVectorStorage&) = default;
    DynamicVectorStorage& operator=(DynamicVectorStorage&&) noexcept = default;
    
    // Destructor
    ~DynamicVectorStorage() = default;
    
    // Element access
    reference operator[](size_type pos) {
        return data_[pos];
    }
    
    const_reference operator[](size_type pos) const {
        return data_[pos];
    }
    
    reference at(size_type pos) {
        return data_.at(pos);
    }
    
    const_reference at(size_type pos) const {
        return data_.at(pos);
    }
    
    reference front() {
        return data_.front();
    }
    
    const_reference front() const {
        return data_.front();
    }
    
    reference back() {
        return data_.back();
    }
    
    const_reference back() const {
        return data_.back();
    }
    
    // Data access
    T* data() noexcept {
        return data_.data();
    }
    
    const T* data() const noexcept {
        return data_.data();
    }
    
    // Iterators
    iterator begin() noexcept {
        return data_.begin();
    }
    
    const_iterator begin() const noexcept {
        return data_.begin();
    }
    
    const_iterator cbegin() const noexcept {
        return data_.cbegin();
    }
    
    iterator end() noexcept {
        return data_.end();
    }
    
    const_iterator end() const noexcept {
        return data_.end();
    }
    
    const_iterator cend() const noexcept {
        return data_.cend();
    }
    
    // Capacity
    bool empty() const noexcept {
        return data_.empty();
    }
    
    size_type size() const noexcept {
        return data_.size();
    }
    
    size_type max_size() const noexcept {
        return data_.max_size();
    }
    
    void reserve(size_type new_cap) {
        data_.reserve(new_cap);
    }
    
    size_type capacity() const noexcept {
        return data_.capacity();
    }
    
    void shrink_to_fit() {
        data_.shrink_to_fit();
    }
    
    // Modifiers
    void clear() noexcept {
        data_.clear();
    }
    
    void resize(size_type count) {
        data_.resize(count);
    }
    
    void resize(size_type count, const value_type& value) {
        data_.resize(count, value);
    }
    
    void push_back(const T& value) {
        data_.push_back(value);
    }
    
    void push_back(T&& value) {
        data_.push_back(std::move(value));
    }
    
    void pop_back() {
        data_.pop_back();
    }
    
private:
    std::vector<T> data_;
};

//-----------------------------------------------------------------------------
// Fixed-size vector storage (stack-allocated)
//-----------------------------------------------------------------------------

template<typename T, std::size_t N>
class StaticVectorStorage {
public:
    using value_type = T;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using reference = T&;
    using const_reference = const T&;
    using pointer = T*;
    using const_pointer = const T*;
    using iterator = T*;
    using const_iterator = const T*;
    
    // Default constructor (zero-initialized)
    StaticVectorStorage() : data_{} {}
    
    // Constructor initialized with a value
    explicit StaticVectorStorage(const T& value) {
        std::fill(begin(), end(), value);
    }
    
    // Copy/move constructors and copy/move assignment operators
    StaticVectorStorage(const StaticVectorStorage&) = default;
    StaticVectorStorage(StaticVectorStorage&&) noexcept = default;
    StaticVectorStorage& operator=(const StaticVectorStorage&) = default;
    StaticVectorStorage& operator=(StaticVectorStorage&&) noexcept = default;
    
    // Destructor
    ~StaticVectorStorage() = default;
    
    // Element access
    reference operator[](size_type pos) {
        return data_[pos];
    }
    
    const_reference operator[](size_type pos) const {
        return data_[pos];
    }
    
    reference at(size_type pos) {
        if (pos >= N) {
            throw IndexError("StaticVectorStorage::at: index out of range");
        }
        return data_[pos];
    }
    
    const_reference at(size_type pos) const {
        if (pos >= N) {
            throw IndexError("StaticVectorStorage::at: index out of range");
        }
        return data_[pos];
    }
    
    reference front() {
        return data_[0];
    }
    
    const_reference front() const {
        return data_[0];
    }
    
    reference back() {
        return data_[N - 1];
    }
    
    const_reference back() const {
        return data_[N - 1];
    }
    
    // Data access
    T* data() noexcept {
        return data_;
    }
    
    const T* data() const noexcept {
        return data_;
    }
    
    // Iterators
    iterator begin() noexcept {
        return data_;
    }
    
    const_iterator begin() const noexcept {
        return data_;
    }
    
    const_iterator cbegin() const noexcept {
        return data_;
    }
    
    iterator end() noexcept {
        return data_ + N;
    }
    
    const_iterator end() const noexcept {
        return data_ + N;
    }
    
    const_iterator cend() const noexcept {
        return data_ + N;
    }
    
    // Capacity
    constexpr bool empty() const noexcept {
        return N == 0;
    }
    
    constexpr size_type size() const noexcept {
        return N;
    }
    
    constexpr size_type max_size() const noexcept {
        return N;
    }
    
    constexpr size_type capacity() const noexcept {
        return N;
    }
    
private:
    T data_[N];
};

//-----------------------------------------------------------------------------
// Aligned dynamic vector storage (for MKL optimization)
//-----------------------------------------------------------------------------

template<typename T>
class AlignedVectorStorage {
public:
    using value_type = T;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using reference = T&;
    using const_reference = const T&;
    using pointer = T*;
    using const_pointer = const T*;
    using iterator = T*;
    using const_iterator = const T*;
    
    // Alignment value
    static constexpr size_type alignment = memory::simd_alignment();
    
    // Default constructor
    AlignedVectorStorage() : data_(nullptr), size_(0), capacity_(0) {}
    
    // Constructor with size
    explicit AlignedVectorStorage(size_type size) : size_(size), capacity_(size) {
        if (size_ > 0) {
            data_ = memory::aligned_alloc<T>(size_, alignment);
            std::uninitialized_default_construct_n(data_, size_);
        } else {
            data_ = nullptr;
        }
    }
    
    // Constructor with size and initial value
    AlignedVectorStorage(size_type size, const T& value) : size_(size), capacity_(size) {
        if (size_ > 0) {
            data_ = memory::aligned_alloc<T>(size_, alignment);
            std::uninitialized_fill_n(data_, size_, value);
        } else {
            data_ = nullptr;
        }
    }
    
    // Iterator-range constructor
    template<typename InputIt>
    AlignedVectorStorage(InputIt first, InputIt last) {
        const auto count = std::distance(first, last);
        size_ = count;
        capacity_ = count;
        
        if (size_ > 0) {
            data_ = memory::aligned_alloc<T>(size_, alignment);
            std::uninitialized_copy(first, last, data_);
        } else {
            data_ = nullptr;
        }
    }
    
    // Initializer-list constructor
    AlignedVectorStorage(std::initializer_list<T> init) 
        : AlignedVectorStorage(init.begin(), init.end()) {}
    
    // Copy constructor
    AlignedVectorStorage(const AlignedVectorStorage& other) 
        : size_(other.size_), capacity_(other.size_) {
        if (size_ > 0) {
            data_ = memory::aligned_alloc<T>(size_, alignment);
            std::uninitialized_copy_n(other.data_, size_, data_);
        } else {
            data_ = nullptr;
        }
    }
    
    // Move constructor
    AlignedVectorStorage(AlignedVectorStorage&& other) noexcept 
        : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
        other.data_ = nullptr;
        other.size_ = 0;
        other.capacity_ = 0;
    }
    
    // Copy assignment operator
    AlignedVectorStorage& operator=(const AlignedVectorStorage& other) {
        if (this != &other) {
            // Destroy old data
            clear();
            if (data_) {
                memory::aligned_free(data_);
            }
            
            // Allocate and copy new data
            size_ = other.size_;
            capacity_ = other.size_;
            
            if (size_ > 0) {
                data_ = memory::aligned_alloc<T>(size_, alignment);
                std::uninitialized_copy_n(other.data_, size_, data_);
            } else {
                data_ = nullptr;
            }
        }
        return *this;
    }
    
    // Move assignment operator
    AlignedVectorStorage& operator=(AlignedVectorStorage&& other) noexcept {
        if (this != &other) {
            // Destroy old data
            clear();
            if (data_) {
                memory::aligned_free(data_);
            }
            
            // Move data from the other
            data_ = other.data_;
            size_ = other.size_;
            capacity_ = other.capacity_;
            
            other.data_ = nullptr;
            other.size_ = 0;
            other.capacity_ = 0;
        }
        return *this;
    }
    
    // Destructor
    ~AlignedVectorStorage() {
        clear();
        if (data_) {
            memory::aligned_free(data_);
        }
    }
    
    // Element access
    reference operator[](size_type pos) {
        return data_[pos];
    }
    
    const_reference operator[](size_type pos) const {
        return data_[pos];
    }
    
    reference at(size_type pos) {
        if (pos >= size_) {
            throw IndexError("AlignedVectorStorage::at: index out of range");
        }
        return data_[pos];
    }
    
    const_reference at(size_type pos) const {
        if (pos >= size_) {
            throw IndexError("AlignedVectorStorage::at: index out of range");
        }
        return data_[pos];
    }
    
    reference front() {
        if (empty()) {
            throw IndexError("AlignedVectorStorage::front: empty container");
        }
        return data_[0];
    }
    
    const_reference front() const {
        if (empty()) {
            throw IndexError("AlignedVectorStorage::front: empty container");
        }
        return data_[0];
    }
    
    reference back() {
        if (empty()) {
            throw IndexError("AlignedVectorStorage::back: empty container");
        }
        return data_[size_ - 1];
    }
    
    const_reference back() const {
        if (empty()) {
            throw IndexError("AlignedVectorStorage::back: empty container");
        }
        return data_[size_ - 1];
    }
    
    // Data access
    T* data() noexcept {
        return data_;
    }
    
    const T* data() const noexcept {
        return data_;
    }
    
    // Iterators
    iterator begin() noexcept {
        return data_;
    }
    
    const_iterator begin() const noexcept {
        return data_;
    }
    
    const_iterator cbegin() const noexcept {
        return data_;
    }
    
    iterator end() noexcept {
        return data_ + size_;
    }
    
    const_iterator end() const noexcept {
        return data_ + size_;
    }
    
    const_iterator cend() const noexcept {
        return data_ + size_;
    }
    
    // Capacity
    bool empty() const noexcept {
        return size_ == 0;
    }
    
    size_type size() const noexcept {
        return size_;
    }
    
    size_type capacity() const noexcept {
        return capacity_;
    }
    
    void reserve(size_type new_cap) {
        if (new_cap <= capacity_) {
            return;
        }
        
        // Allocate new memory
        T* new_data = memory::aligned_alloc<T>(new_cap, alignment);
        
        // Move old data
        if (size_ > 0) {
            std::uninitialized_move_n(data_, size_, new_data);
            // Destroy the old objects
            std::destroy_n(data_, size_);
        }
        
        // Free the old memory
        if (data_) {
            memory::aligned_free(data_);
        }
        
        // Install the new memory
        data_ = new_data;
        capacity_ = new_cap;
    }
    
    // Modifiers
    void clear() noexcept {
        if (data_) {
            std::destroy_n(data_, size_);
        }
        size_ = 0;
    }
    
    void resize(size_type count) {
        if (count > size_) {
            // Grow
            reserve(count);
            // Initialize new elements
            std::uninitialized_default_construct_n(data_ + size_, count - size_);
        } else if (count < size_) {
            // Shrink
            std::destroy_n(data_ + count, size_ - count);
        }
        size_ = count;
    }
    
    void resize(size_type count, const value_type& value) {
        if (count > size_) {
            // Grow
            reserve(count);
            // Initialize new elements
            std::uninitialized_fill_n(data_ + size_, count - size_, value);
        } else if (count < size_) {
            // Shrink
            std::destroy_n(data_ + count, size_ - count);
        }
        size_ = count;
    }
    
    void push_back(const T& value) {
        if (size_ == capacity_) {
            // Grow the capacity
            const size_type new_capacity = capacity_ == 0 ? 1 : capacity_ * 2;
            reserve(new_capacity);
        }
        
        // Add new element
        new (data_ + size_) T(value);
        ++size_;
    }
    
    void push_back(T&& value) {
        if (size_ == capacity_) {
            // Grow the capacity
            const size_type new_capacity = capacity_ == 0 ? 1 : capacity_ * 2;
            reserve(new_capacity);
        }
        
        // Add new element
        new (data_ + size_) T(std::move(value));
        ++size_;
    }
    
    void pop_back() {
        if (empty()) {
            throw IndexError("AlignedVectorStorage::pop_back: empty container");
        }
        
        --size_;
        std::destroy_at(data_ + size_);
    }
    
private:
    T* data_;
    size_type size_;
    size_type capacity_;
};

} // namespace detail
} // namespace sangi

#endif // SANGI_VECTOR_STORAGE_HPP