use crate::proc::{concrete_int_scalars, vector_size_str, vector_sizes, KeywordSet};
use crate::racy_lock::RacyLock;
use alloc::{format, string::String, vec::Vec};

// MSLS - Metal Shading Language Specification:
// https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
//
// C++ - Standard for Programming Language C++ (N4431)
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4431.pdf
const RESERVED: &[&str] = &[
    // Undocumented
    "assert", // found in https://github.com/gfx-rs/wgpu/issues/5347
    // Standard for Programming Language C++ (N4431): 2.5 Alternative tokens
    "and",
    "bitor",
    "or",
    "xor",
    "compl",
    "bitand",
    "and_eq",
    "or_eq",
    "xor_eq",
    "not",
    "not_eq",
    // Standard for Programming Language C++ (N4431): 2.11 Keywords
    "alignas",
    "alignof",
    "asm",
    "auto",
    "bool",
    "break",
    "case",
    "catch",
    "char",
    "char16_t",
    "char32_t",
    "class",
    "const",
    "constexpr",
    "const_cast",
    "continue",
    "decltype",
    "default",
    "delete",
    "do",
    "double",
    "dynamic_cast",
    "else",
    "enum",
    "explicit",
    "export",
    "extern",
    "false",
    "float",
    "for",
    "friend",
    "goto",
    "if",
    "inline",
    "int",
    "long",
    "mutable",
    "namespace",
    "new",
    "noexcept",
    "nullptr",
    "operator",
    "private",
    "protected",
    "public",
    "register",
    "reinterpret_cast",
    "return",
    "short",
    "signed",
    "sizeof",
    "static",
    "static_assert",
    "static_cast",
    "struct",
    "switch",
    "template",
    "this",
    "thread_local",
    "throw",
    "true",
    "try",
    "typedef",
    "typeid",
    "typename",
    "union",
    "unsigned",
    "using",
    "virtual",
    "void",
    "volatile",
    "wchar_t",
    "while",
    // Metal Shading Language Specification: 1.4.4 Restrictions
    "main",
    // Metal Shading Language Specification: 2.1 Scalar Data Types
    "int8_t",
    "uchar",
    "uint8_t",
    "int16_t",
    "ushort",
    "uint16_t",
    "int32_t",
    "uint",
    "uint32_t",
    "int64_t",
    "uint64_t",
    "half",
    "bfloat",
    "size_t",
    "ptrdiff_t",
    // Metal Shading Language Specification: 2.2 Vector Data Types
    "bool2",
    "bool3",
    "bool4",
    "char2",
    "char3",
    "char4",
    "short2",
    "short3",
    "short4",
    "int2",
    "int3",
    "int4",
    "long2",
    "long3",
    "long4",
    "uchar2",
    "uchar3",
    "uchar4",
    "ushort2",
    "ushort3",
    "ushort4",
    "uint2",
    "uint3",
    "uint4",
    "ulong2",
    "ulong3",
    "ulong4",
    "half2",
    "half3",
    "half4",
    "bfloat2",
    "bfloat3",
    "bfloat4",
    "float2",
    "float3",
    "float4",
    "vec",
    // Metal Shading Language Specification: 2.2.3 Packed Vector Types
    "packed_bool2",
    "packed_bool3",
    "packed_bool4",
    "packed_char2",
    "packed_char3",
    "packed_char4",
    "packed_short2",
    "packed_short3",
    "packed_short4",
    "packed_int2",
    "packed_int3",
    "packed_int4",
    "packed_uchar2",
    "packed_uchar3",
    "packed_uchar4",
    "packed_ushort2",
    "packed_ushort3",
    "packed_ushort4",
    "packed_uint2",
    "packed_uint3",
    "packed_uint4",
    "packed_half2",
    "packed_half3",
    "packed_half4",
    "packed_bfloat2",
    "packed_bfloat3",
    "packed_bfloat4",
    "packed_float2",
    "packed_float3",
    "packed_float4",
    "packed_long2",
    "packed_long3",
    "packed_long4",
    "packed_vec",
    // Metal Shading Language Specification: 2.3 Matrix Data Types
    "half2x2",
    "half2x3",
    "half2x4",
    "half3x2",
    "half3x3",
    "half3x4",
    "half4x2",
    "half4x3",
    "half4x4",
    "float2x2",
    "float2x3",
    "float2x4",
    "float3x2",
    "float3x3",
    "float3x4",
    "float4x2",
    "float4x3",
    "float4x4",
    "matrix",
    // Metal Shading Language Specification: 2.6 Atomic Data Types
    "atomic",
    "atomic_int",
    "atomic_uint",
    "atomic_bool",
    "atomic_ulong",
    "atomic_float",
    // Metal Shading Language Specification: 2.20 Type Conversions and Re-interpreting Data
    "as_type",
    // Metal Shading Language Specification: 4 Address Spaces
    "device",
    "constant",
    "thread",
    "threadgroup",
    "threadgroup_imageblock",
    "ray_data",
    "object_data",
    // Metal Shading Language Specification: 5.1 Functions
    "vertex",
    "fragment",
    "kernel",
    // Metal Shading Language Specification: 6.1 Namespace and Header Files
    "metal",
    // C99 / C++ extension:
    "restrict",
    // Metal reserved types in <metal_types>:
    "llong",
    "ullong",
    "quad",
    "complex",
    "imaginary",
    // Constants in <metal_types>:
    "CHAR_BIT",
    "SCHAR_MAX",
    "SCHAR_MIN",
    "UCHAR_MAX",
    "CHAR_MAX",
    "CHAR_MIN",
    "USHRT_MAX",
    "SHRT_MAX",
    "SHRT_MIN",
    "UINT_MAX",
    "INT_MAX",
    "INT_MIN",
    "ULONG_MAX",
    "LONG_MAX",
    "LONG_MIN",
    "ULLONG_MAX",
    "LLONG_MAX",
    "LLONG_MIN",
    "FLT_DIG",
    "FLT_MANT_DIG",
    "FLT_MAX_10_EXP",
    "FLT_MAX_EXP",
    "FLT_MIN_10_EXP",
    "FLT_MIN_EXP",
    "FLT_RADIX",
    "FLT_MAX",
    "FLT_MIN",
    "FLT_EPSILON",
    "FLT_DECIMAL_DIG",
    "FP_ILOGB0",
    "FP_ILOGB0",
    "FP_ILOGBNAN",
    "FP_ILOGBNAN",
    "MAXFLOAT",
    "HUGE_VALF",
    "INFINITY",
    "NAN",
    "M_E_F",
    "M_LOG2E_F",
    "M_LOG10E_F",
    "M_LN2_F",
    "M_LN10_F",
    "M_PI_F",
    "M_PI_2_F",
    "M_PI_4_F",
    "M_1_PI_F",
    "M_2_PI_F",
    "M_2_SQRTPI_F",
    "M_SQRT2_F",
    "M_SQRT1_2_F",
    "HALF_DIG",
    "HALF_MANT_DIG",
    "HALF_MAX_10_EXP",
    "HALF_MAX_EXP",
    "HALF_MIN_10_EXP",
    "HALF_MIN_EXP",
    "HALF_RADIX",
    "HALF_MAX",
    "HALF_MIN",
    "HALF_EPSILON",
    "HALF_DECIMAL_DIG",
    "MAXHALF",
    "HUGE_VALH",
    "M_E_H",
    "M_LOG2E_H",
    "M_LOG10E_H",
    "M_LN2_H",
    "M_LN10_H",
    "M_PI_H",
    "M_PI_2_H",
    "M_PI_4_H",
    "M_1_PI_H",
    "M_2_PI_H",
    "M_2_SQRTPI_H",
    "M_SQRT2_H",
    "M_SQRT1_2_H",
    "DBL_DIG",
    "DBL_MANT_DIG",
    "DBL_MAX_10_EXP",
    "DBL_MAX_EXP",
    "DBL_MIN_10_EXP",
    "DBL_MIN_EXP",
    "DBL_RADIX",
    "DBL_MAX",
    "DBL_MIN",
    "DBL_EPSILON",
    "DBL_DECIMAL_DIG",
    "MAXDOUBLE",
    "HUGE_VAL",
    "M_E",
    "M_LOG2E",
    "M_LOG10E",
    "M_LN2",
    "M_LN10",
    "M_PI",
    "M_PI_2",
    "M_PI_4",
    "M_1_PI",
    "M_2_PI",
    "M_2_SQRTPI",
    "M_SQRT2",
    "M_SQRT1_2",
    // Naga utilities
    "DefaultConstructible",
    super::writer::FREXP_FUNCTION,
    super::writer::MODF_FUNCTION,
    super::writer::ABS_FUNCTION,
    super::writer::DIV_FUNCTION,
    // DOT_FUNCTION_PREFIX variants are added dynamically below
    super::writer::MOD_FUNCTION,
    super::writer::NEG_FUNCTION,
    super::writer::F2I32_FUNCTION,
    super::writer::F2U32_FUNCTION,
    super::writer::F2I64_FUNCTION,
    super::writer::F2U64_FUNCTION,
    super::writer::IMAGE_LOAD_EXTERNAL_FUNCTION,
    super::writer::IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION,
    super::writer::IMAGE_SIZE_EXTERNAL_FUNCTION,
    super::writer::ARGUMENT_BUFFER_WRAPPER_STRUCT,
    super::writer::EXTERNAL_TEXTURE_WRAPPER_STRUCT,
];

// The set of concrete integer dot product function variants.
// This must match the set of names that could be produced by
// `Writer::get_dot_wrapper_function_helper_name`.
static DOT_FUNCTION_NAMES: RacyLock<Vec<String>> = RacyLock::new(|| {
    let mut names = Vec::new();
    for scalar in concrete_int_scalars().map(crate::Scalar::to_msl_name) {
        for size_suffix in vector_sizes().map(vector_size_str) {
            let fun_name = format!(
                "{}_{}{}",
                super::writer::DOT_FUNCTION_PREFIX,
                scalar,
                size_suffix
            );
            names.push(fun_name);
        }
    }
    names
});

/// The above set of reserved keywords, turned into a cached HashSet. This saves
/// significant time during [`Namer::reset`](crate::proc::Namer::reset).
///
/// See <https://github.com/gfx-rs/wgpu/pull/7338> for benchmarks.
pub static RESERVED_SET: RacyLock<KeywordSet> = RacyLock::new(|| {
    let mut set = KeywordSet::from_iter(RESERVED);
    set.extend(DOT_FUNCTION_NAMES.iter().map(String::as_str));
    set
});
