using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Launcheroid
{
    public class IconTool
    {
        private static bool VistaUp
        {
            get
            {
                return (Environment.OSVersion.Version.Major >= 6);
            }
        }

        private static bool IsJumbo(Bitmap bmp)
        {
            for (int y = 10; y < bmp.Height; y += 10)
                for (int x = 50; x < bmp.Width; x += 10)
                    if (bmp.GetPixel(x, y).A != 0)
                        return true;
            for (int y = 50; y < bmp.Height; y += 10)
                for (int x = 10; x < 50; x += 10)
                    if (bmp.GetPixel(x, y).A != 0)
                        return true;
            return false;
        }

        private static Bitmap ResizeToJumbo(Bitmap bmp)
        {
            Bitmap result = new Bitmap(256, 256);
            Graphics g = Graphics.FromImage(result);
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(bmp, new Rectangle(0, 0, 256, 256), new Rectangle(0, 0, 48, 48), GraphicsUnit.Pixel);
            g.Dispose();
            bmp.Dispose();
            return result;
        }

        public static Bitmap GetFileJumbo(string path)
        {
            try
            {
                Bitmap b = (Bitmap)Bitmap.FromFile(path);
                return ResizeBitmap(b);
            }
            catch { };
            Bitmap bmp = VistaUp ? WinApiEx.GetFileIconJumbo(path) : WinApiEx.GetFileIconExtraLarge(path);
            if (!VistaUp || !IsJumbo(bmp))
            {
                bmp = ResizeToJumbo(bmp);
            }
            return bmp;
        }

        private static Bitmap ResizeBitmap(Bitmap b)
        {
            int ox, oy, ww, hh;
            if (b.Width >= b.Height)
            {
                ox = 0;
                ww = 256;
                hh = b.Height * 256 / b.Width;
                oy = (256 - hh) / 2;
            }
            else
            {
                oy = 0;
                hh = 256;
                ww = b.Width * 256 / b.Height;
                ox = (256 - ww) / 2;
            }
            Bitmap bmp = new Bitmap(256, 256);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(b, ox, oy, ww, hh);
            g.Dispose();
            b.Dispose();
            return bmp;
        }
    }

    public class WinApi
    {
        private const string libShell32 = "Shell32.dll";
        private const string libUser32 = "User32.dll";

        #region Shell32

        #region API Constants

        private const int MAX_PATH = 256;

        #region [BIF] Browsing for directory
        protected const uint BIF_RETURNONLYFSDIRS = 0x0001;
        protected const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
        protected const uint BIF_STATUSTEXT = 0x0004;
        protected const uint BIF_RETURNFSANCESTORS = 0x0008;
        protected const uint BIF_EDITBOX = 0x0010;
        protected const uint BIF_VALIDATE = 0x0020;
        protected const uint BIF_NEWDIALOGSTYLE = 0x0040;
        protected const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
        protected const uint BIF_BROWSEINCLUDEURLS = 0x0080;
        protected const uint BIF_BROWSEFORCOMPUTER = 0x1000;
        protected const uint BIF_BROWSEFORPRINTER = 0x2000;
        protected const uint BIF_BROWSEINCLUDEFILES = 0x4000;
        protected const uint BIF_SHAREABLE = 0x8000;
        #endregion

        #region [SHGFI]
        protected const uint SHGFI_ICON = 0x000000100;     // get icon
        protected const uint SHGFI_DISPLAYNAME = 0x000000200;     // get display name
        protected const uint SHGFI_TYPENAME = 0x000000400;     // get type name
        protected const uint SHGFI_ATTRIBUTES = 0x000000800;     // get attributes
        protected const uint SHGFI_ICONLOCATION = 0x000001000;     // get icon location
        protected const uint SHGFI_EXETYPE = 0x000002000;     // return exe type
        protected const uint SHGFI_SYSICONINDEX = 0x000004000;     // get system icon index
        protected const uint SHGFI_LINKOVERLAY = 0x000008000;     // put a link overlay on icon
        protected const uint SHGFI_SELECTED = 0x000010000;     // show icon in selected state
        protected const uint SHGFI_ATTR_SPECIFIED = 0x000020000;     // get only specified attributes
        protected const uint SHGFI_LARGEICON = 0x000000000;     // get large icon
        protected const uint SHGFI_SMALLICON = 0x000000001;     // get small icon
        protected const uint SHGFI_OPENICON = 0x000000002;     // get open icon
        protected const uint SHGFI_SHELLICONSIZE = 0x000000004;     // get shell size icon
        protected const uint SHGFI_PIDL = 0x000000008;     // pszPath is a pidl
        protected const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;     // use passed dwFileAttribute
        protected const uint SHGFI_ADDOVERLAYS = 0x000000020;     // apply the appropriate overlays
        protected const uint SHGFI_OVERLAYINDEX = 0x000000040;     // Get the index of the overlay
        #endregion

        #region [FILE_ATTRIBUTE]
        protected const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
        protected const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
        #endregion

        #region [SHIL]
        protected const uint SHIL_EXTRALARGE = 0x2;
        protected const uint SHIL_JUMBO = 0x4;
        #endregion

        #region [ILD]
        /// <summary>
        /// Draw item normally.
        /// </summary>
        protected const uint ILD_NORMAL = 0x0;
        /// <summary>
        /// Draw item transparently.
        /// </summary>
        protected const uint ILD_TRANSPARENT = 0x1;
        /// <summary>
        /// Draw item blended with 25% of the specified foreground colour
        /// or the Highlight colour if no foreground colour specified.
        /// </summary>
        protected const uint ILD_BLEND25 = 0x2;
        /// <summary>
        /// Draw item blended with 50% of the specified foreground colour
        /// or the Highlight colour if no foreground colour specified.
        /// </summary>
        protected const uint ILD_SELECTED = 0x4;
        /// <summary>
        /// Draw the icon's mask
        /// </summary>
        protected const uint ILD_MASK = 0x10;
        /// <summary>
        /// Draw the icon image without using the mask
        /// </summary>
        protected const uint ILD_IMAGE = 0x20;
        /// <summary>
        /// Draw the icon using the ROP specified.
        /// </summary>
        protected const uint ILD_ROP = 0x40;
        /// <summary>
        /// ?
        /// </summary>
        protected const uint ILD_OVERLAYMASK = 0xF00;
        /// <summary>
        /// Preserves the alpha channel in dest. XP only.
        /// </summary>
        protected const uint ILD_PRESERVEALPHA = 0x1000; // 
        /// <summary>
        /// Scale the image to cx, cy instead of clipping it.  XP only.
        /// </summary>
        protected const uint ILD_SCALE = 0x2000;
        /// <summary>
        /// Scale the image to the current DPI of the display. XP only.
        /// </summary>
        protected const uint ILD_DPISCALE = 0x4000;
        #endregion


        #endregion

        #region Structures
        [StructLayout(LayoutKind.Sequential)]
        protected struct SHITEMID
        {
            public ushort cb;
            [MarshalAs(UnmanagedType.LPArray)]
            public byte[] abID;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct ITEMIDLIST
        {
            public SHITEMID mkid;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct BROWSEINFO
        {
            public IntPtr hwndOwner;
            public IntPtr pidlRoot;
            public IntPtr pszDisplayName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpszTitle;
            public uint ulFlags;
            public IntPtr lpfn;
            public int lParam;
            public IntPtr iImage;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct SHFILEINFO
        {
            public const int NAMESIZE = 80;
            public IntPtr hIcon;
            public int iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
            public string szTypeName;
        };
        #endregion

        [DllImport(libShell32)]
        protected static extern IntPtr SHGetFileInfo(
            string pszPath,
            uint dwFileAttributes,
            ref SHFILEINFO psfi,
            uint cbFileInfo,
            uint uFlags
            );

        #region ImageList


        #region Private ImageList COM Interop (XP)
        [StructLayout(LayoutKind.Sequential)]
        public struct IMAGELISTDRAWPARAMS
        {
            public int cbSize;
            public IntPtr himl;
            public int i;
            public IntPtr hdcDst;
            public int x;
            public int y;
            public int cx;
            public int cy;
            public int xBitmap;        // x offest from the upperleft of bitmap
            public int yBitmap;        // y offset from the upperleft of bitmap
            public int rgbBk;
            public int rgbFg;
            public int fStyle;
            public int dwRop;
            public int fState;
            public int Frame;
            public int crEffect;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct IMAGEINFO
        {
            public IntPtr hbmImage;
            public IntPtr hbmMask;
            public int Unused1;
            public int Unused2;
            public RECT rcImage;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            int left;
            int top;
            int right;
            int bottom;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            int x;
            int y;
        }
        [ComImportAttribute()]
        [GuidAttribute("46EB5926-582E-4017-9FDF-E8998DAA0950")]
        [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
        //helpstring("Image List"),
        public interface IImageList
        {
            [PreserveSig]
            int Add(
               IntPtr hbmImage,
               IntPtr hbmMask,
               ref int pi);

            [PreserveSig]
            int ReplaceIcon(
               int i,
               IntPtr hicon,
               ref int pi);

            [PreserveSig]
            int SetOverlayImage(
               int iImage,
               int iOverlay);

            [PreserveSig]
            int Replace(
               int i,
               IntPtr hbmImage,
               IntPtr hbmMask);

            [PreserveSig]
            int AddMasked(
               IntPtr hbmImage,
               int crMask,
               ref int pi);

            [PreserveSig]
            int Draw(
               ref IMAGELISTDRAWPARAMS pimldp);

            [PreserveSig]
            int Remove(
               uint i);

            [PreserveSig]
            int GetIcon(
               uint i,
               uint flags,
               ref IntPtr picon);

            [PreserveSig]
            int GetImageInfo(
               uint i,
               ref IMAGEINFO pImageInfo);

            [PreserveSig]
            int Copy(
               uint iDst,
               IImageList punkSrc,
               uint iSrc,
               uint uFlags);

            [PreserveSig]
            int Merge(
               int i1,
               IImageList punk2,
               int i2,
               int dx,
               int dy,
               ref Guid riid,
               ref IntPtr ppv);

            [PreserveSig]
            int Clone(
               ref Guid riid,
               ref IntPtr ppv);

            [PreserveSig]
            int GetImageRect(
               int i,
               ref RECT prc);

            [PreserveSig]
            int GetIconSize(
               ref int cx,
               ref int cy);

            [PreserveSig]
            int SetIconSize(
               int cx,
               int cy);

            [PreserveSig]
            int GetImageCount(
               ref int pi);

            [PreserveSig]
            int SetImageCount(
               int uNewCount);

            [PreserveSig]
            int SetBkColor(
               int clrBk,
               ref int pclr);

            [PreserveSig]
            int GetBkColor(
               ref int pclr);

            [PreserveSig]
            int BeginDrag(
               int iTrack,
               int dxHotspot,
               int dyHotspot);

            [PreserveSig]
            int EndDrag();

            [PreserveSig]
            int DragEnter(
               IntPtr hwndLock,
               int x,
               int y);

            [PreserveSig]
            int DragLeave(
               IntPtr hwndLock);

            [PreserveSig]
            int DragMove(
               int x,
               int y);

            [PreserveSig]
            int SetDragCursorImage(
               ref IImageList punk,
               int iDrag,
               int dxHotspot,
               int dyHotspot);

            [PreserveSig]
            int DragShowNolock(
               int fShow);

            [PreserveSig]
            int GetDragImage(
               ref POINT ppt,
               ref POINT pptHotspot,
               ref Guid riid,
               ref IntPtr ppv);

            [PreserveSig]
            int GetItemFlags(
               int i,
               ref int dwFlags);

            [PreserveSig]
            int GetOverlayImage(
               int iOverlay,
               ref int piIndex);
        };
        #endregion
        /// <summary>
        /// SHGetImageList is not exported correctly in XP.  See KB316931
        /// http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q316931
        /// Apparently (and hopefully) ordinal 727 isn't going to change.
        /// </summary>
        [DllImport(libShell32, EntryPoint = "#727")]
        public extern static int SHGetImageList(
           uint iImageList,
           ref Guid riid,
           ref IImageList ppv
           );
        #endregion

        #endregion

        #region User32

        /// <summary>
        /// Provides access to function required to delete handle. This method is used internally
        /// and is not required to be called separately.
        /// </summary>
        /// <param name="hIcon">Pointer to icon handle.</param>
        /// <returns>N/A</returns>
        [DllImport(libUser32)]
        protected static extern int DestroyIcon(IntPtr hIcon);

        #endregion
    }

    public class WinApiEx : WinApi
    {
        private static IImageList GetShellImageList(uint flags)
        {
            Guid shell_idImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
            IImageList result = null;
            int hresult = SHGetImageList(flags, ref shell_idImageList, ref result);
            if (hresult != 0)
                throw new Exception("Can't get Shell Image List");
            return result;
        }

        private static SHFILEINFO SHGetFileInfo(string path, uint flags)
        {
            SHFILEINFO result = new SHFILEINFO();
            SHGetFileInfo(path,
                FILE_ATTRIBUTE_NORMAL,
                ref result,
                (uint)System.Runtime.InteropServices.Marshal.SizeOf(result),
                flags);
            return result;
        }

        private static Icon ImageListGetIcon(IImageList il, uint index)
        {
            IntPtr iIcon = IntPtr.Zero;
            il.GetIcon(index, ILD_TRANSPARENT, ref iIcon);
            Icon ico = (Icon)Icon.FromHandle(iIcon).Clone();
            DestroyIcon(iIcon);
            return ico;
        }

        public static Bitmap GetFileIcon(string path, uint size)
        {
            SHFILEINFO shfi = SHGetFileInfo(path, SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME);
            IImageList shil = GetShellImageList(size);

            Icon ico = ImageListGetIcon(shil, (uint)shfi.iIcon);
            Bitmap bmp = ico.ToBitmap();
            ico.Dispose();
            return bmp;
        }

        public static Bitmap GetFileIconJumbo(string path)
        {
            return GetFileIcon(path, SHIL_JUMBO);
        }

        public static Bitmap GetFileIconExtraLarge(string path)
        {
            return GetFileIcon(path, SHIL_EXTRALARGE);
        }

    }
}
