using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace Standard
{
    internal sealed class SafeDC : SafeHandleZeroOrMinusOneIsInvalid
    {
        public IntPtr Hwnd
        {
            set
            {
                this._hwnd = new IntPtr?(value);
            }
        }

        private SafeDC() : base(true)
        {
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        protected override bool ReleaseHandle()
        {
            if (this._created)
            {
                return SafeDC.NativeMethods.DeleteDC(this.handle);
            }
            return this._hwnd == null || this._hwnd.Value == IntPtr.Zero || SafeDC.NativeMethods.ReleaseDC(this._hwnd.Value, this.handle) == 1;
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
        public static SafeDC CreateDC(string deviceName)
        {
            SafeDC safeDC = null;
            try
            {
                safeDC = SafeDC.NativeMethods.CreateDC(deviceName, null, IntPtr.Zero, IntPtr.Zero);
            }
            finally
            {
                if (safeDC != null)
                {
                    safeDC._created = true;
                }
            }
            if (safeDC.IsInvalid)
            {
                safeDC.Dispose();
                throw new SystemException("Unable to create a device context from the specified device information.");
            }
            return safeDC;
        }

        [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        public static SafeDC CreateCompatibleDC(SafeDC hdc)
        {
            SafeDC safeDC = null;
            try
            {
                IntPtr hdc2 = IntPtr.Zero;
                if (hdc != null)
                {
                    hdc2 = hdc.handle;
                }
                safeDC = SafeDC.NativeMethods.CreateCompatibleDC(hdc2);
                if (safeDC == null)
                {
                    HRESULT.ThrowLastError();
                }
            }
            finally
            {
                if (safeDC != null)
                {
                    safeDC._created = true;
                }
            }
            if (safeDC.IsInvalid)
            {
                safeDC.Dispose();
                throw new SystemException("Unable to create a device context from the specified device information.");
            }
            return safeDC;
        }

        public static SafeDC GetDC(IntPtr hwnd)
        {
            SafeDC safeDC = null;
            try
            {
                safeDC = SafeDC.NativeMethods.GetDC(hwnd);
            }
            finally
            {
                if (safeDC != null)
                {
                    safeDC.Hwnd = hwnd;
                }
            }
            if (safeDC.IsInvalid)
            {
                HRESULT.E_FAIL.ThrowIfFailed();
            }
            return safeDC;
        }

        public static SafeDC GetDesktop()
        {
            return SafeDC.GetDC(IntPtr.Zero);
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        public static SafeDC WrapDC(IntPtr hdc)
        {
            return new SafeDC
            {
                handle = hdc,
                _created = false,
                _hwnd = new IntPtr?(IntPtr.Zero)
            };
        }

        private IntPtr? _hwnd;

        private bool _created;

        private static class NativeMethods
        {
            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
            [DllImport("user32.dll")]
            public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
            [DllImport("user32.dll")]
            public static extern SafeDC GetDC(IntPtr hwnd);

            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
            [DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
            public static extern SafeDC CreateDC([MarshalAs(UnmanagedType.LPWStr)] string lpszDriver, [MarshalAs(UnmanagedType.LPWStr)] string lpszDevice, IntPtr lpszOutput, IntPtr lpInitData);

            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
            [DllImport("gdi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern SafeDC CreateCompatibleDC(IntPtr hdc);

            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
            [DllImport("gdi32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool DeleteDC(IntPtr hdc);
        }
    }
}
