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

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

/// <summary>
/// Method information (reflection)
/// </summary>
public static class method {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="ArgumentNullException" />
public static string GetFullName( [u.req] MethodBase? method )
{
   method = u.arg_check.NN( method, nameof(method) );
   return string.Format( "{0}.{1}", method.DeclaringType, method.Name );
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static Attribute? GetAttribute( [u.req] MethodBase? method, Type attributeType )
{
   method = u.arg_check.NN( method, nameof(method) );
   u.type_argcheck.TypeOf<Attribute>( attributeType, nameof(attributeType) );

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

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

/// <exception cref="ArgumentNullException" />
public static TAttribute? GetAttribute<TAttribute>( [u.req] MethodBase? method )
where TAttribute: Attribute =>
   (TAttribute?)GetAttribute( method, typeof(TAttribute) );

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

/// <exception cref="ArgumentNullException" />
public static bool IsMarkedWithAttribute( [u.req] MethodBase? method, Type attributeType ) =>
   GetAttribute( method, attributeType ) != null;

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

public static object? InvokeStaticMethod( Type type, string methodName ) =>
   InvokeStaticMethod( type, methodName, null );

public static object? InvokeStaticMethod( Type type,
                                          string methodName,
                                          params object?[]? args ) =>
   InvokeStaticMethodUsingVisibilityFlags(
      type, methodName, BindingFlags.Public, args ?? u.args.Empty );

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

public static object? InvokeInstanceMethod( object obj, string methodName ) =>
   InvokeInstanceMethod( obj, methodName, null );

public static object? InvokeInstanceMethod( object obj,
                                            string methodName,
                                            params object?[]? args ) =>
   InvokeInstanceMethodUsingVisibilityFlags(
      obj, methodName, BindingFlags.Public, args ?? u.args.Empty );

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

public static object? InvokeStaticMethod( string typeName, string methodName ) =>
   InvokeStaticMethod( typeName, methodName, null );

public static object? InvokeStaticMethod( string typeName,
                                          string methodName,
                                          params object?[]? args ) =>
   InvokeStaticMethodUsingVisibilityFlags(
      typeName, methodName, BindingFlags.Public, args ?? u.args.Empty );

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

public static object? InvokeStaticMethod( Assembly assembly, string typeName, string methodName ) =>
   InvokeStaticMethod(assembly,typeName,methodName,null);

public static object? InvokeStaticMethod( Assembly assembly,
                                          string typeName,
                                          string methodName,
                                          params object?[]? args ) =>
   InvokeStaticMethodUsingVisibilityFlags(
      assembly, typeName, methodName,
      BindingFlags.Public,
      args ?? u.args.Empty );

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

public static object? InvokeStaticMethodUsingVisibilityFlags( Type type,
                                                              string methodName,
                                                              BindingFlags visibilityFlags ) =>
   InvokeStaticMethodUsingVisibilityFlags( type, methodName, visibilityFlags, null );

public static object? InvokeInstanceMethodUsingVisibilityFlags( object obj,
                                                                string methodName,
                                                                BindingFlags visibilityFlags ) =>
   InvokeInstanceMethodUsingVisibilityFlags( obj, methodName, visibilityFlags, null );

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

public static object? InvokeStaticMethodUsingVisibilityFlags( Type type,
                                                              string methodName,
                                                              BindingFlags visibilityFlags,
                                                              params object?[]? args ) =>
   type.InvokeMember( methodName,
      visibilityFlags | BindingFlags.Static | BindingFlags.InvokeMethod,
      null, null, args ?? u.args.Empty );

public static object? InvokeInstanceMethodUsingVisibilityFlags( object obj,
                                                                string methodName,
                                                                BindingFlags visibilityFlags,
                                                                params object?[]? args ) =>
   obj.GetType().InvokeMember( methodName,
      visibilityFlags | BindingFlags.Instance | BindingFlags.InvokeMethod,
      null, obj, args ?? u.args.Empty );

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

public static object? InvokeStaticMethodUsingVisibilityFlags( string typeName,
                                                              string methodName,
                                                              BindingFlags visibilityFlags ) =>
   InvokeStaticMethodUsingVisibilityFlags(
      typeName, methodName, visibilityFlags, null );

public static object? InvokeStaticMethodUsingVisibilityFlags( string typeName,
                                                              string methodName,
                                                              BindingFlags visibilityFlags,
                                                              params object?[]? args ) =>
   InvokeStaticMethodUsingVisibilityFlags(
      Type.GetType( typeName, true ).NN(),
      methodName, visibilityFlags,
      args ?? u.args.Empty );

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

public static object? InvokeStaticMethodUsingVisibilityFlags( Assembly assembly,
                                                              string typeName,
                                                              string methodName,
                                                              BindingFlags visibilityFlags ) =>
   InvokeStaticMethodUsingVisibilityFlags(
      assembly, typeName, methodName, visibilityFlags, null );

public static object? InvokeStaticMethodUsingVisibilityFlags( Assembly assembly,
                                                              string typeName,
                                                              string methodName,
                                                              BindingFlags visibilityFlags,
                                                              params object?[]? args ) =>
   InvokeStaticMethodUsingVisibilityFlags(
      assembly.GetType( typeName, true ).NN(),
      methodName, visibilityFlags,
      args ?? u.args.Empty );

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

public static InvalidOperationException OnceInvocableViolationError( string methodFullName ) =>
   e_invalid_operation.New(
      "Method must not be called more than one time: “{0}”.",
      methodFullName );

/// <exception cref="ArgumentException" />
public static InvalidOperationException OnceInvocableViolationError( Type objectType,
                                                                     [CallerMemberName]
                                                                     string methodName="" ) =>
   OnceInvocableViolationError( u.code_fmt.MemberName( objectType,
      u.str_argcheck.NE( methodName, nameof(methodName) )));

/// <exception cref="ArgumentNullException" />
public static InvalidOperationException OnceInvocableViolationError( MethodBase? method ) =>
   OnceInvocableViolationError( GetFullName( method ) );

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // method

} // user_rtx (.wasm)
