//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Reflection;

#if !WASM
namespace user_rtx { using u = user_rtx;
#else
namespace user_rtx.wasm { using u = user_rtx.wasm;
#endif

/// <summary>
/// Type information (reflection)
/// </summary>
public static partial class type {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static bool IsTypeOf<TComparand>( Type type ) =>
   IsTypeOf( type, typeof(TComparand) );

public static bool IsTypeOf( Type type, Type comparandType ) =>
   type==comparandType || type.IsSubclassOf( comparandType );

//--------------------------------------------------------------------------------------------------

public static bool IsValueOf( object value, Type comparandType ) =>
   value != null ?
      IsTypeOf( value.GetType(), comparandType ) :
      false;

//==================================================================================================

public static bool IsOneOf( Type type, params Type[] comparandTypes )
{
   foreach ( Type ct in comparandTypes )
      if ( IsTypeOf( type, ct ) )
         return true;
   return false;
}

public static bool IsOneOf<T>( params Type[] comparandTypes ) =>
   IsOneOf( typeof(T), comparandTypes );

//--------------------------------------------------------------------------------------------------

public static bool IsValueOneOf( object value, params Type[] comparandTypes )
{
   foreach ( Type ct in comparandTypes )
      if ( IsValueOf( value, ct ) )
         return true;
   return false;
}

//==================================================================================================

/// <exception cref="ArgumentException" />
public static bool ImplementsInterface( Type type, Type interfaceType )
{
   if ( type.IsInterface )
      throw new ArgumentException(
         "Parameter “{nameof(type)}” must not contain interface type." );
   if ( !interfaceType.IsInterface )
      throw new ArgumentException(
         "Parameter “{nameof(interfaceType)}” must contain interface type." );
   return interfaceType.IsAssignableFrom( type );
}

/// <exception cref="ArgumentException" />
public static bool ImplementsInterface<I>( Type type ) =>
   ImplementsInterface( type, typeof(I) );

//==================================================================================================

public static object CreateInstance( Type type ) =>
   CreateInstanceUsingVisibilityFlags( type, BindingFlags.Public );

public static object CreateInstance( Type type, params object?[] constructorArgs ) =>
   CreateInstanceUsingVisibilityFlags( type, BindingFlags.Public, constructorArgs );

public static object CreateInstanceX( Type type, object?[]? ctorParameters ) =>
   CreateInstanceUsingVisibilityFlagsX( type, BindingFlags.Public, ctorParameters );

//--------------------------------------------------------------------------------------------------

public static T CreateInstance<T>() =>
   (T)CreateInstance( typeof(T) );

public static T CreateInstance<T>( params object?[] constructorArgs ) =>
   (T)CreateInstance( typeof(T), constructorArgs );

public static T CreateInstanceX<T>( object?[]? ctorParameters ) =>
   (T)CreateInstanceX( typeof(T), ctorParameters );

//==================================================================================================

public static object CreateInstanceUsingVisibilityFlags( Type type,
                                                         BindingFlags visibilityFlags ) =>
   CreateInstanceUsingVisibilityFlagsX( type, visibilityFlags, null );

public static object CreateInstanceUsingVisibilityFlags( Type type,
                                                         BindingFlags visibilityFlags,
                                                         params object?[] constructorArgs ) =>
   CreateInstanceUsingVisibilityFlagsX( type, visibilityFlags, constructorArgs );

public static object CreateInstanceUsingVisibilityFlagsX( Type type,
                                                          BindingFlags visibilityFlags,
                                                          object?[]? ctorParameters ) =>
   type.InvokeMember( null!,
      visibilityFlags | BindingFlags.Instance | BindingFlags.CreateInstance,
      null, null, ctorParameters ?? u.args.Empty ).NN();

//--------------------------------------------------------------------------------------------------

public static T CreateInstanceUsingVisibilityFlags<T>( BindingFlags visibilityFlags ) =>
   (T)CreateInstanceUsingVisibilityFlags( typeof(T), visibilityFlags );

public static T CreateInstanceUsingVisibilityFlags<T>( BindingFlags visibilityFlags,
                                                       params object?[] constructorArgs ) =>
   (T)CreateInstanceUsingVisibilityFlags( typeof(T), visibilityFlags, constructorArgs );

public static T CreateInstanceUsingVisibilityFlagsX<T>( BindingFlags visibilityFlags,
                                                        object?[]? ctorParameters ) =>
   (T)CreateInstanceUsingVisibilityFlagsX( typeof(T), visibilityFlags, ctorParameters );

//==================================================================================================

public static Attribute? GetAttribute( Type type, Type attributeType )
{
   u.type_argcheck.TypeOf<Attribute>( attributeType, nameof(attributeType) );

   var attributes =
      (Attribute[])type.GetCustomAttributes( attributeType, false );

   switch ( attributes.Length )
   {
   case 0: return null;
   case 1: return attributes[ 0 ];
   default:
      throw e_unexpected.New(
         "Multiple attributes of “{0}” type on “{1}” type were found.",
         attributeType, type );
   } // switch
}

public static TAttribute? GetAttribute<TAttribute>( Type type )
where TAttribute: Attribute =>
   (TAttribute?)GetAttribute( type, typeof(TAttribute) );

//--------------------------------------------------------------------------------------------------

/// <exception cref="ArgumentException" />
public static bool IsMarkedWithAttribute( Type type, Type attributeType ) =>
   GetAttribute( type, attributeType ) != null;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // type

} // user_rtx (.wasm)
