#include "shellprv.h"
#include "duiview.h"
#include "duidrag.h"


CDUIDropTarget::CDUIDropTarget()
{
    _cRef = 1;
    _pDT = NULL;
    _pNextDT = NULL;
}

CDUIDropTarget::~CDUIDropTarget()
{
    _Cleanup();
}

HRESULT CDUIDropTarget::QueryInterface (REFIID riid, void **ppv)
{
    static const QITAB qit[] =
    {
        QITABENT(CDUIDropTarget, IDropTarget),
        { 0 },
    };

    return QISearch(this, qit, riid, ppv);
}

ULONG CDUIDropTarget::AddRef (void)
{
    return ++_cRef;
}

ULONG CDUIDropTarget::Release (void)
{
    if (--_cRef == 0) {
        delete this;
        return 0;
    }

    return _cRef;
}


// Called by duser / directui to get the IDropTarget interface for the element
// the mouse just moved over.  It is important to understand the sequencing
// calls.  Initialize is called BEFORE DragLeave is called on the previous element's
// IDropTarget, so we can't switch out _pDT right away.  Instead, we'll store the
// new IDropTarget in _pNextDT and then in DragEnter, we'll move it over to _pDT.
//
// The sequence looks like this:
//
//    Initialize()    for first element (bumps ref count to 2)
//    DragEnter
//    DragMove

//    Initialize()    for second element (bumps ref count to 3)
//    DragLeave       for first element
//    Release         for first element  (decrements ref count to 2)

//    DragEnter       for second element

HRESULT CDUIDropTarget::Initialize (LPITEMIDLIST pidl, HWND hWnd, IDropTarget **pdt)
{
    ASSERT(_pNextDT == NULL);

    if (pidl)
    {
        SHGetUIObjectFromFullPIDL(pidl, hWnd, IID_PPV_ARG(IDropTarget, &_pNextDT));
    }

    QueryInterface (IID_PPV_ARG(IDropTarget, pdt));

    return S_OK;
}

VOID CDUIDropTarget::_Cleanup ()
{
    if (_pDT)
    {
        _pDT->Release();
        _pDT = NULL;
    }
}

STDMETHODIMP CDUIDropTarget::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
    if ((_pDT != _pNextDT) || (_cRef == 2))
    {
        _pDT = _pNextDT;
        _pNextDT = NULL;

        if (_pDT)
        {
            _pDT->DragEnter (pDataObj, grfKeyState, ptl, pdwEffect);
        }
        else
        {
            *pdwEffect = DROPEFFECT_NONE;
        }

        POINT pt;
        GetCursorPos(&pt);
        DAD_DragEnterEx2 (NULL, pt, pDataObj);
    }

    return S_OK;
}

STDMETHODIMP CDUIDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
    if (_pDT)
    {
        _pDT->DragOver (grfKeyState, ptl, pdwEffect);
    }
    else
    {
        *pdwEffect = DROPEFFECT_NONE;
    }

    POINT pt;
    GetCursorPos(&pt);
    DAD_DragMove (pt);

    return S_OK;
}

STDMETHODIMP CDUIDropTarget::DragLeave(void)
{
    if (_pDT || (_cRef == 2))
    {
        if (_pDT)
        {
            _pDT->DragLeave ();
        }

        DAD_DragLeave();
        _Cleanup();
    }

    return S_OK;
}

STDMETHODIMP CDUIDropTarget::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
    POINT pt = {ptl.x, ptl.y};
    HRESULT hr = S_OK;

    if (_pDT)
    {
        hr = _pDT->Drop (pDataObj, grfKeyState, ptl, pdwEffect);
    }

    return hr;
}