using System;
using System.Collections.Generic;
using System.Text;
using MyDownloader.Core.Extensions;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using MyDownloader.Core;
using MyDownloader.Core.UI;
using System.Security.Permissions;
using System.ComponentModel;

//http://radsoftware.com.au/articles/clipboardmonitor.aspx 
namespace MyDownloader.Extension.WindowsIntegration.ClipboardMonitor
{
    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    public class ClipboardMonitor : IDisposable
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern bool ChangeClipboardChain(
            IntPtr hWndRemove,  // handle to window to remove
            IntPtr hWndNewNext  // handle to next window
            );

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        private const int WM_DRAWCLIPBOARD = 0x0308;
        private const int WM_CHANGECBCHAIN = 0x030D;

        private IntPtr handle;
        private IntPtr clipboardViewerNext;
        private IClipboardDataHandler dataHandler;
        private bool enabled;

        #region Methods 

        public bool FilterMessage(ref Message m)
        {
            if (!enabled)
            {
                return false;
            }

            switch (m.Msg)
            {
                //
                // The WM_DRAWCLIPBOARD message is sent to the first window 
                // in the clipboard viewer chain when the content of the 
                // clipboard changes. This enables a clipboard viewer 
                // window to display the new content of the clipboard. 
                //
                case WM_DRAWCLIPBOARD:

                    Debug.WriteLine("WindowProc DRAWCLIPBOARD: " + m.Msg, "WndProc");

                    if (dataHandler != null)
                    {
                        try
                        {
                            IDataObject iData = Clipboard.GetDataObject();

                            dataHandler.HandleClipboardData(iData);
                        }
                        catch
                        {
                        }
                    }

                    //
                    // Each window that receives the WM_DRAWCLIPBOARD message 
                    // must call the SendMessage function to pass the message 
                    // on to the next window in the clipboard viewer chain.
                    //
                    SendMessage(clipboardViewerNext, m.Msg, m.WParam, m.LParam);
                    return true;


                //
                // The WM_CHANGECBCHAIN message is sent to the first window 
                // in the clipboard viewer chain when a window is being 
                // removed from the chain. 
                //
                case WM_CHANGECBCHAIN:
                    Debug.WriteLine("WM_CHANGECBCHAIN: lParam: " + m.LParam, "WndProc");

                    // When a clipboard viewer window receives the WM_CHANGECBCHAIN message, 
                    // it should call the SendMessage function to pass the message to the 
                    // next window in the chain, unless the next window is the window 
                    // being removed. In this case, the clipboard viewer should save 
                    // the handle specified by the lParam parameter as the next window in the chain. 

                    //
                    // wParam is the Handle to the window being removed from 
                    // the clipboard viewer chain 
                    // lParam is the Handle to the next window in the chain 
                    // following the window being removed. 
                    if (m.WParam == clipboardViewerNext)
                    {
                        //
                        // If wParam is the next clipboard viewer then it
                        // is being removed so update pointer to the next
                        // window in the clipboard chain
                        //
                        clipboardViewerNext = m.LParam;
                    }
                    else
                    {
                        SendMessage(clipboardViewerNext, m.Msg, m.WParam, m.LParam);
                    }
                    return true;

                default:
                    return false;
            }
        }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            ChangeClipboardChain(handle, clipboardViewerNext);
            handle = IntPtr.Zero;
            clipboardViewerNext = IntPtr.Zero;
            dataHandler = null;
        }

        #endregion

        #region Properties

        public bool Enabled
        {
            get
            {
                return enabled;
            }
            set
            {
                Settings.Default.MonitorClipboard = value;
                Settings.Default.Save();
            }
        } 

        #endregion

        public ClipboardMonitor(IClipboardDataHandler handler, IntPtr winHandle)
        {
            enabled = Settings.Default.MonitorClipboard;

            Settings.Default.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
            {
                enabled = Settings.Default.MonitorClipboard;
            };

            handle = winHandle;
            clipboardViewerNext = SetClipboardViewer(handle);

            this.dataHandler = handler;
        } 
    }
}