#pragma once

#include "fesa/analysis/analysis_model.hpp"
#include "fesa/math/vector.hpp"

#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>

namespace fesa {

enum class DofComponent : std::uint8_t {
    ux,
    uy,
    uz,
    urx,
    ury,
    urz
};

struct SparsePattern {
    std::vector<std::size_t> rowOffsets;
    std::vector<std::size_t> columnIndices;
};

// Owns every equation-space mapping so semantic model objects remain free of
// analysis-specific equation IDs.
class DofManager {
public:
    static Result<DofManager> create(const AnalysisModel& model);

    std::size_t fullDofCount() const noexcept;
    std::size_t freeDofCount() const noexcept;
    std::size_t constrainedDofCount() const noexcept;
    std::size_t fullDof(EntityIndex node, DofComponent component) const;
    std::optional<std::size_t> freeEquation(std::size_t fullDof) const;
    const std::array<std::size_t, 12>& elementScatter(
        EntityIndex element) const;
    const std::array<std::size_t, 24>& shellElementScatter(
        EntityIndex element) const;
    const std::vector<std::size_t>& freeDofs() const noexcept;
    const std::vector<std::size_t>& constrainedDofs() const noexcept;
    const Vector& prescribedValues() const noexcept;
    const SparsePattern& sparsePattern() const noexcept;

private:
    DofManager(
        std::size_t fullDofCount,
        std::vector<std::optional<std::size_t>> freeEquations,
        std::vector<std::array<std::size_t, 12>> elementScatters,
        std::vector<std::array<std::size_t, 24>> shellElementScatters,
        std::vector<std::size_t> freeDofs,
        std::vector<std::size_t> constrainedDofs,
        Vector prescribedValues,
        SparsePattern sparsePattern);

    std::size_t fullDofCount_;
    std::vector<std::optional<std::size_t>> freeEquations_;
    std::vector<std::array<std::size_t, 12>> elementScatters_;
    std::vector<std::array<std::size_t, 24>> shellElementScatters_;
    std::vector<std::size_t> freeDofs_;
    std::vector<std::size_t> constrainedDofs_;
    Vector prescribedValues_;
    SparsePattern sparsePattern_;
};

} // namespace fesa
