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

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

/// <summary>
/// Roman numeral formatting
/// </summary>
public static class roman_num {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public const int
   MinSupportedNumber = 1,
   MaxSupportedNumber = 100;

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

public static object SyncRoot { get; } = new object();

public static Dictionary<int,string>
   DictionaryUnsafe => _dictionary;

public static NumeralFormattingFN? FallbackFormattingRoutine
{
   get { lock (SyncRoot) return _fallbackFormattingRoutine; }
   set { lock (SyncRoot) _fallbackFormattingRoutine = value; }
}

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

public static bool IsSupportedNumber( int number ) =>
   u.val.IsBetween( number, MinSupportedNumber, MaxSupportedNumber );

/// <exception cref="ArgumentOutOfRangeException" />
public static bool IsRegisteredNumber( [u.positive] int number )
{
   lock ( SyncRoot ) {
      string? strValue;
      return
         _dictionary.TryGetValue( VerifyNumber( number ), out strValue ) &&
         strValue != null;
   }
}

/// <exception cref="ArgumentOutOfRangeException" />
[u.needs_in_lock]
public static void RegisterNumber_NoLock( [u.positive] int number, string strValue ) =>
   _dictionary[ VerifyNumber( number ) ] = strValue;

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

/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="EElementNotFound" />
public static string Format( [u.positive] int number, bool useFormattingFallback=true )
{
   VerifyNumber( number );
   NumeralFormattingFN? fallbackRoutine = null;
   lock ( SyncRoot ) {
      string? strValue;
      if ( _dictionary.TryGetValue( number, out strValue ) )
         return strValue;
      if ( useFormattingFallback )
         fallbackRoutine = FallbackFormattingRoutine;
   }
   if ( useFormattingFallback )
      return fallbackRoutine != null ?
         fallbackRoutine( number ) :
         string.Format( "<{0}>", u.int32.ToNormalStr( number ) );
   throw new EElementNotFound(
      $"Can not format value {number} to Roman number because the number is not found "+
      $"and it was not specially registered." );
}

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

static roman_num()
{
   var supportedNumerals = new string[ MaxSupportedNumber - MinSupportedNumber + 1 ] {
"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
"XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX",
"XXI", "XXII", "XXIII", "XXIV", "XXV",
"XXVI", "XXVII", "XXVIII", "XXIX", "XXX",
"XXXI", "XXXII", "XXXIII", "XXXIV", "XXXV", "XXXVI", "XXXVII", "XXXVIII", "XXXIX", "XL",
"XLI", "XLII", "XLIII", "XLIV", "XLV", "XLVI", "XLVII", "XLVIII", "XLIX", "L",
"LI", "LII", "LIII", "LIV", "LV", "LVI", "LVII", "LVIII", "LIX", "LX",
"LXI", "LXII", "LXIII", "LXIV", "LXV", "LXVI", "LXVII", "LXVIII", "LXIX", "LXX",
"LXXI", "LXXII", "LXXIII", "LXXIV", "LXXV",
"LXXVI", "LXXVII", "LXXVIII", "LXXIX", "LXXX",
"LXXXI", "LXXXII", "LXXXIII", "LXXXIV", "LXXXV", "LXXXVI", "LXXXVII", "LXXXVIII", "LXXXIX", "XC",
"XCI", "XCII", "XCIII", "XCIV", "XCV", "XCVI", "XCVII", "XCVIII", "XCIX", "C"
   };
   for ( int i=0; i<supportedNumerals.Length; i++ )
      _dictionary[ i+1 ] = supportedNumerals[ i ];
}

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

/// <exception cref="ArgumentOutOfRangeException" />
public static int VerifyNumber( [u.positive] int number ) =>
   u.arg_check.Positive( number, nameof(number) );

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

static readonly Dictionary<int,string>
   _dictionary = new ();

static NumeralFormattingFN?
   _fallbackFormattingRoutine;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // roman_num

} // user_rtx (.wasm)
