/**************************************************************************
*
* Filename:     ShellShortcut.cs
* Author:       Mattias Sjögren (mattias@mvps.org)
*               http://www.msjogren.net/dotnet/
*
* Description:  Defines a .NET friendly class, ShellShortcut, for reading
*               and writing shortcuts.
*               Define the conditional compilation symbol UNICODE to use
*               IShellLinkW internally.
*
* Public types: class ShellShortcut
*
*
* Dependencies: ShellLinkNative.cs
*
*
* Copyright ©2001-2002, Mattias Sjögren
* 
**************************************************************************/

using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;


namespace MediaBrowser.Installer.Code
{
	/// <remarks>
	///   .NET friendly wrapper for the ShellLink class
	/// </remarks>
	public class ShellShortcut : IDisposable
	{
    private const int INFOTIPSIZE = 1024;
    private const int MAX_PATH = 260;

    private const int SW_SHOWNORMAL = 1;
    private const int SW_SHOWMINIMIZED = 2;
    private const int SW_SHOWMAXIMIZED = 3;
    private const int SW_SHOWMINNOACTIVE = 7;


  #if UNICODE
    private IShellLinkW m_Link;
  #else
    private IShellLinkA m_Link;
  #endif
    private string m_sPath;

    ///
    /// <param name='linkPath'>
    ///   Path to new or existing shortcut file (.lnk).
    /// </param>
    ///
    public ShellShortcut(string linkPath)
    {
      IPersistFile pf;

      m_sPath = linkPath;

    #if UNICODE
      m_Link = (IShellLinkW) new ShellLink();
    #else
      m_Link = (IShellLinkA) new ShellLink();
    #endif

      if ( File.Exists( linkPath ) ) {
        pf = (IPersistFile)m_Link;
        pf.Load( linkPath, 0 );
      }

    }

    //
    //  IDisplosable implementation
    //
    public void Dispose()
    {
      if ( m_Link != null ) {
        Marshal.ReleaseComObject( m_Link );
        m_Link = null;
      }
    }

    /// <value>
    ///   Gets or sets the argument list of the shortcut.
    /// </value>
    public string Arguments
    {
      get
      {
        StringBuilder sb = new StringBuilder( INFOTIPSIZE );
        m_Link.GetArguments( sb, sb.Capacity );
        return sb.ToString();
      }
      set { m_Link.SetArguments( value ); }
    }

    /// <value>
    ///   Gets or sets a description of the shortcut.
    /// </value>
    public string Description
    {
      get
      {
        StringBuilder sb = new StringBuilder( INFOTIPSIZE );
        m_Link.GetDescription( sb, sb.Capacity );
        return sb.ToString();
      }
      set { m_Link.SetDescription( value ); }
    }

    /// <value>
    ///   Gets or sets the working directory (aka start in directory) of the shortcut.
    /// </value>
    public string WorkingDirectory
    {
      get
      {
        StringBuilder sb = new StringBuilder( MAX_PATH );
        m_Link.GetWorkingDirectory( sb, sb.Capacity );
        return sb.ToString();
      }
      set { m_Link.SetWorkingDirectory( value ); }
    }

    //
    // If Path returns an empty string, the shortcut is associated with
    // a PIDL instead, which can be retrieved with IShellLink.GetIDList().
    // This is beyond the scope of this wrapper class.
    //
    /// <value>
    ///   Gets or sets the target path of the shortcut.
    /// </value>
    public string Path
    {
      get
      {
      #if UNICODE
        WIN32_FIND_DATAW wfd = new WIN32_FIND_DATAW();
      #else
        WIN32_FIND_DATAA wfd = new WIN32_FIND_DATAA();
      #endif
        StringBuilder sb = new StringBuilder( MAX_PATH );

        m_Link.GetPath( sb, sb.Capacity, out wfd, SLGP_FLAGS.SLGP_UNCPRIORITY );
        return sb.ToString();
      }
      set { m_Link.SetPath( value ); }
    }

    /// <value>
    ///   Gets or sets the path of the <see cref="Icon"/> assigned to the shortcut.
    /// </value>
    /// <summary>
    ///   <seealso cref="IconIndex"/>
    /// </summary>
    public string IconPath
    {
      get
      {
        StringBuilder sb = new StringBuilder( MAX_PATH );
        int nIconIdx;
        m_Link.GetIconLocation( sb, sb.Capacity, out nIconIdx );
        return sb.ToString();
      }
      set { m_Link.SetIconLocation( value, IconIndex ); }
    }

    /// <value>
    ///   Gets or sets the index of the <see cref="Icon"/> assigned to the shortcut.
    ///   Set to zero when the <see cref="IconPath"/> property specifies a .ICO file.
    /// </value>
    /// <summary>
    ///   <seealso cref="IconPath"/>
    /// </summary>
    public int IconIndex
    {
      get
      {
        StringBuilder sb = new StringBuilder( MAX_PATH );
        int nIconIdx;
        m_Link.GetIconLocation( sb, sb.Capacity, out nIconIdx );
        return nIconIdx;
      }
      set { m_Link.SetIconLocation( IconPath, value ); }
    }

