using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace Corp3s.NativeAPI
{
    public class WinProcModifier
    {
        private const int GWL_WNDPROC = -4;

        public const uint WM_LBUTTONDOWN = 0x0201;
        public const uint WM_LBUTTONUP = 0x0202;
        public const uint MK_LBUTTON = 0x0001;

        private Hashtable m_Listeners = new Hashtable();
        private IntPtr m_hWnd;
        private IntPtr m_hOldWinProc = IntPtr.Zero;        

        public WinProcModifier(IntPtr hWnd)
        {            
            Attach(hWnd);
        }

        public void Register(uint msg, WndProcDelegate proc)
        {
            if (m_Listeners.Contains(msg))
                m_Listeners[msg] = proc;
            else
                m_Listeners.Add(msg, proc);
        }

        public void UnRegister(uint msg)
        {
            if (m_Listeners.Contains(msg))
                m_Listeners.Remove(msg);
        }

        public void Attach(IntPtr hWnd)
        {
            if(m_hOldWinProc != IntPtr.Zero)
                throw new System.ComponentModel.Win32Exception(0, "Only one WinProc is registered.");

            m_hWnd = hWnd;
            int hResult = WinAPI.Instance.GetWindowLong(hWnd, GWL_WNDPROC);
            if (hResult == 0)
                throw new System.ComponentModel.Win32Exception(1, "Failed to get WinProc.");

            m_hOldWinProc = new IntPtr(hResult);

            hResult = WinAPI.Instance.SetWindowLong(hWnd, GWL_WNDPROC, new WndProcDelegate(WndProc));
            if (hResult == 0)
                throw new System.ComponentModel.Win32Exception(2, "Failed to set WinProc.");
        }

        public void Detach(IntPtr hWnd)
        {            
            if(m_hOldWinProc == IntPtr.Zero)
                throw new System.ComponentModel.Win32Exception(2, "There is no WinProc to detach.");

            int hResult = WinAPI.Instance.SetWindowLong(m_hWnd, GWL_WNDPROC, m_hOldWinProc);
            //if (hResult == 0)
            //    throw new System.ComponentModel.Win32Exception(2, "Failed to detach WinProc.");

            m_hOldWinProc = IntPtr.Zero;
        }

        protected int WndProc(IntPtr hWnd, uint msg, IntPtr wparam, IntPtr lparam)
        {
            try
            {
                WndProcDelegate proc = m_Listeners[msg] as WndProcDelegate;
                if (proc != null)
                    proc(hWnd, msg, wparam, lparam);
            }
            catch { }

            return WinAPI.Instance.CallWindowProc(m_hOldWinProc, hWnd, msg, wparam, lparam);
        }
    }
}
