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

namespace CommonHelpers {

//**************************************************************************************************
partial class HConsole {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

enum InputKey { Other, NullChar, PrintableChar, Backspace, ClearInput, AcceptInput, CancelInput }

//**************************************************************************************************

string StringInputRoutine( HConsoleColorRetention colorRetention,
                           [HOptional] string initialString,
                           HConsoleInputOptions options,
                           [HOptional] HValidateConsoleInputCallback validationCallback )
{/*_IsInStringInputRoutine_=true;try{*/
   FlushPendingNewLine();

   if ( Console.CursorLeft >= Console.WindowWidth-1 )
      Print();
   int
      startPos = Console.CursorLeft,
      maxPos = Console.WindowWidth-1;
   var
      sb = new StringBuilder();

   int maxLen = HArith.ConfinePositiveOrZero( maxPos - startPos );

   if (initialString!=null)
   {
      string str =
         StringToPrintable( initialString.Substring( 0,
            Math.Min( initialString.Length, maxLen ) ));
      sb.Append(str);
      OutputDefault( (options & HConsoleInputOptions.PasswordMasking)==0 ?
         str : new string(InputPasswordChar,str.Length) );
   }

   ResetInput();

   for (;;)
   {
      InputKey inputKey;
      char printableChar;
      int savedPos;

      inputKey = ReadInputKey(out printableChar);

      int cursorLeft = Console.CursorLeft;
      if ( cursorLeft != CursorLeft_Calculated &&
           !HOps.IsOneOf( inputKey, InputKey.NullChar, InputKey.CancelInput ) )
         inputKey = InputKey.Other;

      switch (inputKey)
      {
      case InputKey.PrintableChar:
         if ( sb.Length == maxLen || cursorLeft >= maxPos )
            goto default;
         OutputDefault(
            (options & HConsoleInputOptions.PasswordMasking)!=0 ?
            InputPasswordChar : printableChar );
         sb.Append(printableChar);
         break;
      case InputKey.NullChar:
         break;
      case InputKey.Backspace:
         savedPos = cursorLeft;
         if ( sb.Length == 0 || savedPos <= startPos )
            goto default;
         SetCursorLeftAndCalculated( savedPos - 1 );
         using (new HConsoleColorRetention(this,null,colorRetention.PreviousBackgroundColor))
            OutputDefault(" ");
         SetCursorLeftAndCalculated( savedPos - 1 );
         HStringBuilder.RemoveLastChar( sb );
         break;
      case InputKey.ClearInput:
         savedPos = cursorLeft;
         if ( sb.Length == 0 || savedPos <= startPos )
            goto default;
         SetCursorLeftAndCalculated( startPos );
         using (new HConsoleColorRetention(this,null,colorRetention.PreviousBackgroundColor))
            OutputDefault(new string(' ',savedPos-startPos));
         SetCursorLeftAndCalculated( startPos );
         sb.Length = 0;
         break;
      case InputKey.AcceptInput:
         string str=sb.ToString();
         if ( (options & HConsoleInputOptions.RequireNonEmpty)!=0 && sb.Length==0 ||
              validationCallback!=null && !validationCallback(str) )
            goto default;
         return str;
      case InputKey.CancelInput:
         if ((options & HConsoleInputOptions.DisallowCancellation)!=0)
            goto default;
         return null;
      default:
         PlayDisabledSound();
         ResetInput();
         break;
      }
   }
/*}finally{_IsInStringInputRoutine_=false;}*/}

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

InputKey ReadInputKey(out char printableChar)
{
   var keyInfo = Console.ReadKey(true);
   var modifiers = keyInfo.Modifiers;

   printableChar = '\0';

   switch (keyInfo.Key)
   {
   case ConsoleKey.Backspace:
      if (modifiers==0)
         return InputKey.Backspace;
      if (modifiers==ConsoleModifiers.Control)
         return InputKey.ClearInput;
      break;
   case ConsoleKey.Enter:
      if (modifiers==0)
         return InputKey.AcceptInput;
      break;
   case ConsoleKey.Escape:
      if (modifiers==0)
         return InputKey.CancelInput;
      break;
   }

   char ch = keyInfo.KeyChar;
   //HDebug.PrintInfo("CharCode = 0x{0:X2}",(ushort)ch);

   if ( ch == '\0' )
      return InputKey.NullChar;

   if ( Environment.Version.Major < 4 )
   {
      //if ( ch == '?' && !(
      //     keyInfo.Key == ConsoleKey.Oem2 &&
      //     keyInfo.Modifiers == ConsoleModifiers.Shift ) )
      //   return InputKey.Other;
      try
         { ch = Console.InputEncoding.GetString( BitConverter.GetBytes( ch ) )[ 0 ]; }
      catch ( DecoderFallbackException )
         { return InputKey.Other; }
   }

   if ( IsCharPrintable(ch,false) )
   {
      printableChar = ch;
      return InputKey.PrintableChar;
   }

   return InputKey.Other;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HConsole
//**************************************************************************************************

} // CommonHelpers
