/*******************************************************************************
* Copyright 2019-2021 Intel Corporation.
*
* This software and the related documents are Intel copyrighted  materials,  and
* your use of  them is  governed by the  express license  under which  they were
* provided to you (License).  Unless the License provides otherwise, you may not
* use, modify, copy, publish, distribute,  disclose or transmit this software or
* the related documents without Intel's prior written permission.
*
* This software and the related documents  are provided as  is,  with no express
* or implied  warranties,  other  than those  that are  expressly stated  in the
* License.
*******************************************************************************/

#ifndef _ONEAPI_MKL_VM_DECLS_HPP_
#define _ONEAPI_MKL_VM_DECLS_HPP_ 1

#include <cstdint>
#include <complex>
#include <exception>
#include <CL/sycl.hpp>

#include "oneapi/mkl/types.hpp"


#ifdef MKL_BUILD_DLL
#define __ONEAPI_MKL_VM_EXPORT     __declspec(dllexport)
#define __ONEAPI_MKL_VM_EXPORT_CPP __declspec(dllexport)
#else
#define __ONEAPI_MKL_VM_EXPORT extern
#define __ONEAPI_MKL_VM_EXPORT_CPP
#endif

namespace oneapi {
namespace mkl {

#ifndef _ONEAPI_MKL_SLICE_
#define _ONEAPI_MKL_SLICE_ 1
struct slice {
    size_t start;
    size_t size;
    int64_t stride;

    constexpr slice(): start(0), size(0), stride(0) { }
    constexpr slice(slice const & rhs) = default;

    constexpr slice(size_t _start, size_t _size, int64_t _stride): start(_start), size(_size), stride(_stride) { }
}; // struct slice
#endif // #ifndef _ONEAPI_MKL_SLICE_

namespace vm {

using event_vector = sycl::vector_class<sycl::event>;

namespace enums {
    enum class mode : std::uint32_t {
        not_defined = 0x0,

        la = 0x1,
        ha = 0x2,
        ep = 0x3,
        accuracy_mask = 0xF,

        global_status_report = 0x100,

        slice_normal  = 0x1000,
        slice_minimum = 0x2000,
        slice_cyclic  = 0x4000,
        slice_mask    = 0xF000,

        badarg_exception = 0x10000,
        badarg_quiet     = 0x20000,
        badarg_mask      = 0xF0000,

        default_mode = (ha | slice_normal | badarg_exception)
    };

    enum class status : std::uint32_t {
        not_defined       = 0x0,
        success           = 0x0,
        errdom            = 0x1,
        sing              = 0x2,
        overflow          = 0x4,
        underflow         = 0x8,
        accuracy_warning  = 0x80,
        fix_all           = 0xff,

        computation_empty = 0x100
    };

    template <typename T>
    struct bits_enabled { static constexpr bool enabled = false; };

    template <>
    struct bits_enabled<mode> { static constexpr bool enabled = true; };

    template <>
    struct bits_enabled<status> { static constexpr bool enabled = true; };

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, T>::type
    operator |(T lhs, T rhs) {
        auto r = static_cast<typename std::underlying_type_t<T> >(lhs) | static_cast<typename std::underlying_type_t<T> >(rhs);
        return static_cast<T>(r);
    }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, T>::type &
    operator |=(T & lhs, T rhs) {
        auto r = static_cast<typename std::underlying_type_t<T> >(lhs) | static_cast<typename std::underlying_type_t<T> >(rhs);
        lhs = static_cast<T>(r);
        return lhs;
    }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, T>::type
    operator &(T lhs, T rhs) {
        auto r = static_cast<typename std::underlying_type_t<T> >(lhs) & static_cast<typename std::underlying_type_t<T> >(rhs);
        return static_cast<T>(r);
    }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, T>::type
    operator &=(T & lhs, T rhs) {
        auto r = static_cast<typename std::underlying_type_t<T> >(lhs) & static_cast<typename std::underlying_type_t<T> >(rhs);
        lhs = static_cast<T>(r);
        return lhs;
    }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, T>::type
    operator ^(T lhs, T rhs) {
        auto r = static_cast<typename std::underlying_type_t<T> >(lhs) ^ static_cast<typename std::underlying_type_t<T> >(rhs);
        return static_cast<T>(r);
    }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, T>::type
    operator ^=(T & lhs, T rhs) {
        auto r = static_cast<typename std::underlying_type_t<T> >(lhs) ^ static_cast<typename std::underlying_type_t<T> >(rhs);
        lhs = static_cast<T>(r);
        return lhs;
    }


    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, bool>::type
    operator !(T v) { return (0 == static_cast<typename std::underlying_type_t<T> >(v)); }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, bool>::type
    has_none(T v, T mask) {
        auto r = static_cast<typename std::underlying_type_t<T> >(v) & static_cast<typename std::underlying_type_t<T> >(mask);
        return (0 == r);
    }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, bool>::type
    has_any(T v, T mask) {
        auto r = static_cast<typename std::underlying_type_t<T> >(v) & static_cast<typename std::underlying_type_t<T> >(mask);
        return (0 != r);
    }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, bool>::type
    has_all(T v, T mask) {
        auto r = static_cast<typename std::underlying_type_t<T> >(v) & static_cast<typename std::underlying_type_t<T> >(mask);
        return (static_cast<typename std::underlying_type_t<T> >(mask) == r);
    }

