//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// 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>
/// Property information (reflection)
/// </summary>
public static class property {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static string GetFullName( [u.req] MethodBase? propertyMethod )
{
   propertyMethod = u.arg_check.NN( propertyMethod, nameof(propertyMethod) );
   string methodName = propertyMethod.Name;
   if ( methodName.Length <= 4 || !(
        methodName.StartsWith( "get_" ) ||
        methodName.StartsWith( "set_" ) ))
      throw new ArgumentException(
         "Method is not a “get” or “set” method of a property.",
         nameof(propertyMethod) );
   return string.Format( "{0}.{1}", propertyMethod.DeclaringType, methodName[ 4.. ] );
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static string ExtractNameByGetMethod( [u.req] MethodBase? getMethod )
{
   getMethod = u.arg_check.NN( getMethod, nameof(getMethod) );
   string methodName = getMethod.Name;
   return methodName.StartsWith( "get_" ) && methodName.Length > 4 ?
      methodName[ 4.. ] : throw new ArgumentException(
         "Method is not a “get”-method of a property.",
         nameof(getMethod) );
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static string ExtractNameBySetMethod( [u.req] MethodBase? setMethod )
{
   setMethod = u.arg_check.NN( setMethod, nameof(setMethod) );
   string methodName = setMethod.Name;
   return methodName.StartsWith( "set_" ) && methodName.Length > 4 ?
      methodName[ 4.. ] : throw new ArgumentException(
         "Method is not a “set”-method of a property.",
         nameof(setMethod) );
}

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

public static object? GetStaticValue( Type type, string propertyName ) =>
   GetStaticValueUsingVisibilityFlags( type, propertyName, BindingFlags.Public );

public static void SetStaticValue( Type type, string propertyName, object value ) =>
   SetStaticValueUsingVisibilityFlags( type, propertyName, BindingFlags.Public, value );

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

public static object? GetInstanceValue( object obj, string propertyName ) =>
   GetInstanceValueUsingVisibilityFlags( obj, propertyName, BindingFlags.Public );

public static void SetInstanceValue( object obj, string propertyName, object value ) =>
   SetInstanceValueUsingVisibilityFlags( obj, propertyName, BindingFlags.Public, value );

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

public static object? GetStaticValueUsingVisibilityFlags( Type type,
                                                          string propertyName,
                                                          BindingFlags visibilityFlags ) =>
   type.InvokeMember( propertyName,
      visibilityFlags | BindingFlags.Static | BindingFlags.GetProperty,
      null, null, u.args.Empty );

public static void SetStaticValueUsingVisibilityFlags( Type type,
                                                       string propertyName,
                                                       BindingFlags visibilityFlags,
                                                       object value ) =>
   type.InvokeMember( propertyName,
      visibilityFlags | BindingFlags.Static | BindingFlags.SetProperty,
      null, null, new object[]{ value } );

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

public static object? GetInstanceValueUsingVisibilityFlags( object obj,
                                                            string propertyName,
                                                            BindingFlags visibilityFlags ) =>
   obj.GetType().InvokeMember( propertyName,
      visibilityFlags | BindingFlags.Instance | BindingFlags.GetProperty,
      null, obj, u.args.Empty );

public static void SetInstanceValueUsingVisibilityFlags( object obj,
                                                         string propertyName,
                                                         BindingFlags visibilityFlags,
                                                         object value ) =>
   obj.GetType().InvokeMember( propertyName,
      visibilityFlags | BindingFlags.Instance | BindingFlags.SetProperty,
      null, obj, new object[]{ value } );

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

public static InvalidOperationException OnceSettableViolationError( string propertyFullName ) =>
   e_invalid_operation.New(
      "Property must not be set more than one time: “{0}”.",
      propertyFullName );

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // property

} // user_rtx (.wasm)
