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

namespace CommonHelpers {

//**************************************************************************************************
public partial class HShellLinkProperties: HDisposable, ISysHelpers_ShellLinkProperties {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public string Arguments {
   get { return Impl.Arguments; }
   set { Impl.Arguments = value; }}
/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public string Description {
   get { return Impl.Description; }
   set { Impl.Description = value; }}
/// <exception cref="HOperationFailedException" />
public ushort Hotkey {
   get { return Impl.Hotkey; }
   set { Impl.Hotkey = value; }}
/// <exception cref="HOperationFailedException" />
public short Hotkey_AsInt16 {
   get { unchecked{ return (short)Hotkey; }}
   set { unchecked{ Hotkey = (ushort)value; }}}
/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public string Path {
   get { return Impl.Path; }
   set { ObstructEnvironment( value, true ); Impl.Path = value; }}
/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public string DisplayName {
   get { return Impl.DisplayName; }
   set { Impl.DisplayName = value; }}
/// <exception cref="HOperationFailedException" />
public int ShowCmd {
   get { return Impl.ShowCmd; }
   set { Impl.ShowCmd = value; }}
/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public string WorkingDirectory {
   get { return Impl.WorkingDirectory; }
   set { ObstructEnvironment( value ); Impl.WorkingDirectory = value; }}

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

/// <exception cref="HOperationFailedException" />
public string GetIconLocation(out int index)
   { return Impl.GetIconLocation( out index ); }
/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public void SetIconLocation(string path,int index)
   { ObstructEnvironment( path ); Impl.SetIconLocation( path, index ); }

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

public bool UseEnvironmentObstruction
{
   get { return _UseEnvironmentObstruction_; }
}

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

/// <exception cref="PlatformNotSupportedException" />
/// <exception cref="HOperationFailedException" />
public HShellLinkProperties()
{
   FFM.Access();
   _Impl_ = (ISysHelpers_ShellLinkProperties)HType.CreateInstance(
      CommonHelpersLibrary.GetOrLoadCommonHelpersAssembly_CPP().GetType(
         "CommonHelpers.SysHelpers_ShellLinkProperties", true ) );
   ValidateDisposable();
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public void Load(string filePath)
{
   HShellLinkSpecialProps specialProps;
   Load( filePath, out specialProps );
}
/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public void Load(string filePath,out HShellLinkSpecialProps specialProps)
{
   VerifyValidDisposable();
   Impl.Load( filePath, out specialProps );
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public void Save(string filePath)
{
   Save( filePath, null );
}
/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public void Save(string filePath,[HOptional] HShellLinkSpecialProps specialProps)
{
   Impl.Save( filePath, specialProps );
}

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

[HOptionalResult]
[HReadonlyObject]
public HSimpleDictionary<string,string> GetEnvironmentRollback()
{
   VerifyValidDisposable();
   return _EnvironmentRollback_ != null ?
      _EnvironmentRollback_.GetShallowReadonlyInstance() : null;
}

/// <exception cref="InvalidOperationException" />
/// <exception cref="HOperationFailedException" />
public void BeginEnvironmentObstruction()
{
   BeginEnvironmentObstruction( false );
}
/// <exception cref="InvalidOperationException" />
/// <exception cref="HOperationFailedException" />
public void BeginEnvironmentObstruction(bool processPathProperties)
{
   VerifyValidDisposable();
   if ( _EnvironmentRollback_ != null )
      return;
   _UseEnvironmentObstruction_ = true;
   if ( processPathProperties )
   {
      Path = HString.NullAsEmpty( Path );
      WorkingDirectory = HString.NullAsEmpty( WorkingDirectory );
      int iconIdx;
      SetIconLocation(
         HString.NullAsEmpty( GetIconLocation( out iconIdx ) ),
         iconIdx );
   }
}

public void EndEnvironmentObstruction()
{
   VerifyValidDisposable();
   if ( _EnvironmentRollback_ == null )
      return;
   try
   {
      foreach (var de in _EnvironmentRollback_)
         Environment.SetEnvironmentVariable( de.Key, de.Value );
   }
   catch (Exception e)
   {
      throw HOpFailedExceptionCreator.Create( e );
   }
   finally
   {
      _EnvironmentRollback_ = null;
      _OriginalVarNames_ = null;
      _PathExt_ = null;
   }
}

/// <exception cref="HOperationFailedException" />
public bool IsProbablyExecutablePath([HOptional] string varQueryString)
{
   if ( varQueryString == null )
      return false;
   string pathExpansion =
      Environment.ExpandEnvironmentVariables( varQueryString.TrimEnd() );
   if ( string.IsNullOrWhiteSpace( pathExpansion ) )
      return false;
   if ( HString.LastChar( pathExpansion ) == '%' )
      return true;
   string extension;
   try
   {
      extension = System.IO.Path.GetExtension( pathExpansion );
   }
   catch (ArgumentException e)
   {
      throw HOpFailedExceptionCreator.Create(
         HShellLinkProperties.Verbose.
         CANT_EXTRACT_FILE_EXTENSION_FROM_PATH_0__1__SHORTCUT_PROPERTY_PATH,
         pathExpansion, e.Message );
   }
   if (!( extension.Length > 1 && extension[ 0 ] == '.' ))
      return false;
   string pathExt = _EnvironmentRollback_ != null ?
      _PathExt_ : GetPathExt();
   return
      pathExt != null && (
      pathExt.IndexOf( extension+';', StringComparison.InvariantCultureIgnoreCase ) >= 0 ||
      pathExt.EndsWith( extension, true, CultureInfo.InvariantCulture ) );
}

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

/// <exception cref="HOperationFailedException" />
protected override void OnDispose(bool synchronous)
{
   try
   {
      HDisposable.Dispose( ref _Impl_ );
   }
   catch
   {
      if ( synchronous )
         HOps.Try(delegate{ EndEnvironmentObstruction(); });
      throw;
   }
   if ( synchronous )
      EndEnvironmentObstruction();
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="ObjectDisposedException" />
ISysHelpers_ShellLinkProperties Impl
{
   get { VerifyValidDisposable(); return _Impl_; }
}

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

/// <exception cref="InvalidOperationException" />
void PrepareEnvironmentRollback()
{
   VerifyValidDisposable();
   if ( !UseEnvironmentObstruction )
      throw new InvalidOperationException();
   if ( _EnvironmentRollback_ != null )
      return;
   var varList = new List<string>();
   foreach (DictionaryEntry de in Environment.GetEnvironmentVariables())
      varList.Add( (string)de.Key );
   _OriginalVarNames_ = varList.ToArray();
   _EnvironmentRollback_ = new HSimpleDictionary<string,string>(
      StringComparer.InvariantCultureIgnoreCase );
   _PathExt_ = GetPathExt();
}

bool ObstructEnvironment([HOptional] string varQueryString,bool isPathPropertyValue=false)
{
   VerifyValidDisposable();
   if ( !UseEnvironmentObstruction || varQueryString == null )
      return false;
   try
   {
      if ( isPathPropertyValue && !IsProbablyExecutablePath( varQueryString ) )
         return false; // << Folder path does not tolerate environment obstruction!
      PrepareEnvironmentRollback();
      int pctPos = -1;
      for (int pos = 0; pos < varQueryString.Length; pos ++)
         if ( varQueryString[ pos ] == '%' )
            if ( pctPos >= 0 )
            {
               int len = pos - pctPos - 1;
               if ( len > 0 )
               {
                  var varName = HString.NormilizeCaseByList(
                     varQueryString.Substring( pctPos+1, len ),
                     _OriginalVarNames_ );
                  if ( varName != null && _EnvironmentRollback_[ varName ] == null )
                  {
                     _EnvironmentRollback_[ varName ] =
                        Environment.GetEnvironmentVariable( varName );
                     Environment.SetEnvironmentVariable( varName, "'"+varName+"'" );
                  }
               }
               pctPos = -1;
            }
            else
               pctPos = pos;
   }
   catch (Exception e)
   {
      throw HOpFailedExceptionCreator.Create( e );
   }
   return true;
}

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

static HShellLinkProperties()
{
   CommonHelpers_SysUtils.Access();
}

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

[HOptionalResult]
static string GetPathExt()
{
   string pathExt = Environment.GetEnvironmentVariable( "PATHEXT" );
   if ( pathExt == null )
      return null;
   HString.Trim( ref pathExt );
   return HString.EmptyAsNull( pathExt );
}

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

ISysHelpers_ShellLinkProperties
   _Impl_;
string[]
   _OriginalVarNames_;
bool
   _UseEnvironmentObstruction_;
HSimpleDictionary<string,string>
   _EnvironmentRollback_;
string
   _PathExt_;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HShellLinkProperties
//**************************************************************************************************

} // CommonHelpers
