#ifndef SRC_DATA_STRUCTURES_GLOBALFRICTIONCONTAINER_HH
#define SRC_DATA_STRUCTURES_GLOBALFRICTIONCONTAINER_HH

#include <vector>
#include <cstddef>
#include <list>
#include <cassert>
#include <memory>

template <class BaseGlobalFriction, size_t depth>
class GlobalFrictionContainer {
public:
    using IndexObject = GlobalFrictionContainer<BaseGlobalFriction, depth-1>;
    using GlobalFriction = std::vector<IndexObject>;

    GlobalFrictionContainer() {}

    auto operator[](std::size_t i) -> IndexObject& {
        return globalFriction_[i];
    }

    auto operator[](std::size_t i) const -> const IndexObject& {
        return globalFriction_[i];
    }

    auto size() const -> size_t {
        return globalFriction_.size();
    }

    void resize(std::list<size_t> list) {
        assert(list.size() <= depth);

        if (list.size() == 0) {
            globalFriction_.resize(0);
        } else {
            globalFriction_.resize(list.front());
            list.pop_front();

            for (size_t i=0; i<size(); i++) {
                globalFriction_[i].resize(list);
            }
        }
    }

    template <class VectorContainer>
    void updateAlpha(const VectorContainer& newAlpha) {
        assert(newAlpha.size() == size());

        for (size_t i=0; i<size(); i++) {
                globalFriction_[i].updateAlpha(newAlpha[i]);
        }
    }

    auto globalFriction() -> GlobalFriction& {
        return globalFriction_;
    }

    auto globalFriction() const -> const GlobalFriction& {
        return globalFriction_;
    }

private:
    GlobalFriction globalFriction_;
};


template <class BaseGlobalFriction>
class GlobalFrictionContainer<BaseGlobalFriction, 1> {
public:
    using IndexObject = std::shared_ptr<BaseGlobalFriction>;
    using GlobalFriction = std::vector<IndexObject>;

    GlobalFrictionContainer() {}

    auto operator[](std::size_t i) -> IndexObject& {
        return globalFriction_[i];
    }

    auto operator[](std::size_t i) const -> const IndexObject& {
        return globalFriction_[i];
    }

    auto size() const -> size_t {
        return globalFriction_.size();
    }

    void resize(std::list<size_t> newSize) {
        if (newSize.size() > 0) {
            globalFriction_.resize(newSize.front(), nullptr);
        } else {
            globalFriction_.resize(0);
        }
    }

    template <class Vector>
    void updateAlpha(const Vector& newAlpha) {
        for (size_t i=0; i<size(); i++) {
                globalFriction_[i]->updateAlpha(newAlpha);
        }
    }

    auto globalFriction() -> GlobalFriction& {
        return globalFriction_;
    }

    auto globalFriction() const -> const GlobalFriction& {
        return globalFriction_;
    }

private:
    GlobalFriction globalFriction_;
};

#endif