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

namespace CommonHelpers {

//**************************************************************************************************
/// <summary>
/// </summary>
partial class HCmdLineParametersParser {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="InvalidOperationException" />
[HStrictResult]
public int ParameterCount
{
   get { return Parameters.Length; }
}

/// <exception cref="InvalidOperationException" />
[HStrictResult]
public int SwitchKeyCount
{
   get { return SwitchKeyDictionary.Count; }
}
/// <exception cref="InvalidOperationException" />
[HStrictResult]
public bool HasSwitchKeys
{
   get { return SwitchKeyCount > 0; }
}

/// <exception cref="InvalidOperationException" />
[HStrictResult]
public int UnnamedParamCount
{
   get { return UnnamedParameters.Length; }
}
/// <exception cref="InvalidOperationException" />
[HStrictResult]
public bool HasUnnamedParameters
{
   get { return UnnamedParameters.Length > 0; }
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
[HOptionalValue]
public HCmdLineParameter this[string key]
{
   get { return SwitchKeyDictionary[ key ]; }
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
public bool ContainsSwitchKey(string key)
{
   return SwitchKeyDictionary.ContainsKey( key );
}

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="KeyNotFoundException" />
[HStrictResult]
public HCmdLineParameter StrictGetParameter(string key)
{
   return SwitchKeyDictionary.StrictGetValue( key );
}

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="InvalidOperationException" />
[HOptionalResult]
public HCmdLineParameter GetParameterByIdx(int paramIdx)
{
   return Parameters[ paramIdx ];
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="KeyNotFoundException" />
[HOptionalResult]
public string[] GetSwitchKeyArguments(string key)
{
   var parameter = SwitchKeyDictionary.StrictGetValue( key );
   string[] arguments = parameter.NakedSwitchKeyArguments;
   return arguments != null ? HArray.Duplicate( arguments ) : null;
}

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="KeyNotFoundException" />
public int GetSwitchKeyArgumentCount(string key)
{
   var parameter = SwitchKeyDictionary.StrictGetValue( key );
   string[] arguments = parameter.NakedSwitchKeyArguments;
   return arguments != null ? arguments.Length : 0;
}
/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="KeyNotFoundException" />
[HOptionalResult]
public int GetSwitchKeyArgCountOrMinusOne(string key)
{
   var parameter = SwitchKeyDictionary.StrictGetValue( key );
   string[] arguments = parameter.NakedSwitchKeyArguments;
   return arguments != null ? arguments.Length : -1;
}

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="KeyNotFoundException" />
/// <exception cref="ArgumentOutOfRangeException" />
[HStrictResult]
public string GetSwitchKeyArgument(string key,int idx)
{
   return SwitchKeyDictionary.StrictGetValue( key )[ idx ];
}

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

/// <exception cref="InvalidOperationException" />
public string GetUnnamedParameter(int idx)
{
   return UnnamedParameters[ idx ];
}
/// <exception cref="InvalidOperationException" />
public string[] GetUnnamedParameters()
{
   return HArray.Duplicate( UnnamedParameters );
}

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

public int SwitchKeyDefinitionCount
{
   get { return SwitchKeyDefs.Count; }
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="KeyNotFoundException" />
public string GetKeyArgConcatenation(string key)
{
   return ConcatenateKeyArgs( StrictSwitchKeyArguments( key ) );
}

/// <exception cref="InvalidOperationException" />
public string[] GetKeyArray()
{
   return SwitchKeyDictionary.GetKeyArray();
}
/// <exception cref="InvalidOperationException" />
public string[] GetKeyArgConcatenations()
{
   var list = new List<string>();
   foreach (var parameter in SwitchKeyDictionary.GetValueArray())
      list.Add( ConcatenateKeyArgs( parameter.NakedSwitchKeyArguments ) );
   return list.ToArray();
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="InvalidOperationException" />
public void AddSwitchKeyDef(string switchKey)
{
   VerifyNoParseAttempt();
   SwitchKeyDefs[ switchKey ] = new HCmdLineKeyDefinition( switchKey );
}
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="InvalidOperationException" />
public void AddSwitchKeyDef(string switchKey,bool required)
{
   VerifyNoParseAttempt();
   SwitchKeyDefs[ switchKey ] = new HCmdLineKeyDefinition( switchKey, required );
}
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="InvalidOperationException" />
public void AddSwitchKeyDef(string switchKey,HCmdLineKeyArgumentation argumentation)
{
   VerifyNoParseAttempt();
   SwitchKeyDefs[ switchKey ] = new HCmdLineKeyDefinition( switchKey, false, argumentation );
}
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="InvalidOperationException" />
public void AddSwitchKeyDef(string switchKey,bool required,HCmdLineKeyArgumentation argumentation)
{
   VerifyNoParseAttempt();
   SwitchKeyDefs[ switchKey ] = new HCmdLineKeyDefinition( switchKey, required, argumentation );
}

/// <exception cref="ArgumentNullException" />
[HOptionalResult]
public HCmdLineKeyDefinition GetSwitchKeyDefinition(string switchKey)
{
   var keyDefinition = SwitchKeyDefs[ switchKey ];
   return keyDefinition != null ? keyDefinition.Duplicate() : null;
}
/// <exception cref="ArgumentNullException" />
/// <exception cref="KeyNotFoundException" />
[HStrictResult]
public HCmdLineKeyDefinition StrictGetSwitchKeyDefinition(string switchKey)
{
   return SwitchKeyDefs.StrictGetValue( switchKey );
}
public HSimpleDictionary<string,HCmdLineKeyDefinition> GetSwitchKeyDefinitions
(
   bool asStrictReadonly = true
)
{
   var newKeyDefs =
      new HSimpleDictionary<string,HCmdLineKeyDefinition>( SwitchKeyDefs.Comparer );
   foreach (var de in SwitchKeyDefs)
      newKeyDefs.Add( de.Key, de.Value.Duplicate() );
   return asStrictReadonly ? newKeyDefs.MakeStrictReadonly() : newKeyDefs;
}

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

/// <exception cref="ArgumentException" />
public static string ConcatenateKeyArgs([HOptional] params string[] nakedValues)
{
   if ( nakedValues == null )
      return "";
   HArgChecking.VerifyNotNulls( nakedValues );
   var sb = new StringBuilder();
   bool hasValues = false;
   foreach (string arg in nakedValues)
   {
      if ( hasValues ) sb.Append( "," );
      sb.Append( ParamQuoteCharacter );
      foreach (char ch in arg)
      {
         sb.Append( ch );
         if ( IsParamQuoteCharacter( ch ) )
            sb.Append( ch );
      }
      sb.Append( ParamQuoteCharacter );
      hasValues = true;
   }
   return sb.ToString();
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="KeyNotFoundException" />
[HStrictResult]
protected string[] StrictSwitchKeyArguments(string key)
{
   return SwitchKeyDictionary.StrictGetValue( key ).NakedSwitchKeyArguments;
}

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

/// <exception cref="HCmdLineParametersParseException" />
void ExamineKeys()
{
   _SwitchKeyDictionary_ = new HSimpleDictionary<string,HCmdLineParameter>( Options.KeyComparer );

   var unnamedParamList = new List<string>();

   foreach (var param in _Parameters_)
   {
      if ( !param.HasSwitchKey )
      {
         if ( !_Options_.AllowUnnamedParameters )
            throw new HCmdLineParametersParseException(
               "Unnamed parameters (without switch key) are not allowed.",
               param.RelativeStartPosition );
         unnamedParamList.Add( param.NakedValue );
         continue;
      }

      string switchKey = param.SwitchKey;
      var keyDefinition = SwitchKeyDefs[ switchKey ];
      if ( keyDefinition != null )
         switchKey = keyDefinition.SwitchKey;

      Action<string> raiseSwitchKeyError = delegate (string messageBase)
      {
         throw HCmdLineParametersParseExceptionCreator.Create( param.RelativeStartPosition,
            "{0}: \"{1}\" (key name).{2}", messageBase, param.SwitchKeyWithSign,
            Options.UseCaseInsensitiveKeys ?
               "\n(Case insensitive option was used for parse operation.)" : "" );
      };

      if ( _SwitchKeyDictionary_.ContainsKey( switchKey ) )
         raiseSwitchKeyError( "Repeated switch key in command line arguments" );
      if ( !Options.IgnoreSwitchKeyDefinitions )
      {
         if ( keyDefinition == null )
            raiseSwitchKeyError( "Unknown switch key in command line arguments" );
         if ( keyDefinition.Argumentation == HCmdLineKeyArgumentation.None )
         {
            if ( param.HasArgumentationSign ) {
               raiseSwitchKeyError( "No argument can be specified for switch key" ); }
         }
         else
         {
            switch ( keyDefinition.Argumentation )
            {
            case HCmdLineKeyArgumentation.Optional:
               if ( param.SwitchKeyArgumentCount > 1 )
                  raiseSwitchKeyError( "Not more than one switch key argument was expected for" );
               break;
            case HCmdLineKeyArgumentation.Single:
               if ( param.SwitchKeyArgumentCount != 1 )
                  raiseSwitchKeyError( "Single switch key argument was expected for" );
               break;
            case HCmdLineKeyArgumentation.Multiple:
               if ( param.SwitchKeyArgumentCount == 0 )
                  raiseSwitchKeyError( "One or more switch key arguments were expected for" );
               break;
            }
         }
      }
      _SwitchKeyDictionary_.Add( switchKey, param );
   }

   foreach (var keyDef in SwitchKeyDefs.ToList())
      if ( keyDef.Value.Required && !_SwitchKeyDictionary_.ContainsKey( keyDef.Key ) )
         throw HCmdLineParametersParseExceptionCreator.Create(
            "Required switch is not present in command line arguments: \"/{0}\" (key name).",
            keyDef.Key );

   _SwitchKeyDictionary_.MakeStrictReadonly();
   _UnnamedParameters_ = unnamedParamList.ToArray();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HCmdLineParametersParser
//**************************************************************************************************

} // CommonHelpers
