//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#if !WASM
using System;
using System.Reflection;
using System.Configuration;
using System.Runtime.CompilerServices;
using u = user_rtx;

namespace user_rtx {

/// <summary>
/// Simple application settings (instance class)
/// </summary>
public abstract class ApplicationSettingsBase: System.Configuration.ApplicationSettingsBase {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

enum ValueSource { Default, Storage, Field }

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

[u.volatile_access]
public Exception? InitializationException =>
   _initializationException;

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

/// <exception cref="ArgumentException" />
/// <exception cref="EOperationFailed" />
public T? GetValue<T>( [CallerMemberName] string propertyName="" )
{
   u.str_argcheck.NE( propertyName, nameof(propertyName) );
   if ( _readActualValues )
   {
      object valueObject = this[ propertyName ];
      if ( valueObject != null )
         return (T)valueObject;
   }
   string? defaultValueString = GetDefaultValueString( propertyName );
   return defaultValueString != null ?
      (T?)Parse<T>( propertyName, defaultValueString ) :
      default( T );
}

/// <exception cref="ArgumentException" />
public void SetValue( object? value, [CallerMemberName] string propertyName="" ) =>
   this[ u.str_argcheck.NE( propertyName, nameof(propertyName) ) ] = value;

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

[MethodImpl( MethodImplOptions.NoInlining )]
public void ProbeValue( object? value )
{}

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

protected virtual Type ActualType =>
   throw new NotSupportedException();

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

protected virtual void ProbeValues() =>
   throw new NotSupportedException();

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

protected void Constructor( bool useDefaultValues )
{
   Prepare( true );
   if ( !useDefaultValues )
      if ( !Prepare( false ) )
         Prepare( true );
}

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

bool Prepare( bool useDefaultValues )
{
   _readActualValues = !useDefaultValues;
   try {
      ProbeValues(); }
   catch ( Exception e ) {
      if ( useDefaultValues )
         throw;
      _initializationException = e;
      return false;
   }
   return true;
}

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

/// <exception cref="EOperationFailed" />
string? GetDefaultValueString( string propertyName )
{
   var propertyInfo = ActualType.GetProperty( propertyName );
   if ( propertyInfo == null )
      return null;
   var attributes = propertyInfo.GetCustomAttributes(
      typeof(DefaultSettingValueAttribute), false );
   if ( attributes.Length > 1 )
      throw e_operation_failed.New(
         "Ambigous default value of application settings property: “{0}”.",
         propertyName );
   return attributes.Length > 0 ?
      ((DefaultSettingValueAttribute)attributes[0]).Value :
      null;
}

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

/// <exception cref="EOperationFailed" />
static object? Parse<T>( string propertyName, string? valueString )
{
   MethodInfo? method =
      typeof(T).GetMethod( "Parse",
         BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
         null, new Type[]{ typeof(string) }, null );
   if ( method == null )
      throw e_operation_failed.New(
         "Necessary static method “Parse” of type “{0}” is not found.\n"+
         "(Application settings property: “{1}”.)",
         typeof(T), propertyName );
   return
      method.Invoke( null, new object?[]{ valueString } );
}

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

volatile bool _readActualValues;
volatile Exception? _initializationException;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // ApplicationSettingsBase

} // user_rtx
#endif // !WASM
