#include "shellprv.h"
#pragma  hdrstop

// Must have access to:
// IID_IPrinterFolder & IID_IFolderNotify interfaces
// declared in windows\inc\winprtp.h
//
#include <initguid.h>
#include <winprtp.h>

#include "w32utils.h"
#include "dpa.h"
#include <msprintx.h>
#include "ids.h"
#include "printer.h"
#include "copy.h"
#include "fstreex.h"
#include "datautil.h"
#include "infotip.h"
#include "idldrop.h"
#include "ovrlaymn.h"
#include "netview.h"
#include "prnfldr.h"

// thread data param
typedef struct {
    CIDLDropTarget *pdt;
    IStream     *pstmDataObj;
    IDataObject *pdtobj;
    DWORD        grfKeyState;
    POINTL       pt;
    DWORD        dwEffect;
} PRINT_DROP_THREAD;

class CPrinterFolderDropTarget : public CIDLDropTarget
{
    friend HRESULT CPrinterFolderDropTarget_CreateInstance(HWND hwnd, IDropTarget **ppdropt);
public:
    CPrinterFolderDropTarget(HWND hwnd) : CIDLDropTarget(hwnd) { };

    // IDropTarget methods overwirte
    STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
    STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);

    static STDMETHODIMP _HIDATestForRmPrns( LPIDA pida, int * pcRPFs, int * pcNonRPFs );
    static void _FreePrintDropData(PRINT_DROP_THREAD *pthp);
    static DWORD CALLBACK _ThreadProc(void *pv);
};

STDMETHODIMP CPrinterFolderDropTarget::_HIDATestForRmPrns(LPIDA pida, int *pcRPFs, int *pcNonRPFs)
{
    // check to see if any of the ID's are remote printers....
    for (UINT i = 0; i < pida->cidl; i++)
    {
        LPITEMIDLIST pidlTo = IDA_ILClone(pida, i);
        if (pidlTo)
        {
            LPCITEMIDLIST pidlRemainder = NULL;
            // *pidlRemainder will be NULL for remote print folders,
            // and non-NULL for printers under remote print folders
            if (NET_IsRemoteRegItem(pidlTo, CLSID_Printers, &pidlRemainder)) // && (pidlRemainder->mkid.cb == 0))
            {
                (*pcRPFs)++;
            }
            else
            {
                (*pcNonRPFs)++;
            }
            ILFree(pidlTo);
        }
    }

    return S_OK;
}

