///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef GSL_SPAN_EXT_H
#define GSL_SPAN_EXT_H

///////////////////////////////////////////////////////////////////////////////
//
// File: span_ext
// Purpose: continue offering features that have been cut from the official
//   implementation of span.
//   While modernizing gsl::span a number of features needed to be removed to
//   be compliant with the design of std::span
//
///////////////////////////////////////////////////////////////////////////////

#include "gsl_assert.h" // GSL_KERNEL_MODE
#include "gsl_util.h"   // for narrow_cast, narrow

#include <cstddef> // for ptrdiff_t, size_t
#include <utility>

#ifndef GSL_KERNEL_MODE
#include <algorithm> // for lexicographical_compare
#endif               // GSL_KERNEL_MODE

namespace gsl
{

// [span.views.constants], constants
constexpr const std::size_t dynamic_extent = narrow_cast<std::size_t>(-1);

template <class ElementType, std::size_t Extent = dynamic_extent>
class span;

// std::equal and std::lexicographical_compare are not /kernel compatible
// so all comparison operators must be removed for kernel mode.
#ifndef GSL_KERNEL_MODE

// [span.comparison], span comparison operators
template <class ElementType, std::size_t FirstExtent, std::size_t SecondExtent>
constexpr bool operator==(span<ElementType, FirstExtent> l, span<ElementType, SecondExtent> r)
{
    return std::equal(l.begin(), l.end(), r.begin(), r.end());
}

template <class ElementType, std::size_t Extent>
constexpr bool operator!=(span<ElementType, Extent> l, span<ElementType, Extent> r)
{
    return !(l == r);
}

template <class ElementType, std::size_t Extent>
constexpr bool operator<(span<ElementType, Extent> l, span<ElementType, Extent> r)
{
    return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
}

template <class ElementType, std::size_t Extent>
constexpr bool operator<=(span<ElementType, Extent> l, span<ElementType, Extent> r)
{
    return !(l > r);
}

template <class ElementType, std::size_t Extent>
constexpr bool operator>(span<ElementType, Extent> l, span<ElementType, Extent> r)
{
    return r < l;
}

template <class ElementType, std::size_t Extent>
constexpr bool operator>=(span<ElementType, Extent> l, span<ElementType, Extent> r)
{
    return !(l < r);
}

#endif // GSL_KERNEL_MODE

//
// make_span() - Utility functions for creating spans
//
template <class ElementType>
constexpr span<ElementType> make_span(ElementType* ptr, typename span<ElementType>::size_type count)
{
    return span<ElementType>(ptr, count);
}

template <class ElementType>
constexpr span<ElementType> make_span(ElementType* firstElem, ElementType* lastElem)
{
    return span<ElementType>(firstElem, lastElem);
}

template <class ElementType, std::size_t N>
constexpr span<ElementType, N> make_span(ElementType (&arr)[N]) noexcept
{
    return span<ElementType, N>(arr);
}

template <class Container>
constexpr span<typename Container::value_type> make_span(Container& cont)
{
    return span<typename Container::value_type>(cont);
}

template <class Container>
constexpr span<const typename Container::value_type> make_span(const Container& cont)
{
    return span<const typename Container::value_type>(cont);
}

template <class Ptr>
constexpr span<typename Ptr::element_type> make_span(Ptr& cont, std::size_t count)
{
    return span<typename Ptr::element_type>(cont, count);
}

template <class Ptr>
constexpr span<typename Ptr::element_type> make_span(Ptr& cont)
{
    return span<typename Ptr::element_type>(cont);
}

// Specialization of gsl::at for span
template <class ElementType, std::size_t Extent>
constexpr ElementType& at(span<ElementType, Extent> s, index i)
{
    // No bounds checking here because it is done in span::operator[] called below
    Ensures(i >= 0);
    return s[narrow_cast<std::size_t>(i)];
}

// [span.obs] Free observer functions
template <class ElementType, std::size_t Extent>
constexpr std::ptrdiff_t ssize(const span<ElementType, Extent>& s) noexcept
{
    return static_cast<std::ptrdiff_t>(s.size());
}

// [span.iter] Free functions for begin/end functions
template <class ElementType, std::size_t Extent>
constexpr typename span<ElementType, Extent>::iterator
begin(const span<ElementType, Extent>& s) noexcept
{
    return s.begin();
}

template <class ElementType, std::size_t Extent = dynamic_extent>
constexpr typename span<ElementType, Extent>::iterator
end(const span<ElementType, Extent>& s) noexcept
{
    return s.end();
}

template <class ElementType, std::size_t Extent>
constexpr typename span<ElementType, Extent>::reverse_iterator
rbegin(const span<ElementType, Extent>& s) noexcept
{
    return s.rbegin();
}

template <class ElementType, std::size_t Extent>
constexpr typename span<ElementType, Extent>::reverse_iterator
rend(const span<ElementType, Extent>& s) noexcept
{
    return s.rend();
}

template <class ElementType, std::size_t Extent>
constexpr typename span<ElementType, Extent>::iterator
cbegin(const span<ElementType, Extent>& s) noexcept
{
    return s.begin();
}

template <class ElementType, std::size_t Extent = dynamic_extent>
constexpr typename span<ElementType, Extent>::iterator
cend(const span<ElementType, Extent>& s) noexcept
{
    return s.end();
}

template <class ElementType, std::size_t Extent>
constexpr typename span<ElementType, Extent>::reverse_iterator
crbegin(const span<ElementType, Extent>& s) noexcept
{
    return s.rbegin();
}

template <class ElementType, std::size_t Extent>
constexpr typename span<ElementType, Extent>::reverse_iterator
crend(const span<ElementType, Extent>& s) noexcept
{
    return s.rend();
}

} // namespace gsl

#endif // GSL_SPAN_EXT_H
