//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Globalization;

#if !WASM
namespace CommonHelpers {
#else
namespace CommonHelpers.WASM {
#endif

//**************************************************************************************************
/// <summary>
/// </summary>
[Flags]
public enum HHexParseOptions
{
   None                 = 0,
   AllowPrefix          = 1 << 0,
   RequirePrefix        = 1 << 1,
   RequireRegularLength = 1 << 2,
   RequireFullLength    = 1 << 3
}
//**************************************************************************************************


//**************************************************************************************************
/// <summary>
/// </summary>
public static partial class HHexParser {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="HParseValueException" />
public static T Parse<T>(string src) where T: struct
   { return Parse<T>(src,HHexParseOptions.None); }
/// <exception cref="HParseValueException" />
public static T Parse<T>(string src,T def) where T: struct
   { return Parse<T>(src,HHexParseOptions.None,def); }

/// <exception cref="HParseValueException" />
public static T Parse<T>(string src,HHexParseOptions options) where T: struct
   { return ParseEx(src,options,(T?)null); }
/// <exception cref="HParseValueException" />
public static T Parse<T>(string src,HHexParseOptions options,T def) where T: struct
   { return ParseEx(src,options,(T?)def); }

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

public static bool TryParse<T>(string src,out T dst) where T: struct
   { return TryParse(src,HHexParseOptions.None,out dst); }
public static bool TryParse<T>(string src,T def,out T dst) where T: struct
   { return TryParse(src,HHexParseOptions.None,def,out dst); }

public static bool TryParse<T>(string src,out T dst,out HParseValueException e) where T: struct
   { return TryParse(src,HHexParseOptions.None,out dst,out e); }
public static bool TryParse<T>( string src, T def,
                                out T dst, out HParseValueException e ) where T: struct
   { return TryParse(src,HHexParseOptions.None,def,out dst,out e); }

public static bool TryParse<T>(string src,HHexParseOptions options,out T dst) where T: struct
   { HParseValueException e; return TryParse(src,options,out dst,out e); }
public static bool TryParse<T>( string src, HHexParseOptions options, T def,
                                out T dst ) where T: struct
   { HParseValueException e; return TryParse(src,options,def,out dst,out e); }

public static bool TryParse<T>( string src, HHexParseOptions options,
                                out T dst, out HParseValueException e ) where T: struct
{
   try
      { dst=Parse<T>(src,options);}
   catch (HParseValueException x)
      { dst=default(T); e=x; return false; }
   e=null; return true;
}
public static bool TryParse<T>( string src, HHexParseOptions options, T def,
                                out T dst, out HParseValueException e ) where T: struct
{
   try
      { dst=Parse<T>(src,options,def); }
   catch (HParseValueException x)
      { dst=default(T); e=x; return false; }
   e=null; return true;
}

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

public static T ParseEx<T>(string src,HHexParseOptions options,T? def) where T: struct
{
   Type type = typeof(T);

   if (!HType.IsStdUnsignedNumber(type) && type!=typeof(UIntPtr))
      throw HGenericTypeExceptionCreator.Create(
         "Hexadecimal parse is supported only for following types: "+
         "\"{0}\", \"{1}\", \"{2}\", \"{3}\", \"{4}\", \"{5}\", \"{6}\", \"{7}\".",
         typeof(Byte),  typeof(UInt16), typeof(UInt32), typeof(UInt64),
         typeof(ushort), typeof(uint), typeof(ulong), typeof(UIntPtr) );

   if (type==typeof(UIntPtr))
      return (T)(object)new UIntPtr
      (
         HPlatform.Is32BitPlatform ?
            ParseEx<UInt32>( src, options, def!=null ?
               (UInt32?)((UIntPtr)(object)def.Value).ToUInt32() : null ) :
            ParseEx<UInt64>( src, options, def!=null ?
               (UInt64?)((UIntPtr)(object)def.Value).ToUInt64() : null )
      );

   HArgChecking.VerifyNotNull( src );

   src = src.Trim();

   if (src=="" && def.HasValue)
      return def.Value;

   if (src.StartsWith("0x",StringComparison.OrdinalIgnoreCase))
   {
      if ((options & (HHexParseOptions.AllowPrefix | HHexParseOptions.RequirePrefix))==0)
         throw new HParseValueException(
            "Hexadecimal prefix is not allowed." );
      src = src.Remove(0,2);
      if (src=="" || src.StartsWith("0x",StringComparison.OrdinalIgnoreCase))
         throw new HParseValueException(
            "Incorrect hexadecimal number." );
   }
   else
      if ((options & HHexParseOptions.RequirePrefix)!=0)
         throw new HParseValueException(
            "Hexadecimal prefix is required." );

   int length = src.Trim().Length;

   if (length==0)
      throw new HParseValueException(
         "Value string is empty." );

   if ( (options & HHexParseOptions.RequireRegularLength)!=0 &&
        !HBinExp.IsRegular( length ) )
      throw new HParseValueException(
         "Regular string length is required for hexadecimal number." );

   if ( (options & HHexParseOptions.RequireFullLength)!=0 &&
        !( type==typeof(Byte) && length==2  ||
           HOps.IsOneOf( type, typeof(ushort), typeof(UInt16) ) && length==4  ||
           HOps.IsOneOf( type, typeof(uint)  , typeof(UInt32) ) && length==8  ||
           HOps.IsOneOf( type, typeof(ulong) , typeof(UInt64) ) && length==16 ||
           type==typeof(UIntPtr) && length==UIntPtr.Size*2 ) )
      throw new HParseValueException(
         "Full string length value is required for hexadecimal number to parse it." );

   return HValueParser.ParseEx<T>( src,
      new object[]{ NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo },
      def );
}

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

static HHexParser()
{
   CommonHelpers_Core.Access();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HHexParser
//**************************************************************************************************

} // CommonHelpers (.WASM)
