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

namespace CommonHelpers {

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

public const char
   ParamQuoteCharacter        = '"',
   SwitchCharacterSlash       = '/',
   SwitchCharacterHyphen      = '-',
   SwitchKeyFromArgsSeparator = ':',
   SwitchKeyArgumentsSeparator= ',';

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

public bool ParseAttempt { get; private set;  }
public bool HasParsed { get; private set;  }

/// <exception cref="InvalidOperationException" />
[HStrictResult]
public string LineOfParameters
{
   get { VerifyParsed(); return _LineOfParameters_; }
}

/// <exception cref="InvalidOperationException" />
[HStrictResult][HOptionalValue]
public string FullCommandLine
{
   get { VerifyParsed(); return _FullCommandLine_; }
}

/// <exception cref="InvalidOperationException" />
[HStrictResult][HOptionalValue]
public string ExeModulePath
{
   get { VerifyParsed(); return _ExeModulePath_; }
}

/// <exception cref="InvalidOperationException" />
[HStrictResult]
public bool IsFullCommandLine
{
   get { VerifyParsed(); return _IsFullCommandLine_; }
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="IndexOutOfRangeException" />
[HStrictResult]
public HCmdLineParameter this[int idx]
{
   get { return Parameters[ idx ]; }
}

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

public HCmdLineParametersParser(): this( null )
{}

public HCmdLineParametersParser([HOptional] HCmdLineParametersParseOptions options)
{
   _Options_ = options ?? new HCmdLineParametersParseOptions();
   _SwitchKeyDefs_ = new HSimpleDictionary<string,HCmdLineKeyDefinition>( Options.KeyComparer );
}

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

/// <exception cref="ArgumentException" />
/// <exception cref="InvalidOperationException" />
public HCmdLineParametersParseOptions GetOptions()
{
   return Options.Duplicate();
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="HCmdLineParametersParseException" />
public void Parse(string lineOfParameters)
{
   Parse( lineOfParameters, false );
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="HCmdLineParametersParseException" />
public void Parse(string lineOfParameters,bool isFullCommandLine)
{
   ParseAttempt = true;

   HArgChecking.VerifyNotNull( lineOfParameters );

   HasParsed = false;

   _LineOfParameters_ = _FullCommandLine_ = _ExeModulePath_ = null;
   _IsFullCommandLine_ = false;
   _Parameters_ = null;
   _SwitchKeyDictionary_ = null;
   _UnnamedParameters_ = null;

   HString.TrimEnd( ref lineOfParameters );

   int pos = 0;
   if ((_IsFullCommandLine_ = isFullCommandLine))
   {
      if ( lineOfParameters == "" )
         throw new HCmdLineParametersParseException(
            "Absent executable module path in full command line." );
      bool inDQuotes = IsDQuoteChar( lineOfParameters[ 0 ] );
      int len = lineOfParameters.Length;
      if ( inDQuotes ) pos++;
      while ( pos < len )
      {
         char ch = lineOfParameters[ pos ];
         if ( inDQuotes && IsDQuoteChar( ch ) )
         {
            if ( ++pos < len && IsDQuoteChar( lineOfParameters[ pos ] ) )
               throw new HCmdLineParametersParseException(
                  "Unexpected repeated double quote character in part of executable module path.",
                  pos, true );
            break;
         }
         if ( !inDQuotes && char.IsWhiteSpace( ch ) )
            break;
         if ( inDQuotes && pos == len-1 )
            throw new HCmdLineParametersParseException(
               "Unclosed double quotation mark in executable module path.",
               pos, true );
         pos++;
      }
      _FullCommandLine_ = lineOfParameters;
      _ExeModulePath_ = HString.UndecorateEmbracement(
         lineOfParameters.Substring( 0, pos ), '"' );
      HString.SkipWhiteSpaces( lineOfParameters, ref pos );
      _LineOfParameters_ = lineOfParameters.Substring( pos );
   }
   else
      _LineOfParameters_ = lineOfParameters;

   _Parameters_ = new CmdLineParametersScan( this, _LineOfParameters_, pos ).ExtractParameters();

   ExamineKeys();

   HasParsed = true;
}

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

/// <exception cref="HCmdLineParametersParseException" />
[MethodImpl(MethodImplOptions.NoInlining)]
public void ParseProcessCommandLine()
{
   Parse( Environment.CommandLine, true );
}

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

/// <exception cref="InvalidOperationException" />
public string[] GetNakedParameters()
{
   VerifyParsed();
   var list = new List<string>();
   foreach (var parameter in Parameters)
      list.Add( parameter.NakedValue );
   return list.ToArray();
}

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

/// <exception cref="InvalidOperationException" />
public void VerifyParsed()
{
   if ( !HasParsed )
      throw new InvalidOperationException(
         "Command line arguments have not been parsed." );
}
/// <exception cref="InvalidOperationException" />
public void VerifyNoParseAttempt()
{
   if ( HasParsed )
      throw new InvalidOperationException(
         "The operation may be called only before any attempt of command line arguments parse." );
}

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

public static bool IsParamQuoteCharacter(char ch)
{
   return ch == ParamQuoteCharacter;
}

public static bool IsSwitchCharacter(char ch)
{
   return IsSwitchCharacterSlash( ch ) || IsSwitchCharacterHyphen( ch );
}
public static bool IsSwitchCharacterSlash(char ch)
{
   return ch == SwitchCharacterSlash;
}
public static bool IsSwitchCharacterHyphen(char ch)
{
   return ch == SwitchCharacterHyphen;
}

public static bool IsSwitchKeyFromArgsSeparator(char ch)
{
   return ch == SwitchKeyFromArgsSeparator;
}
public static bool IsSwitchKeyArgumentsSeparator(char ch)
{
   return ch == SwitchKeyArgumentsSeparator;
}

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

protected HCmdLineParametersParseOptions Options
{
   get { return _Options_; }
}

protected HSimpleDictionary<string,HCmdLineKeyDefinition> SwitchKeyDefs
{
   get { return _SwitchKeyDefs_; }
}

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

/// <exception cref="InvalidOperationException" />
[HStrictResult]
protected HCmdLineParameter[] Parameters
{
   get { VerifyParsed(); return _Parameters_; }
}

/// <exception cref="InvalidOperationException" />
[HStrictResult][HReadonlyObject]
protected HSimpleDictionary<string,HCmdLineParameter> SwitchKeyDictionary
{
   get { VerifyParsed(); return _SwitchKeyDictionary_; }
}

/// <exception cref="InvalidOperationException" />
[HStrictResult]
protected string[] UnnamedParameters
{
   get { VerifyParsed(); return _UnnamedParameters_; }
}

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

static HCmdLineParametersParser()
{
   CommonHelpers_Plus.Access();
}

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

static bool IsDQuoteChar(char ch)
{
   return ch == '"';
}

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

readonly HCmdLineParametersParseOptions
   _Options_;
readonly HSimpleDictionary<string,HCmdLineKeyDefinition>
   _SwitchKeyDefs_;

string
   _LineOfParameters_,
   _FullCommandLine_,
   _ExeModulePath_;
bool
   _IsFullCommandLine_;
HCmdLineParameter[]
   _Parameters_;
HSimpleDictionary<string,HCmdLineParameter>
   _SwitchKeyDictionary_;
string[]
   _UnnamedParameters_;

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

} // CommonHelpers
