// Copyright (c) 2014
// INRIA Saclay-Ile de France (France)
//
// This file is part of CGAL (www.cgal.org)
//
// $URL: https://github.com/CGAL/cgal/blob/v6.1.1/NewKernel_d/include/CGAL/NewKernel_d/Vector/array.h $
// $Id: include/CGAL/NewKernel_d/Vector/array.h 08b27d3db14 $
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s)     : Marc Glisse

#ifndef CGAL_VECTOR_ARRAY_H
#define CGAL_VECTOR_ARRAY_H
#include <boost/type_traits/is_arithmetic.hpp>
#include <CGAL/Dimension.h>
#include <CGAL/NewKernel_d/utils.h>
#include <CGAL/array.h>
#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/repetition/enum.hpp>

#include <CGAL/NewKernel_d/Vector/determinant_of_points_from_vectors.h>
#include <CGAL/NewKernel_d/Vector/determinant_of_vectors_small_dim.h>
#include <CGAL/NewKernel_d/Vector/determinant_of_iterator_to_points_from_iterator_to_vectors.h>
#include <CGAL/NewKernel_d/Vector/determinant_of_iterator_to_points_from_points.h>
#include <CGAL/NewKernel_d/Vector/determinant_of_iterator_to_vectors_from_vectors.h>



namespace CGAL {

// May not be safe to use with dim!=max_dim.
// In that case, we should store the real dim next to the array.
template<class NT_,class Dim_,class Max_dim_=Dim_> struct Array_vector {
        typedef NT_ NT;
        typedef Dim_ Dimension;
        typedef Max_dim_ Max_dimension;
        template< class D2, class D3=D2 >
        struct Rebind_dimension {
          typedef Array_vector< NT, D2, D3 > Other;
        };
        template<class> struct Property : std::false_type {};

        static const unsigned d_=Max_dim_::value;
        static_assert(d_ != (unsigned)UNKNOWN_DIMENSION);

        typedef std::array<NT,d_> Vector;
        struct Construct_vector {
                struct Dimension {
                        // Initialize with NaN if possible?
                  Vector operator()(unsigned CGAL_assertion_code(d)) const {
                                CGAL_assertion(d<=d_);
                                return Vector();
                        }
                };

                struct Iterator {
                        template<typename Iter>
                                Vector operator()(unsigned CGAL_assertion_code(d),Iter const& f,Iter const& e) const {
                                        CGAL_assertion(d==(unsigned) std::distance(f,e));
                                        CGAL_assertion(d<=d_);
                                        //TODO: optimize for forward iterators
                                        Vector a;
                                        CGAL_assume(f!=e);
                                        std::copy(f,e,a.begin());
                                        return a;
                                }
                };

#if 0
                struct Iterator_add_one {
                        template<typename Iter>
                                Vector operator()(unsigned d,Iter const& f,Iter const& e) const {
                                        CGAL_assertion(d==std::distance(f,e)+1);
                                        CGAL_assertion(d<=d_);
                                        //TODO: optimize
                                        Vector a;
                                        std::copy(f,e,a.begin());
                                        a.back()=1;
                                        return a;
                                }
                };
#endif

                struct Iterator_and_last {
                        template<typename Iter,typename T>
                        Vector operator()(unsigned  CGAL_assertion_code(d),Iter const& f,Iter const& e,T&& t) const {
                                        CGAL_assertion(d==std::distance(f,e)+1);
                                        CGAL_assertion(d<=d_);
                                        //TODO: optimize for forward iterators
                                        Vector a;
                                        std::copy(f,e,a.begin());
                                        a.back()=std::forward<T>(t);
                                        return a;
                                }
                };

                struct Values {
                        template<class...U>
                                Vector operator()(U&&...u) const {
                                        static_assert(sizeof...(U)<=d_,"too many arguments");
                                        Vector a={{forward_safe<NT,U>(u)...}};
                                        return a;
                                }
                };

                struct Values_divide {
                        template<class H,class...U>
                                Vector operator()(H const& h,U&&...u) const {
                                        static_assert(sizeof...(U)<=d_,"too many arguments");
                                        Vector a={{Rational_traits<NT>().make_rational(std::forward<U>(u),h)...}};
                                        return a;
                                }
                };
        };

        typedef NT const* Vector_const_iterator;
        static Vector_const_iterator vector_begin(Vector const&a){
                return &a[0];
        }
        static Vector_const_iterator vector_end(Vector const&a){
                return &a[0]+d_; // Don't know the real size
        }
        static unsigned size_of_vector(Vector const&){
                return d_; // Don't know the real size
        }

};
// Do not try to instantiate the above
template<class NT_,class Max_dim_> struct Array_vector<NT_,Dynamic_dimension_tag,Max_dim_> {};

}
#endif