    /// <value>
    ///   Retrieves the Icon of the shortcut as it will appear in Explorer.
    ///   Use the <see cref="IconPath"/> and <see cref="IconIndex"/>
    ///   properties to change it.
    /// </value>
    public Icon Icon
    {
      get
      {
        StringBuilder sb = new StringBuilder( MAX_PATH );
        int nIconIdx;
        IntPtr hIcon, hInst;
        Icon ico, clone;


        m_Link.GetIconLocation( sb, sb.Capacity, out nIconIdx );
        hInst = Marshal.GetHINSTANCE( this.GetType().Module );
        hIcon = Native.ExtractIcon( hInst, sb.ToString(), nIconIdx );
        if ( hIcon == IntPtr.Zero )
          return null;

        // Return a cloned Icon, because we have to free the original ourselves.
        ico = Icon.FromHandle( hIcon );
        clone = (Icon)ico.Clone();
        ico.Dispose();
        Native.DestroyIcon( hIcon );
        return clone;
      }
    }

    /// <value>
    ///   Gets or sets the System.Diagnostics.ProcessWindowStyle value
    ///   that decides the initial show state of the shortcut target. Note that
    ///   ProcessWindowStyle.Hidden is not a valid property value.
    /// </value>
    public ProcessWindowStyle WindowStyle
    {
      get
      {
        int nWS;
        m_Link.GetShowCmd( out nWS );

        switch ( nWS ) {
          case SW_SHOWMINIMIZED:
          case SW_SHOWMINNOACTIVE:
            return ProcessWindowStyle.Minimized;
          
          case SW_SHOWMAXIMIZED:
            return ProcessWindowStyle.Maximized;

          default:
            return ProcessWindowStyle.Normal;
        }
      }
      set
      {
        int nWS;

        switch ( value ) {
          case ProcessWindowStyle.Normal:
            nWS = SW_SHOWNORMAL;
            break;

          case ProcessWindowStyle.Minimized:
            nWS = SW_SHOWMINNOACTIVE;
            break;

          case ProcessWindowStyle.Maximized:
            nWS = SW_SHOWMAXIMIZED;
            break;

          default: // ProcessWindowStyle.Hidden
            throw new ArgumentException("Unsupported ProcessWindowStyle value.");
        }

        m_Link.SetShowCmd( nWS );
      
      }
    }

    /// <value>
    ///   Gets or sets the hotkey for the shortcut.
    /// </value>
    public Keys Hotkey
    {
      get
      {
        short wHotkey;
        int dwHotkey;

        m_Link.GetHotkey( out wHotkey );

        //
        // Convert from IShellLink 16-bit format to Keys enumeration 32-bit value
        // IShellLink: 0xMMVK
        // Keys:  0x00MM00VK        
        //   MM = Modifier (Alt, Control, Shift)
        //   VK = Virtual key code
        //       
        dwHotkey = ((wHotkey & 0xFF00) << 8) | (wHotkey & 0xFF);
        return (Keys) dwHotkey;
      }
      set
      {
        short wHotkey;

        if ( (value & Keys.Modifiers) == 0 )
          throw new ArgumentException("Hotkey must include a modifier key.");

        //    
        // Convert from Keys enumeration 32-bit value to IShellLink 16-bit format
        // IShellLink: 0xMMVK
        // Keys:  0x00MM00VK        
        //   MM = Modifier (Alt, Control, Shift)
        //   VK = Virtual key code
        //       
        wHotkey = unchecked((short) ( ((int) (value & Keys.Modifiers) >> 8) | (int) (value & Keys.KeyCode) ));
        m_Link.SetHotkey( wHotkey );

      }
    }

    /// <summary>
    ///   Saves the shortcut to disk.
    /// </summary>
    public void Save()
    {
      IPersistFile pf = (IPersistFile) m_Link;
      pf.Save( m_sPath, true );
    }

    /// <summary>
    ///   Returns a reference to the internal ShellLink object,
    ///   which can be used to perform more advanced operations
    ///   not supported by this wrapper class, by using the
    ///   IShellLink interface directly.
    /// </summary>
    public object ShellLink
    {
      get { return m_Link; }
    }


  #region Native Win32 API functions
    private class Native
    {
      [DllImport("shell32.dll", CharSet=CharSet.Auto)]
      public static extern IntPtr ExtractIcon(IntPtr hInst, string lpszExeFileName, int nIconIndex);

      [DllImport("user32.dll")]
      public static extern bool DestroyIcon(IntPtr hIcon);
    }
  #endregion

	}
}