STDMETHODIMP CPrinterFolderDropTarget::DragEnter(IDataObject * pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    // We allow printer shares to be dropped for installing
    // But we don't want to spend the time on DragEnter finding out if it's
    // a printer share, so allow drops of any net resource or HIDA
    // REVIEW: Actually, it wouldn't take long to check the first one, but
    // sequencing through everything does seem like a pain.

    // let the base-class process it now to save away the pdwEffect
    CIDLDropTarget::DragEnter(pdtobj, grfKeyState, pt, pdwEffect);

    // are we dropping on the background ? Do we have the IDLIST clipformat ?
    if (m_dwData & DTID_HIDA)
    {
        int cRPFs = 0;
        int cNonRPFs = 0;
        
        STGMEDIUM medium;
        FORMATETC fmte = {g_cfNetResource, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
        
        LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
        if (pida)
        {
            _HIDATestForRmPrns( pida, &cRPFs, &cNonRPFs );
            HIDA_ReleaseStgMedium(pida, &medium);
        }

        // if we have no Remote printers or we have any non "remote printers"
        // and we have no other clipformat to test...
        if ((( cRPFs == 0 ) || ( cNonRPFs != 0 )) && !( m_dwData & DTID_NETRES ))
        {
            // the Drop code below only handles drops for HIDA format on NT
            // and only if all off them are Remote Printers
            *pdwEffect &= ~DROPEFFECT_LINK;
        }
    }   

    if ((m_dwData & DTID_NETRES) || (m_dwData & DTID_HIDA))
    {
        *pdwEffect &= DROPEFFECT_LINK;
    }
    else
    {
        *pdwEffect = DROPEFFECT_NONE;
    }

    m_dwEffectLastReturned = *pdwEffect;
    return S_OK;
}

void CPrinterFolderDropTarget::_FreePrintDropData(PRINT_DROP_THREAD *pthp)
{
    if (pthp->pstmDataObj)
        pthp->pstmDataObj->Release();

    if (pthp->pdtobj)
        pthp->pdtobj->Release();

    pthp->pdt->Release();
    LocalFree((HLOCAL)pthp);
}

DWORD CALLBACK CPrinterFolderDropTarget::_ThreadProc(void *pv)
{
    PRINT_DROP_THREAD *pthp = (PRINT_DROP_THREAD *)pv;
    STGMEDIUM medium;
    HRESULT hres = E_FAIL;
    FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};

    CoGetInterfaceAndReleaseStream(pthp->pstmDataObj, IID_IDataObject, (void **)&pthp->pdtobj);
    pthp->pstmDataObj = NULL;

    if (pthp->pdtobj == NULL)
    {
        _FreePrintDropData(pthp);
        return 0;
    }

    // First try to drop as a link to a remote print folder
    LPIDA pida = DataObj_GetHIDA(pthp->pdtobj, &medium);
    if (pida)
    {
        // Make sure that if one item in the dataobject is a
        // remote print folder, that they are all remote print folders.

        // If none are, we just give up on dropping as a RPF link, and
        // fall through to checking for printer shares via the
        // NETRESOURCE clipboard format, below.
        int cRPFs = 0, cNonRPFs = 0;
        
        _HIDATestForRmPrns( pida, &cRPFs, &cNonRPFs );

        if ((cRPFs > 0) && (cNonRPFs == 0))
        {
            // All the items in the dataobject are remote print folders or
            // printers under remote printer folders
            for (UINT i = 0; i < pida->cidl; i++)
            {
                LPITEMIDLIST pidlTo = IDA_ILClone(pida, i);
                if (pidlTo)
                {
                    LPCITEMIDLIST pidlRemainder; // The part after the remote regitem
                    NET_IsRemoteRegItem(pidlTo, CLSID_Printers, &pidlRemainder);
                    if (ILIsEmpty(pidlRemainder))
                    {
                        // This is a remote printer folder.  Drop a link to the
                        // 'PrintHood' directory

                        IShellFolder2 *psf = CPrintRoot_GetPSF();
                        if (psf)
                        {
                            IDropTarget *pdt;
                            hres = psf->CreateViewObject(pthp->pdt->_GetWindow(),
                                                                 IID_PPV_ARG(IDropTarget, &pdt));
                            if (SUCCEEDED(hres))
                            {
                                pthp->dwEffect = DROPEFFECT_LINK;
                                hres = SHSimulateDrop(pdt, pthp->pdtobj, pthp->grfKeyState, &pthp->pt, &pthp->dwEffect);
                                pdt->Release();
                            }
                        }
                    }
                    else
                    {
                        TCHAR szPrinter[MAX_PATH];

                        SHGetNameAndFlags(pidlTo, SHGDN_FORPARSING, szPrinter, ARRAYSIZE(szPrinter), NULL);
                        //
                        // Setup if not the add printer wizard.
                        //
                        if (lstrcmpi(szPrinter, c_szNewObject))
                        {
                            LPITEMIDLIST pidl = Printers_PrinterSetup(pthp->pdt->_GetWindow(), MSP_NETPRINTER, szPrinter, NULL);
                            if (pidl)
                                ILFree(pidl);
                        }

                        // make sure we set hres to S_OK, so we don't break the main loop
                        hres = S_OK;
                    }
                    ILFree(pidlTo);

                    if (FAILED(hres))
                        break;
                }
            }
            HIDA_ReleaseStgMedium(pida, &medium);
            SHChangeNotifyHandleEvents();       // force update now
            goto Cleanup;
        }
        else if ((cRPFs > 0) && (cNonRPFs > 0))
        {
            // At least one, but not all, item(s) in this dataobject
            // was a remote printer folder.  Jump out now.
            goto Cleanup;
        }

        // else none of the items in the dataobject were remote print
        // folders, so fall through to the NETRESOURCE parsing
    }

    // Reset FORMATETC to NETRESOURCE clipformat for next GetData call
    fmte.cfFormat = g_cfNetResource;

    // DragEnter only allows network resources to be DROPEFFECT_LINKed
    ASSERT(S_OK == pthp->pdtobj->QueryGetData(&fmte));

    if (SUCCEEDED(pthp->pdtobj->GetData(&fmte, &medium)))
    {
        LPNETRESOURCE pnr = (LPNETRESOURCE)LocalAlloc(LPTR, 1024);
        if (pnr)
        {
            BOOL fNonPrnShare = FALSE;
            UINT cItems = SHGetNetResource(medium.hGlobal, (UINT)-1, NULL, 0);
            for (UINT iItem = 0; iItem < cItems; iItem++)
            {
                if (SHGetNetResource(medium.hGlobal, iItem, pnr, 1024) &&
                    pnr->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE &&
                    pnr->dwType == RESOURCETYPE_PRINT)
                {
                    LPITEMIDLIST pidl = Printers_PrinterSetup(pthp->pdt->_GetWindow(),
                               MSP_NETPRINTER, pnr->lpRemoteName, NULL);

                    if (pidl)
                        ILFree(pidl);
                }
                else
                {
                    if (!fNonPrnShare)
                    {
                        // so we don't get > 1 of these messages per drop
                        fNonPrnShare = TRUE;

                        // let the user know that they can't drop non-printer
                        // shares into the printers folder
                        SetForegroundWindow(pthp->pdt->_GetWindow());
                        ShellMessageBox(HINST_THISDLL,
                            pthp->pdt->_GetWindow(),
                            MAKEINTRESOURCE(IDS_CANTINSTALLRESOURCE), NULL,
                            MB_OK|MB_ICONINFORMATION,
                            (LPTSTR)pnr->lpRemoteName);
                    }
                }
            }

            LocalFree((HLOCAL)pnr);
        }
        ReleaseStgMedium(&medium);
    }

Cleanup:
    _FreePrintDropData(pthp);
    return 0;
}

