// Description: Dimension of a point
// DocumentationOf: point_concept.h
#ifndef PASTELSYS_POINT_DIMENSION_H
#define PASTELSYS_POINT_DIMENSION_H
#include "pastel/sys/point/point_concept.h"
#include "pastel/sys/real/real_ring_concept.h"
#include <array>
namespace Pastel
{
    // Dimension by having member .pointDimension().
    template <typename T>
    concept Point_HasMemberDimension__ = requires(T t) {
        {t.pointDimension()} -> std::convertible_to<integer>;
    };
    template <typename T>
    concept Point_HasMemberDimension_ = 
        Point_HasMemberDimension__<RemoveCvRef<T>>;
    template <typename Type>
    using Point_HasMemberDimension = 
        std::bool_constant<Point_HasMemberDimension_<Type>>;
    template <Point_HasMemberDimension_ Point>
    decltype(auto) dimension(Point&& point)
    {
        return std::forward<Point>(point).pointDimension();
    }
}
namespace Pastel
{
    // Dimension by having member .size().
    template <typename T>
    concept Point_HasMemberSize__ = requires(T t) {
        {addConst(t).size()} -> std::convertible_to<integer>;
    };
    template <typename T>
    concept Point_HasMemberSize_ = 
        Point_HasMemberSize__<RemoveCvRef<T>>;
    template <typename Type>
    using Point_HasMemberSize = 
        std::bool_constant<Point_HasMemberSize_<Type>>;
    template <Point_HasMemberSize_ Point>
    requires (!Point_HasMemberDimension<Point>::value)
    integer dimension(const Point& point)
    {
        return point.size();
    }
}
namespace Pastel
{
    // Dimension by being a number.
    template <Real_Ring_Concept Point>
    requires
        (!Point_HasMemberSize<Point>::value &&
        !Point_HasMemberDimension<Point>::value)
    integer dimension(const Point& point)
    {
        return 1;
    }
}
namespace Pastel
{
    template <Real_Ring_Concept Point>
    IntegerConstant<1> pointN(const Point* point);
    template <typename Real, std::size_t N>
    IntegerConstant<N> pointN(const std::array<Real, N>* point);
    namespace Point_
    {
        template <
            typename Type,
            typename Result = decltype(pointN((const Type*)nullptr))>
        struct Point_N_ 
        {
            using type = Result;
        };
    }
    template <typename Point>
    using Point_N = typename Compute<IntegerConstant<Dynamic>, Point_::Point_N_, Point>::type;
}
namespace Pastel
{
    template <typename Point>
    using Point_Dimension = 
        Point_N<Point>;
}
#endif