//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.IO;
using System.Text;
#if LEGACY
using System.Security.Permissions;
#endif // LEGACY
using System.ComponentModel;

namespace CommonHelpers {

//**************************************************************************************************
/// <summary>
/// </summary>
[HThreadSafe]
#if LEGACY
[PermissionSet(SecurityAction.Demand,Unrestricted=true)]
#endif // LEGACY
public partial class HConsole {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public bool IsOnStdOutput
{
   get { lock (_InstanceSyncRoot_) return _IsOnStdOutput_; }
}

public bool IsInNativeVisualMode
{
   get { lock (_InstanceSyncRoot_) return _IsInNativeVisualMode_; }
}

public bool IsInStdOutputBackground
{
   get { return IsOnStdOutput && !IsInNativeVisualMode; }
}

public bool ReliesOnProxy
{
   get { lock (_InstanceSyncRoot_) return _ProxySettings_!=null; }
}

public bool IsInTextWriterMode
{
   get { lock (_InstanceSyncRoot_) return _TextWriter_!=null; }
}

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

public TextWriter TextWriter
{
   get { lock (_InstanceSyncRoot_) return _TextWriter_; }
}

#if WIN32
/// <exception cref="InvalidOperationException" />
/// <exception cref="HOperationFailedException" />
public IntPtr WindowHandle
{
   get
   {
      VerifyInNativeVisualMode();
      IntPtr handle = Win32_Console.GetConsoleWindow();
      if (handle==IntPtr.Zero)
         throw HWin32Error.CreateOpFailedException( "GetConsoleWindow" );
      return handle;
   }
}
#endif // WIN32

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

public int Width
{
   get
   {
      lock (_InstanceSyncRoot_)
         return IsInNativeVisualMode ? _InitialWidth_ : NormalWidth;
   }
}
public int NormLimitedWidth
{
   get
   {
      lock (_InstanceSyncRoot_)
         return HArith.Min( Width, NormalWidth );      
   }
}

public int Height
{
   get
   {
      lock (_InstanceSyncRoot_)
         return IsInNativeVisualMode ? Console.BufferHeight : NormalHeight;
   }
}

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

public int CursorLeft_Calculated
{
   get { lock (_InstanceSyncRoot_) return _CursorLeft_Calculated_; }
   set { lock (_InstanceSyncRoot_) _CursorLeft_Calculated_ = value; }
}

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

public bool PendingLineFeed
{
   get { lock ( _InstanceSyncRoot_ ) return _PendingNewLine_; }
}

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

/// <exception cref="InvalidOperationException" />
public ConsoleColor ForegroundColor
{
   get
   {
      lock ( _InstanceSyncRoot_ )
      {
         VerifyNotInTextWriterMode();
         if ( IsOnStdOutput )
         {
            VerifyNotInStdOutputBackground();
            return Console.ForegroundColor;
         }
         if ( ReliesOnProxy )
            return _ProxyForegroundColor_;
         throw HAssertionFailure.DEAD_BRANCH();
      }
   }
}

/// <exception cref="InvalidOperationException" />
public ConsoleColor InitialParentBackgroundColor
{
   get
   {
      lock ( _InstanceSyncRoot_ )
      {
         VerifyInNativeVisualMode();
         return _InitialParentBackgroundColor_;
      }
   }
   set
   {
      lock ( _InstanceSyncRoot_ )
      {
         VerifyInNativeVisualMode();
         Console.BackgroundColor = _InitialParentBackgroundColor_ = value;
      }
   }
}

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

/// <exception cref="Win32Exception" />
/// <exception cref="HOperationFailedException" />
public static HConsole StdConIO
{
   get
   {
      lock (_StaticSyncRoot_)
      {
         InitializeStdAppConWrapper();
         return _StdConIO_;
      }
   }
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="HNullValueException" />
public HConsole(HProxyConsoleSettings proxySettings)
{
   HArgChecking.VerifyNotNull( proxySettings );
   if ( proxySettings.WriteCallback==null || proxySettings.WriteLineCallback==null )
      throw new HNullValueException();
   lock ( _InstanceSyncRoot_ )
   {
      _ProxySettings_ = proxySettings;
      _ProxyForegroundColor_ = proxySettings.ForegroundColor;
      CorrectProperties();
   }
}

/// <exception cref="ArgumentNullException" />
public HConsole(TextWriter writer)
{
   HArgChecking.VerifyNotNull( writer );
   lock ( _InstanceSyncRoot_ )
   {
      _TextWriter_ = writer;
      CorrectProperties();
   }
}

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

public HProxyConsoleSettings GetProxySettings()
{
   lock ( _InstanceSyncRoot_ ) { VerifyReliesOnProxy(); return _ProxySettings_.Duplicate(); }
}

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

/// <exception cref="Win32Exception" />
public static void InitializeStdAppConWrapper()
{
   lock (_StaticSyncRoot_)
   {
      if (_StdConIO_==null)
         _StdConIO_=new HConsole();
   }
}

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

/// <exception cref="InvalidOperationException" />
public void VerifyOnStdAppConsole()
{
   if (!IsOnStdOutput)
      throw InvOpException_NotOnStdAppConsole;
}

/// <exception cref="InvalidOperationException" />
public void VerifyInNativeVisualMode()
{
   if (!IsInNativeVisualMode)
      throw new InvalidOperationException(
         "Requested console operation requires visual mode." );
}

/// <exception cref="InvalidOperationException" />
public void VerifyReliesOnProxy()
{
   if (!ReliesOnProxy)
      throw new InvalidOperationException(
         "Requested console operation requires proxy mode." );
}

/// <exception cref="InvalidOperationException" />
public void VerifyInTextWriterMode()
{
   if (!IsInTextWriterMode)
      throw new InvalidOperationException(
         "Requested console operation requires text writer mode." );
}

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

/// <exception cref="InvalidOperationException" />
public void VerifyNotInStdOutputBackground()
{
   if (IsInStdOutputBackground)
      throw new InvalidOperationException(
         "Requested console operation is not supported in std-output background." );
}

/// <exception cref="InvalidOperationException" />
public void VerifyNotInTextWriterMode()
{
   if (IsInTextWriterMode)
      throw new InvalidOperationException(
         "Requested console operation is not supported in text writer mode." );
}

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

public void SetCursorLeftAndCalculated(int cursorLeft)
{
   if ( IsInNativeVisualMode )
      Console.CursorLeft = cursorLeft;
   CursorLeft_Calculated = cursorLeft;
}

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

#if WIN32
/// <exception cref="Win32Exception" />
public bool DeleteSystemMenuCloseItem()
{
   if ( !IsInNativeVisualMode )
      return false;
   IntPtr hMenu = Win32_Console.GetSystemMenu(
      WindowHandle, false );
   if ( hMenu == IntPtr.Zero)
      return false;
   if ( !Win32_Console.DeleteMenu( hMenu, Win32_Console.SC_CLOSE, Win32_Console.MF_BYCOMMAND ) )
   {
      if ( HWin32Error.LastErrorCode == 0 )
         return false;
      throw HWin32Error.CreateExceptionFromLastError();
   }
   return true;
}
#endif // WIN32

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

internal ConsoleColor ProxyForegroundColor
{
   get
   {
      lock ( _InstanceSyncRoot_ )
         return _ProxyForegroundColor_;
   }
   set
   {
      lock ( _InstanceSyncRoot_ )
         _ProxyForegroundColor_ = value;
   }
}

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

/// <exception cref="Win32Exception" />
/// <exception cref="HOperationFailedException" />
HConsole()
{
   bool isInNativeVisualMode = false;
#if WIN32
   IntPtr hStdOutput =
      Win32_Console.GetStdHandle( Win32_Console.STD_OUTPUT_HANDLE );
   if ( hStdOutput == Win32_Console.INVALID_HANDLE_VALUE )
      throw HWin32Error.CreateExceptionFromLastError();
   Win32_Console.CONSOLE_SCREEN_BUFFER_INFO
      screenBufferInfo;
   if ( Win32_Console.GetConsoleScreenBufferInfo( hStdOutput, out screenBufferInfo ) )
      isInNativeVisualMode = true;
   else
      if ( HWin32Error.LastErrorCode != Win32_Console.ERROR_INVALID_HANDLE )
         throw HWin32Error.CreateExceptionFromLastError();
#else // !WIN32:
   int? cursorLeft = null;
   try{ cursorLeft = Console.CursorLeft; }catch{}
   isInNativeVisualMode = cursorLeft.HasValue;
#endif // !WIN32
   lock ( _InstanceSyncRoot_ )
   {
      if ( isInNativeVisualMode )
      {
         _IsInNativeVisualMode_ = true;
         _InitialWidth_ = Console.BufferWidth - 1;
         if ( _InitialWidth_ <= 0 )
            throw new HOperationFailedException(
               "Console screen buffer width must not be less than 1." );
         _InitialParentBackgroundColor_ = Console.BackgroundColor;
         bool avoidSpecialInputBackground = !HCompilationCH.IsDotNetLegacy;
#if LEGACY
         avoidSpecialInputBackground = HOperatingSystem.IsProbablyWindows10orLater;
#endif // LEGACY
         if ( avoidSpecialInputBackground )
            _InputBackgroundColor_ = _InitialParentBackgroundColor_;
      }
      _IsOnStdOutput_ = true;
      CorrectProperties();
   }
}

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

static HConsole()
{
   CommonHelpers_Console.Access();
}

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

static InvalidOperationException InvOpException_NotOnStdAppConsole
{
   get
   {
      return new InvalidOperationException(
         "Requested operation requires standard application console." );
   }
}

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

void CorrectProperties()
{
   if ( IsInTextWriterMode )
   {
      BulletChar = HUnicode.Bullet;
      HeaderFooterChar = HUnicode.LightShade;
   }
   if ( !IsInNativeVisualMode )
      OperationFormatString = OperationFormatString.TrimEnd();
}

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

readonly object
   _InstanceSyncRoot_ = new object();
readonly bool
   _IsInNativeVisualMode_;
readonly TextWriter
   _TextWriter_;
readonly HProxyConsoleSettings
   _ProxySettings_;
volatile bool
   _IsOnStdOutput_;
bool
   _PendingNewLine_,
   _IgnoreFlushPendingNewLine_;
/*
//#pragma warning disable 0414
   _IsInStringInputRoutine_;
//#pragma warning restore 0414
*/
int
   _InitialWidth_,
   _CursorLeft_Calculated_;
ConsoleColor
   _InitialParentBackgroundColor_,
   _ProxyForegroundColor_;

static HConsole
   _StdConIO_;
static readonly object
   _StaticSyncRoot_ = new object();

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

} // CommonHelpers
