module std.traits;

public import core.internal.traits;

enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real);

template isIntegral(T)
{
    static if (!__traits(isIntegral, T))
        enum isIntegral = false;
    else static if (is(T U == enum))
        enum isIntegral = isIntegral!U;
    else
        enum isIntegral = __traits(isZeroInit, T) // Not char, wchar, or dchar.
            && !is(immutable T == immutable bool) && !is(T == __vector);
}

template isNumeric(T)
{
    static if (!__traits(isArithmetic, T))
        enum isNumeric = false;
    else static if (__traits(isFloating, T))
        enum isNumeric = is(T : real); // Not __vector, imaginary, or complex.
    else static if (is(T U == enum))
        enum isNumeric = isNumeric!U;
    else
        enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar.
            && !is(immutable T == immutable bool) && !is(T == __vector);
}

enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T)
                                                  && is(T : real);

enum hasMember(T, string name) = __traits(hasMember, T, name);

enum bool isArray(T) = isStaticArray!T || isDynamicArray!T;

enum bool isStaticArray(T) = __traits(isStaticArray, T);

template isDynamicArray(T)
{
    static if (is(T == U[], U))
        enum bool isDynamicArray = true;
    else static if (is(T U == enum))
        // BUG: isDynamicArray / isStaticArray considers enums
        // with appropriate base types as dynamic/static arrays
        // Retain old behaviour for now, see
        // https://github.com/dlang/phobos/pull/7574
        enum bool isDynamicArray = isDynamicArray!U;
    else
        enum bool isDynamicArray = false;
}

enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs);

enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; });

enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; });

enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...);

template isInstanceOf(alias S, alias T)
{
    enum impl(alias T : S!Args, Args...) = true;
    enum impl(alias T) = false;
    enum isInstanceOf = impl!T;
}

import core.internal.traits : CoreUnqual = Unqual;
alias Unqual = CoreUnqual;

@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);

@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);

enum bool isIterable(T) = is(typeof({ foreach (elem; T.init) {} }));

template isAggregateType(T)
{
    static if (is(T == enum))
        enum isAggregateType = isAggregateType!(OriginalType!T);
    else
        enum isAggregateType = is(T == struct) || is(T == class) || is(T == interface) || is(T == union);
}

enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar));

template Select(bool condition, T...)
if (T.length == 2)
{
	import std.meta : Alias;
	alias Select = Alias!(T[!condition]);
}