    template <typename T>
    constexpr
    typename std::enable_if<bits_enabled<T>::enabled, bool>::type
    has_only(T v, T mask) {
        auto r = static_cast<typename std::underlying_type_t<T> >(v) ^ static_cast<typename std::underlying_type_t<T> >(mask);
        return (0 == r);
    }
} // namespace enums

using enums::mode;
using enums::status;

namespace detail {
    using std::int64_t;
    namespace one_vm = oneapi::mkl::vm;

    template <typename T>
    struct __ONEAPI_MKL_VM_EXPORT_CPP error_handler {
        bool enabled_;
        bool is_usm_;

        sycl::buffer<one_vm::status, 1> buf_status_;
        one_vm::status                  * usm_status_;
        std::int64_t len_;

        one_vm::status status_to_fix_;
        T fixup_value_;
        bool copy_sign_;

        error_handler():
            enabled_       { false },
            is_usm_        { false },

            buf_status_    { sycl::buffer<one_vm::status, 1>(1) },
            usm_status_    { nullptr },
            len_           { 0 },
            status_to_fix_ { one_vm::status::not_defined },
            fixup_value_   { T {} },
            copy_sign_     { false }
            { }

        error_handler(one_vm::status status_to_fix, T fixup_value, bool copy_sign = false):
            enabled_          { true },
            is_usm_           { false },

            buf_status_       { sycl::buffer<one_vm::status, 1>(1) },
            usm_status_       { nullptr },
            len_              { 0 },
            status_to_fix_    { status_to_fix },
            fixup_value_      { fixup_value },
            copy_sign_        { copy_sign }
            { }

        error_handler(one_vm::status * array, std::int64_t len = 1, one_vm::status status_to_fix = one_vm::status::not_defined, T fixup_value = {}, bool copy_sign = false):
            enabled_       { true },
            is_usm_        { true },

            buf_status_    { sycl::buffer<one_vm::status, 1>(1) },
            usm_status_    { array },
            len_           { len },
            status_to_fix_ { status_to_fix },
            fixup_value_   { fixup_value },
            copy_sign_     { copy_sign }
            { }

        error_handler(sycl::buffer<one_vm::status, 1> & buf, std::int64_t len = 1, one_vm::status status_to_fix = one_vm::status::not_defined, T fixup_value = {}, bool copy_sign = false):
            enabled_       { true },
            is_usm_        { false },

            buf_status_    { buf },
            usm_status_    { nullptr },
            len_           { len },
            status_to_fix_ { status_to_fix },
            fixup_value_   { fixup_value },
            copy_sign_     { copy_sign }
            { }

    }; // struct error_handler

} // namespace detail

using detail::error_handler;

// Service functions
__ONEAPI_MKL_VM_EXPORT oneapi::mkl::vm::mode    get_mode(sycl::queue & queue);
__ONEAPI_MKL_VM_EXPORT oneapi::mkl::vm::mode    set_mode(sycl::queue & queue, oneapi::mkl::vm::mode new_mode);

__ONEAPI_MKL_VM_EXPORT oneapi::mkl::vm::status  get_status(sycl::queue & queue);
__ONEAPI_MKL_VM_EXPORT oneapi::mkl::vm::status  set_status(sycl::queue & queue, oneapi::mkl::vm::status new_status);
__ONEAPI_MKL_VM_EXPORT oneapi::mkl::vm::status  clear_status(sycl::queue & queue);

} // namespace vm
} // namespace mkl
} // namespace oneapi

#endif // ifndef _ONEAPI_MKL_VM_DECLS_HPP_