STDMETHODIMP CPrinterFolderDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    *pdwEffect = DROPEFFECT_LINK;

    HRESULT hr = CIDLDropTarget::DragDropMenu(DROPEFFECT_LINK, pdtobj,
        pt, pdwEffect, NULL, NULL, MENU_PRINTOBJ_NEWPRN_DD, grfKeyState);

    if (*pdwEffect)
    {
        PRINT_DROP_THREAD *pthp = (PRINT_DROP_THREAD *)LocalAlloc(LPTR, SIZEOF(*pthp));
        if (pthp)
        {
            pthp->grfKeyState = grfKeyState;
            pthp->pt          = pt;
            pthp->dwEffect    = *pdwEffect;

            CoMarshalInterThreadInterfaceInStream(IID_IDataObject, (IUnknown *)pdtobj, &pthp->pstmDataObj);

            pthp->pdt = this;
            pthp->pdt->AddRef();

            if (SHCreateThread(_ThreadProc, pthp, CTF_COINIT, NULL))
            {
                hr = S_OK;
            }
            else
            {
                _FreePrintDropData(pthp);
                hr = E_OUTOFMEMORY;
            }
        }
    }
    CIDLDropTarget::DragLeave();

    return hr;
}

STDAPI CPrinterFolderDropTarget_CreateInstance(HWND hwnd, IDropTarget **ppdropt)
{
    *ppdropt = NULL;

    HRESULT hr;
    CPrinterFolderDropTarget *ppfdt = new CPrinterFolderDropTarget(hwnd);
    if (ppfdt)
    {
        hr = ppfdt->QueryInterface(IID_PPV_ARG(IDropTarget, ppdropt));
        ppfdt->Release();
    }
    else
        hr = E_OUTOFMEMORY;
    return hr;
}