/*
 * Types of NumPy arrays.
 *
 * Author: Malte J. Ziebarth (malte.ziebarth@tum.de)
 *
 * Copyright (C) 2026 Technische Universität München
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *
 *    1. Redistributions of source code must retain the above copyright notice, this
 *       list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. Neither the name of the copyright holder nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * SPDX-FileCopyrightText: 2026 Technical University of Munich
 * SPDX-License-Identifier: BSD-3-Clause
 * SPDX-FileContributor: Malte J. Ziebarth <malte.ziebarth@tum.de>
 */

#ifndef FUNKTIONSKLEMPNER_NUMPYTYPES_HPP
#define FUNKTIONSKLEMPNER_NUMPYTYPES_HPP

#include <stdexcept>
#include <cstdint>
#include <string_view>
#include <type_traits>

namespace funktionsklempner {

template<typename T>
concept NumPyDType =
    std::is_same_v<std::decay_t<T>, char>
    || std::is_same_v<std::decay_t<T>, unsigned char>
    || std::is_same_v<std::decay_t<T>, short>
    || std::is_same_v<std::decay_t<T>, unsigned short>
    || std::is_same_v<std::decay_t<T>, int>
    || std::is_same_v<std::decay_t<T>, unsigned int>
    || std::is_same_v<std::decay_t<T>, long>
    || std::is_same_v<std::decay_t<T>, unsigned long>
    || std::is_same_v<std::decay_t<T>, long long>
    || std::is_same_v<std::decay_t<T>, unsigned long long>
    /* The fixed width types are optional but need to be
     * accompanied by the corresponding *_MAX macro, if they
     * are defined:
     */
    #ifdef INT8_MAX
    || std::is_same_v<std::decay_t<T>, int8_t>
    #endif
    #ifdef UINT8_MAX
    || std::is_same_v<std::decay_t<T>, uint8_t>
    #endif
    #ifdef INT16_MAX
    || std::is_same_v<std::decay_t<T>, int16_t>
    #endif
    #ifdef UINT16_MAX
    || std::is_same_v<std::decay_t<T>, uint16_t>
    #endif
    #ifdef INT32_MAX
    || std::is_same_v<std::decay_t<T>, int32_t>
    #endif
    #ifdef UINT32_MAX
    || std::is_same_v<std::decay_t<T>, uint32_t>
    #endif
    #ifdef INT64_MAX
    || std::is_same_v<std::decay_t<T>, int64_t>
    #endif
    #ifdef UINT64_MAX
    || std::is_same_v<std::decay_t<T>, uint64_t>
    #endif
    #ifdef INTPTR_MAX
    || std::is_same_v<std::decay_t<T>, intptr_t>
    #endif
    #ifdef UINTPTR_MAX
    || std::is_same_v<std::decay_t<T>, uintptr_t>
    #endif
    || std::is_same_v<std::decay_t<T>, float>
    || std::is_same_v<std::decay_t<T>, double>
    || std::is_same_v<std::decay_t<T>, long double>;


template<typename T>
concept NonConstNumPyDType =
    NumPyDType<T> && !std::is_const_v<T>;


/*
 * Validate the type string passed from the Python side.
 */
template<NumPyDType T>
constexpr bool validate_dtype(const char* s)
{
    std::string_view sv(s);
    bool valid = false;
    if constexpr (std::is_same_v<T, char>)
        valid = valid || sv == "char";
    if constexpr (std::is_same_v<T, unsigned char>)
        valid = valid || sv == "unsigned char";
    if constexpr (std::is_same_v<T, short>)
        valid = valid || sv == "short";
    if constexpr (std::is_same_v<T, unsigned short>)
        valid = valid || sv == "unsigned short";
    if constexpr (std::is_same_v<T, int>)
        valid = valid || sv == "int";
    if constexpr (std::is_same_v<T, unsigned int>)
        valid = valid || sv == "unsigned int";
    if constexpr (std::is_same_v<T, long>)
        valid = valid || sv == "long";
    if constexpr (std::is_same_v<T, unsigned long>)
        valid = valid || sv == "unsigned long";
    if constexpr (std::is_same_v<T, long long>)
        valid = valid || sv == "long long";
    if constexpr (std::is_same_v<T, unsigned long long>)
        valid = valid || sv == "unsigned long long";

    /*
    * Optional fixed-width integer types:
    */
    #ifdef INT8_MAX
    if constexpr (std::is_same_v<T, int8_t>)
        valid = valid || sv == "int8_t";
    #endif

    #ifdef UINT8_MAX
    if constexpr (std::is_same_v<T, uint8_t>)
        valid = valid || sv == "uint8_t";
    #endif

    #ifdef INT16_MAX
    if constexpr (std::is_same_v<T, int16_t>)
        valid = valid || sv == "int16_t";
    #endif

    #ifdef UINT16_MAX
    if constexpr (std::is_same_v<T, uint16_t>)
        valid = valid || sv == "uint16_t";
    #endif

    #ifdef INT32_MAX
    if constexpr (std::is_same_v<T, int32_t>)
        valid = valid || sv == "int32_t";
    #endif

    #ifdef UINT32_MAX
    if constexpr (std::is_same_v<T, uint32_t>)
        valid = valid || sv == "uint32_t";
    #endif

    #ifdef INT64_MAX
    if constexpr (std::is_same_v<T, int64_t>)
        valid = valid || sv == "int64_t";
    #endif

    #ifdef UINT64_MAX
    if constexpr (std::is_same_v<T, uint64_t>)
        valid = valid || sv == "uint64_t";
    #endif

    #ifdef INTPTR_MAX
    if constexpr (std::is_same_v<T, intptr_t>)
        valid = valid || sv == "intptr_t";
    #endif

    #ifdef UINTPTR_MAX
    if constexpr (std::is_same_v<T, uintptr_t>)
        valid = valid || sv == "uintptr_t";
    #endif

    /*
     * Floating point types:
     */
    if constexpr (std::is_same_v<T, float>)
        valid = valid || sv == "float";
    if constexpr (std::is_same_v<T, double>)
        valid = valid || sv == "double";
    if constexpr (std::is_same_v<T, long double>)
        valid = valid || sv == "long double";

    return valid;
}



} // namespace funktionsklempner

#endif