#include "cwndproc.h"

#define ID_NOTIFY_SUBCLASS (DWORD)'CHN'     // CHN change notify
//
// CImpWndProc
//
LRESULT CALLBACK CImpWndProc::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (WM_NCCREATE == uMsg)
    {
        CImpWndProc* pThis = (CImpWndProc*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
        if (EVAL(pThis))
        {
            pThis->_hwnd = hwnd;
            SetWindowPtr(hwnd, 0, pThis);

            // Even if pThis->vWndProc fails the create, USER will always
            // send us a WM_NCDESTROY so we always get a chance to clean up
            return pThis->v_WndProc(hwnd, uMsg, wParam, lParam);
        }
        return FALSE;
    }
    else
    {
        CImpWndProc* pThis = (CImpWndProc*)GetWindowPtr0(hwnd);    // GetWindowLong(hwnd, 0);
        LRESULT lres;

        if (pThis)
        {
            // Always retain a ref across the v_WndProc in case
            // the window destroys itself during the callback.
            pThis->AddRef();

            lres = pThis->v_WndProc(hwnd, uMsg, wParam, lParam);

            if (uMsg == WM_NCDESTROY)
            {
                SetWindowPtr(hwnd, 0, NULL);
                pThis->_hwnd = NULL;
            }
            pThis->Release();
        }
        else
        {
            //
            // The only way this should happen is if we haven't actually
            // gotten a WM_NCCREATE yet.  User sends a WM_GETMINMAXINFO
            // to some windows before WM_NCCREATE (for legacy compat).
            // Assert that we're hitting that case.
            //
            ASSERT(uMsg == WM_GETMINMAXINFO);

            lres = SHDefWindowProc(hwnd, uMsg, wParam, lParam);
        }

        return lres;
    }
}

#ifndef NO_NOTIFYSUBCLASSWNDPROC

//
// CNotifySubclassWndProc
//
UINT g_idFSNotify;        // the message SHChangeNotify sends

BOOL CNotifySubclassWndProc::_SubclassWindow(HWND hwnd)
{
    if (0 == g_idFSNotify)
    {
        g_idFSNotify = RegisterWindowMessage(TEXT("SubclassedFSNotify"));
    }

    DEBUG_CODE( _hwndSubclassed = hwnd; );

    return SetWindowSubclass(hwnd, _SubclassWndProc, ID_NOTIFY_SUBCLASS, (DWORD_PTR)this);
}

void CNotifySubclassWndProc::_UnsubclassWindow(HWND hwnd)
{
    RemoveWindowSubclass(hwnd, _SubclassWndProc, ID_NOTIFY_SUBCLASS);
}

LRESULT CNotifySubclassWndProc::_DefWindowProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
    return DefSubclassProc(hwnd, uMessage, wParam, lParam);
}

LRESULT CALLBACK CNotifySubclassWndProc::_SubclassWndProc(
                                  HWND hwnd, UINT uMessage, 
                                  WPARAM wParam, LPARAM lParam,
                                  UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    CNotifySubclassWndProc* pObj = (CNotifySubclassWndProc*)dwRefData;

    if (pObj)
    {
        if (uMessage == g_idFSNotify)
        {
            LPSHChangeNotificationLock pshcnl;
            LPITEMIDLIST *ppidl;
            LONG lEvent;

            if (g_fNewNotify && (wParam || lParam))
            {
                // New style of notifications need to lock and unlock in order to free the memory...
                pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD) lParam, &ppidl, &lEvent);
                if (pshcnl)
                {
                    pObj->OnChange(lEvent, ppidl[0], ppidl[1]);
                }
            }
            else
            {
                lEvent = (DWORD) lParam; // process id's are 32bits even in WIN64
                ppidl = (LPITEMIDLIST*)wParam;
                pshcnl = NULL;
                if (ppidl)
                {
                    pObj->OnChange(lEvent, ppidl[0], ppidl[1]);
                }
            }

            if (pshcnl)
            {
                SHChangeNotification_Unlock(pshcnl);
            }

            return 0;
        }
        else
        {
            return pObj->_DefWindowProc(hwnd, uMessage, wParam, lParam);
        }
    }
    else
    {
        return DefSubclassProc(hwnd, uMessage, wParam, lParam);
    }
}

void CNotifySubclassWndProc::_FlushNotifyMessages(HWND hwnd)
{
    MSG msg;

    ASSERT(hwnd == _hwndSubclassed);

    // This SHChangeNotify calls flushes notifications
    // via PostMessage, so I need to remove them
    // myself and process them immediately...
    //
    SHChangeNotify(0, SHCNF_FLUSH, NULL, NULL);

    while (PeekMessage(&msg, hwnd, g_idFSNotify, g_idFSNotify, PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

void CNotifySubclassWndProc::_RegisterWindow(HWND hwnd, LPCITEMIDLIST pidl, long lEvents,
                                             UINT uFlags/* = SHCNRF_ShellLevel | SHCNRF_InterruptLevel*/)
{
    ASSERT(0 != g_idFSNotify);

    // We only register if there's something to listen to
    //
    if (0==_uRegister)
    {
        // Since we don't know what this pidl actually points to,
        // we have to register to listen to everything that might affect it...
        //
        _uRegister = RegisterNotify(hwnd, g_idFSNotify, pidl, lEvents, uFlags, TRUE);

        ASSERT(hwnd == _hwndSubclassed);
    }
}

void CNotifySubclassWndProc::_UnregisterWindow(HWND hwnd)
{
    if (_uRegister)
    {
        ASSERT(hwnd == _hwndSubclassed);

        // Avoid getting reentered...
        UINT uRegister = _uRegister;
        _uRegister = 0;
        SHChangeNotifyDeregister(uRegister);
    }
}

#endif  // NO_NOTIFYSUBCLASSWNDPROC
