using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Threading;

namespace Standard
{
    internal sealed class MessageWindow : DispatcherObject, IDisposable
    {
        public IntPtr Handle { get; private set; }

        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
        public MessageWindow(CS classStyle, WS style, WS_EX exStyle, Rect location, string name, WndProc callback)
        {
            this._wndProcCallback = callback;
            this._className = "MessageWindowClass+" + Guid.NewGuid().ToString();
            WNDCLASSEX wndclassex = new WNDCLASSEX
            {
                cbSize = Marshal.SizeOf(typeof(WNDCLASSEX)),
                style = classStyle,
                lpfnWndProc = MessageWindow.s_WndProc,
                hInstance = NativeMethods.GetModuleHandle(null),
                hbrBackground = NativeMethods.GetStockObject(StockObject.NULL_BRUSH),
                lpszMenuName = "",
                lpszClassName = this._className
            };
            NativeMethods.RegisterClassEx(ref wndclassex);
            GCHandle value = default(GCHandle);
            try
            {
                value = GCHandle.Alloc(this);
                IntPtr lpParam = (IntPtr) value;
                this.Handle = NativeMethods.CreateWindowEx(exStyle, this._className, name, style, (int) location.X, (int) location.Y, (int) location.Width, (int) location.Height, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, lpParam);
            }
            finally
            {
                value.Free();
            }
        }

        ~MessageWindow()
        {
            this._Dispose(false, false);
        }

        public void Dispose()
        {
            this._Dispose(true, false);
            GC.SuppressFinalize(this);
        }

        [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "disposing")]
        private void _Dispose(bool disposing, bool isHwndBeingDestroyed)
        {
            if (this._isDisposed)
            {
                return;
            }
            this._isDisposed = true;
            IntPtr hwnd = this.Handle;
            string className = this._className;
            if (isHwndBeingDestroyed)
            {
                base.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback((object arg) => MessageWindow._DestroyWindow(IntPtr.Zero, className)));
            }
            else if (this.Handle != IntPtr.Zero)
            {
                if (base.CheckAccess())
                {
                    MessageWindow._DestroyWindow(hwnd, className);
                }
                else
                {
                    base.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback((object arg) => MessageWindow._DestroyWindow(hwnd, className)));
                }
            }
            MessageWindow.s_windowLookup.Remove(hwnd);
            this._className = null;
            this.Handle = IntPtr.Zero;
        }

        [SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly")]
        private static IntPtr _WndProc(IntPtr hwnd, WM msg, IntPtr wParam, IntPtr lParam)
        {
            IntPtr result = IntPtr.Zero;
            MessageWindow messageWindow = null;
            if (msg == WM.CREATE)
            {
                messageWindow = (MessageWindow) GCHandle.FromIntPtr(((CREATESTRUCT) Marshal.PtrToStructure(lParam, typeof(CREATESTRUCT))).lpCreateParams).Target;
                MessageWindow.s_windowLookup.Add(hwnd, messageWindow);
            }
            else if (!MessageWindow.s_windowLookup.TryGetValue(hwnd, out messageWindow))
            {
                return NativeMethods.DefWindowProc(hwnd, msg, wParam, lParam);
            }
            WndProc wndProcCallback = messageWindow._wndProcCallback;
            if (wndProcCallback != null)
            {
                result = wndProcCallback(hwnd, msg, wParam, lParam);
            }
            else
            {
                result = NativeMethods.DefWindowProc(hwnd, msg, wParam, lParam);
            }
            if (msg == WM.NCDESTROY)
            {
                messageWindow._Dispose(true, true);
                GC.SuppressFinalize(messageWindow);
            }
            return result;
        }

        private static object _DestroyWindow(IntPtr hwnd, string className)
        {
            Utility.SafeDestroyWindow(ref hwnd);
            NativeMethods.UnregisterClass(className, NativeMethods.GetModuleHandle(null));
            return null;
        }

        private static readonly WndProc s_WndProc = new WndProc(MessageWindow._WndProc);

        private static readonly Dictionary<IntPtr, MessageWindow> s_windowLookup = new Dictionary<IntPtr, MessageWindow>();

        private WndProc _wndProcCallback;

        private string _className;

        private bool _isDisposed;
    }
}
