#include "local.h"
#include "../security.h"
#include "../favorite.h"
#include "resource.h"
#include "chcommon.h"
#include "cafolder.h"

#include <mluisupp.h>

#define DM_HSFOLDER 0

STDAPI  AddToFavorites(HWND hwnd, LPCITEMIDLIST pidlCur, LPCTSTR pszTitle,
                       BOOL fDisplayUI, IOleCommandTarget *pCommandTarget, IHTMLDocument2 *pDoc);

#define MAX_ITEM_OPEN 10

//////////////////////////////////////////////////////////////////////////////
//
// CCacheItem Object
//
//////////////////////////////////////////////////////////////////////////////


CCacheItem::CCacheItem() 
{
    _dwDelCookie = DEL_COOKIE_WARN;
}

CCacheItem::~CCacheItem()
{
    if (_pCFolder)
        _pCFolder->Release();          // release the pointer to the sf
}

HRESULT CCacheItem::Initialize(CCacheFolder *pCFolder, HWND hwnd, UINT cidl, LPCITEMIDLIST *ppidl)
{
    HRESULT hres = CBaseItem::Initialize(hwnd, cidl, ppidl);

    if (SUCCEEDED(hres))
    {
        _pCFolder = pCFolder;
        _pCFolder->AddRef();      // we're going to hold onto this pointer, so
    }

    return hres;
}        

HRESULT CCacheItem_CreateInstance(CCacheFolder *pCFolder, HWND hwnd,
    UINT cidl, LPCITEMIDLIST *ppidl, REFIID riid, void **ppv)
{
    HRESULT hr;

    *ppv = NULL;                 // null the out param

    CCacheItem *pHCItem = new CCacheItem;
    if (pHCItem)
    {
        hr = pHCItem->Initialize(pCFolder, hwnd, cidl, ppidl);
        if (SUCCEEDED(hr))
            hr = pHCItem->QueryInterface(riid, ppv);
        pHCItem->Release();
    }
    else
        hr = E_OUTOFMEMORY;

    return hr;
}

//////////////////////////////////
//
// IUnknown Methods...
//
HRESULT CCacheItem::QueryInterface(REFIID iid, void **ppv)
{
    HRESULT hres = CBaseItem::QueryInterface(iid, ppv);

    if (FAILED(hres) && iid == IID_ICache) 
    {
        *ppv = (LPVOID)this;    // for our friends
        AddRef();
        hres = S_OK;
    }
    return hres;
}

//////////////////////////////////
//
// IQueryInfo Methods
//
HRESULT CCacheItem::GetInfoTip(DWORD dwFlags, WCHAR **ppwszTip)
{
    return _pCFolder->_GetInfoTip(_ppidl[0], dwFlags, ppwszTip);
}

//////////////////////////////////
//
// IContextMenu Methods
//
HRESULT CCacheItem::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst,UINT idCmdLast, UINT uFlags)
{
    USHORT cItems;

    TraceMsg(DM_HSFOLDER, "hci - cm - QueryContextMenu() called.");
    
    if ((uFlags & CMF_VERBSONLY) || (uFlags & CMF_DVFILE))
    {
        cItems = MergePopupMenu(&hmenu, POPUP_CONTEXT_URL_VERBSONLY, 0, indexMenu, 
            idCmdFirst, idCmdLast);
    
    }
    else  // (uFlags & CMF_NORMAL)
    {
        UINT idResource = POPUP_CACHECONTEXT_URL;

        cItems = MergePopupMenu(&hmenu, idResource, 0, indexMenu, idCmdFirst, idCmdLast);

        if (IsInetcplRestricted(L"History"))
        {
            DeleteMenu(hmenu, RSVIDM_DELCACHE + idCmdFirst, MF_BYCOMMAND);
            _SHPrettyMenu(hmenu);
        }
    }
    if (hmenu)
        SetMenuDefaultItem(hmenu, indexMenu, MF_BYPOSITION);

    return ResultFromShort(cItems);    // number of menu items    
}

static BOOL CachevuWarningDlg(LPCEIPIDL pcei, UINT uIDWarning, HWND hwnd)
{
    TCHAR szFormat[MAX_PATH], szBuff[MAX_PATH], szTitle[MAX_PATH];

    _GetCacheItemTitle(pcei, szTitle, ARRAYSIZE(szTitle));
    MLLoadString(uIDWarning, szFormat, ARRAYSIZE(szFormat));
    wnsprintf(szBuff, ARRAYSIZE(szFormat), szFormat);

    return DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(DLG_HISTCACHE_WARNING),
                             hwnd, HistoryConfirmDeleteDlgProc, (LPARAM)szBuff) == IDYES;
} 

STDMETHODIMP CCacheItem::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
    UINT i;
    int idCmd = _GetCmdID(pici->lpVerb);
    HRESULT hres = S_OK;
    DWORD dwAction;
    BOOL fCancelCopyAndOpen = FALSE;
    BOOL fZonesUI = FALSE;
    BOOL fMustFlushNotify = FALSE;
    BOOL fBulkDelete;

    TraceMsg(DM_HSFOLDER, "hci - cm - InvokeCommand() called.");

    // ZONES SECURITY CHECK.
    //
    // We need to cycle through each action and Zone Check the URLs.
    // We pass NOUI when zone checking the URLs because we don't want info
    // displayed to the user.  We will stop when we find the first questionable
    // URL.  We will then 
    for (i = 0; (i < _cItems) && !fZonesUI; i++)
    {
        if (_ppidl[i]) 
        {
            switch (idCmd)
            {
            case RSVIDM_OPEN:
                if ((i < MAX_ITEM_OPEN))
                {
                    if (!_ZoneCheck(i, URLACTION_SHELL_VERB))
                    {
                        fZonesUI = TRUE;
                        dwAction = URLACTION_SHELL_VERB;
                    }
                }
                break;

            case RSVIDM_COPY:
                if (!_ZoneCheck(i, URLACTION_SHELL_MOVE_OR_COPY))
                {
                    fZonesUI = TRUE;
                    dwAction = URLACTION_SHELL_MOVE_OR_COPY;
                }
                break;
            }
        }
    }

    if (fZonesUI)
    {
        LPCTSTR pszUrl = _GetUrl(i-1);  // Sub 1 because of for loop above.
        if (S_OK != ZoneCheckUrl(pszUrl, dwAction, PUAF_DEFAULT|PUAF_WARN_IF_DENIED, NULL))
        {
            // The user cannot do this or does not want to do this.
            fCancelCopyAndOpen = TRUE;
        }
    }

    i = _cItems;
    fBulkDelete = i > LOTS_OF_FILES;

    // fCancelCopyAndOpen happens if the user cannot or chose not to proceed.
    while (i && !fCancelCopyAndOpen)
    {
        i--;
        if (_ppidl[i]) 
        {

            switch (idCmd)
            {
            case RSVIDM_OPEN:
                if (i >= MAX_ITEM_OPEN)
                {
                    hres = S_FALSE;
                    goto Done;
                }

                if ((CEI_CACHEENTRYTYPE((LPCEIPIDL)_ppidl[i]) & COOKIE_CACHE_ENTRY))
                {
                    ASSERT(PathFindExtension(CEI_LOCALFILENAME((LPCEIPIDL)_ppidl[i])) && \
                        !StrCmpI(PathFindExtension(CEI_LOCALFILENAME((LPCEIPIDL)_ppidl[i])),TEXT(".txt")));
                    hres = _LaunchApp(pici->hwnd, CEI_LOCALFILENAME((LPCEIPIDL)_ppidl[i]));
                }
                else
                {
                    TCHAR szDecoded[MAX_URL_STRING];
                    LPCTSTR pszUrl = _GetUrl(i);
                    if (pszUrl)
                    {
                        ConditionallyDecodeUTF8(pszUrl, szDecoded, ARRAYSIZE(szDecoded));
                        hres = _LaunchApp(pici->hwnd, szDecoded);
                    }
                    else
                    {
                        hres = E_FAIL;
                    }
                }
                break;

            case RSVIDM_ADDTOFAVORITES:
                hres = _AddToFavorites(i);
                goto Done;
            case RSVIDM_OPEN_NEWWINDOW:
                {
                    LPCTSTR pszUrl = _GetUrl(i);
                    if (pszUrl)
                    {
                        TCHAR szDecoded[MAX_URL_STRING];
                        ConditionallyDecodeUTF8(pszUrl, szDecoded, ARRAYSIZE(szDecoded));
                        LPWSTR pwszTarget;
                    
                        if (SUCCEEDED((hres = SHStrDup(szDecoded, &pwszTarget)))) {
                            hres = NavToUrlUsingIEW(pwszTarget, TRUE);
                            CoTaskMemFree(pwszTarget);
                        }
                    }
                    else
                        hres = E_FAIL;
                    goto Done;
                }
            case RSVIDM_COPY:
                OleSetClipboard((IDataObject *)this);
                goto Done;

            case RSVIDM_DELCACHE:
                // pop warning msg for cookie only once
                if ((CEI_CACHEENTRYTYPE((LPCEIPIDL)_ppidl[i]) & COOKIE_CACHE_ENTRY) &&     
                    (_dwDelCookie == DEL_COOKIE_WARN ))
                {
                    if(CachevuWarningDlg((LPCEIPIDL)_ppidl[i], IDS_WARN_DELETE_CACHE, pici->hwnd))
                        _dwDelCookie = DEL_COOKIE_YES;
                    else
                        _dwDelCookie = DEL_COOKIE_NO;
                }

                if ((CEI_CACHEENTRYTYPE((LPCEIPIDL)_ppidl[i]) & COOKIE_CACHE_ENTRY) &&     
                    (_dwDelCookie == DEL_COOKIE_NO ))
                    continue;
          
                if (DeleteUrlCacheEntry(CPidlToSourceUrl((LPCEIPIDL)_ppidl[i])))
                {
                    if (!fBulkDelete)
                    {
                        _GenerateEvent(SHCNE_DELETE, _pCFolder->_pidl, _ppidl[i], NULL);
                    }
                    fMustFlushNotify = TRUE;
                }
                else 
                    hres = E_FAIL;
                break;

            case RSVIDM_PROPERTIES:
                // NOTE: We'll probably want to split this into two cases
                // and call a function in each case
                //
                _CreatePropSheet(pici->hwnd, _ppidl[i], DLG_CACHEITEMPROP, _sPropDlgProc,
                    CEI_SOURCEURLNAME((LPCEIPIDL)_ppidl[i]));
                goto Done;

            default:
                hres = E_FAIL;
                break;
            }
            
            ASSERT(SUCCEEDED(hres));
            if (FAILED(hres))
                TraceMsg(DM_HSFOLDER, "Cachevu failed the command at: %s", CPidlToSourceUrl((LPCEIPIDL)_ppidl[i]));
        }
    }
Done:
    if (fMustFlushNotify)
    {
        if (fBulkDelete)
        {
            _GenerateEvent(SHCNE_UPDATEDIR, _pCFolder->_pidl, NULL, NULL);
        }

        SHChangeNotifyHandleEvents();
    }
    return hres;
}

//////////////////////////////////
//
// IDataObject Methods...
//

HRESULT CCacheItem::GetData(LPFORMATETC pFEIn, LPSTGMEDIUM pSTM)
{
    HRESULT hres;

#ifdef DEBUG
    TCHAR szName[64];
    if (!GetClipboardFormatName(pFEIn->cfFormat, szName, sizeof(szName)))
        wnsprintf(szName, ARRAYSIZE(szName), TEXT("#%d"), pFEIn->cfFormat);

    TraceMsg(DM_HSFOLDER, "hci - do - GetData(%s)", szName);
#endif

    pSTM->hGlobal = NULL;
    pSTM->pUnkForRelease = NULL;

    if (pFEIn->cfFormat == CF_HDROP && (pFEIn->tymed & TYMED_HGLOBAL))
        hres = _CreateHDROP(pSTM);

    else if ((pFEIn->cfFormat == g_cfPreferredEffect) && (pFEIn->tymed & TYMED_HGLOBAL))
        hres = _CreatePrefDropEffect(pSTM);

    else
        hres = DATA_E_FORMATETC;
    
    return hres;

}

HRESULT CCacheItem::QueryGetData(LPFORMATETC pFEIn)
{
#ifdef DEBUG
    TCHAR szName[64];
    if (!GetClipboardFormatName(pFEIn->cfFormat, szName, sizeof(szName)))
        wnsprintf(szName, ARRAYSIZE(szName), TEXT("#%d"), pFEIn->cfFormat);

    TraceMsg(DM_HSFOLDER, "hci - do - QueryGetData(%s)", szName);
#endif

    if (pFEIn->cfFormat == CF_HDROP            || 
        pFEIn->cfFormat == g_cfPreferredEffect)
    {
        TraceMsg(DM_HSFOLDER, "		   format supported.");
        return NOERROR;
    }
    return S_FALSE;
}

HRESULT CCacheItem::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC *ppEnum)
{
    FORMATETC Cachefmte[] = {
        {CF_HDROP,                NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
        {g_cfPreferredEffect,     NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
    };
    return SHCreateStdEnumFmtEtc(ARRAYSIZE(Cachefmte), Cachefmte, ppEnum);
}

//////////////////////////////////
//
// IExtractIconA Methods...
//
HRESULT CCacheItem::GetIconLocation(UINT uFlags, LPSTR pszIconFile, UINT ucchMax, PINT pniIcon, PUINT puFlags)
{
    if (ucchMax < 2)
        return E_FAIL;
    
    *puFlags = GIL_NOTFILENAME;
    pszIconFile[0] = '*';
    pszIconFile[1] = '\0';
    
    // "*" as the file name means iIndex is already a system icon index.
    return _pCFolder->GetIconOf(_ppidl[0], uFlags, pniIcon);
}

//////////////////////////////////////////////////////////////////////////////
//
// Helper Routines
//
//////////////////////////////////////////////////////////////////////////////

UNALIGNED const TCHAR* CCacheItem::_GetURLTitle(LPCITEMIDLIST pidl)
{
    return ::_GetURLTitle( (LPCEIPIDL) pidl);
}

LPCTSTR CCacheItem::_GetUrl(int nIndex)
{
    LPCTSTR pszUrl = NULL;
    pszUrl = CPidlToSourceUrl((LPCEIPIDL)_ppidl[nIndex]);
    return pszUrl;
}

LPCTSTR CCacheItem::_PidlToSourceUrl(LPCITEMIDLIST pidl)
{
    return CPidlToSourceUrl((LPCEIPIDL) pidl);
}


// Return value:
//               TRUE - URL is Safe.
//               FALSE - URL is questionable and needs to be re-zone checked w/o PUAF_NOUI.
BOOL CCacheItem::_ZoneCheck(int nIndex, DWORD dwUrlAction)
{
    LPCTSTR pszUrl = _GetUrl(nIndex);

    if (S_OK != ZoneCheckUrl(pszUrl, dwUrlAction, PUAF_NOUI, NULL))
        return FALSE;

    return TRUE;
}

INT_PTR CALLBACK CCacheItem::_sPropDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    LPPROPSHEETPAGE lpPropSheet = (LPPROPSHEETPAGE) GetWindowLongPtr(hDlg, DWLP_USER);
    LPCEIPIDL pcei = lpPropSheet ? (LPCEIPIDL)lpPropSheet->lParam : NULL;

    switch(message) {

        case WM_INITDIALOG: {
            SHFILEINFO sfi;
            TCHAR szBuf[80];
            
            SetWindowLongPtr(hDlg, DWLP_USER, lParam);
            pcei = (LPCEIPIDL)((LPPROPSHEETPAGE)lParam)->lParam;

            // get the icon and file type strings

            SHGetFileInfo(CEI_LOCALFILENAME(pcei), 0, &sfi, SIZEOF(sfi), SHGFI_ICON | SHGFI_TYPENAME);

            SendDlgItemMessage(hDlg, IDD_ITEMICON, STM_SETICON, (WPARAM)sfi.hIcon, 0);

            // set the info strings
            SetDlgItemText(hDlg, IDD_HSFURL, CPidlToSourceUrl((LPCEIPIDL)pcei));
            SetDlgItemText(hDlg, IDD_FILETYPE, sfi.szTypeName);

            SetDlgItemText(hDlg, IDD_FILESIZE, StrFormatByteSize(pcei->cei.dwSizeLow, szBuf, ARRAYSIZE(szBuf)));
            SetDlgItemText(hDlg, IDD_CACHE_NAME, PathFindFileName(CEI_LOCALFILENAME(pcei)));
            FileTimeToDateTimeStringInternal(&pcei->cei.ExpireTime, szBuf, ARRAYSIZE(szBuf), FALSE);
            SetDlgItemText(hDlg, IDD_EXPIRES, szBuf);
            FileTimeToDateTimeStringInternal(&pcei->cei.LastModifiedTime, szBuf, ARRAYSIZE(szBuf), FALSE);
            SetDlgItemText(hDlg, IDD_LAST_MODIFIED, szBuf);
            FileTimeToDateTimeStringInternal(&pcei->cei.LastAccessTime, szBuf, ARRAYSIZE(szBuf), FALSE);
            SetDlgItemText(hDlg, IDD_LAST_ACCESSED, szBuf);
            
            break;
        }

        case WM_DESTROY:
            {
                HICON hIcon = (HICON)SendDlgItemMessage(hDlg, IDD_ITEMICON, STM_GETICON, 0, 0);
                if (hIcon)
                    DestroyIcon(hIcon);
            }
            break;

        case WM_COMMAND:
        case WM_HELP:
        case WM_CONTEXTMENU:
            // user can't change anything, so we don't care about any messages

            break;

        default:
            return FALSE;
            
    } // end of switch
    
    return TRUE;
}

// use CEI_LOCALFILENAME to get the file name for the HDROP, but map that
// to the final file name (store in the file system) through the "FileNameMap"
// data which uses _GetURLTitle() as the final name of the file.

HRESULT CCacheItem::_CreateHDROP(STGMEDIUM *pmedium)
{
    UINT i;
    UINT cbAlloc = sizeof(DROPFILES) + sizeof(CHAR);        // header + null terminator

    for (i = 0; i < _cItems; i++)
    {
        char szAnsiUrl[MAX_URL_STRING];
        
        SHTCharToAnsi(CEI_LOCALFILENAME((LPCEIPIDL)_ppidl[i]), szAnsiUrl, ARRAYSIZE(szAnsiUrl));
        cbAlloc += sizeof(CHAR) * (lstrlenA(szAnsiUrl) + 1);
    }

    pmedium->tymed = TYMED_HGLOBAL;
    pmedium->pUnkForRelease = NULL;
    pmedium->hGlobal = GlobalAlloc(GPTR, cbAlloc);
    if (pmedium->hGlobal)
    {
        LPDROPFILES pdf = (LPDROPFILES)pmedium->hGlobal;
        LPSTR pszFiles  = (LPSTR)(pdf + 1);
        int   cchFiles  = (cbAlloc - sizeof(DROPFILES) - sizeof(CHAR));
        pdf->pFiles = sizeof(DROPFILES);
        pdf->fWide = FALSE;

        for (i = 0; i < _cItems; i++)
        {
            LPTSTR pszPath = CEI_LOCALFILENAME((LPCEIPIDL)_ppidl[i]);
            int    cchPath = lstrlen(pszPath);

            SHTCharToAnsi(pszPath, pszFiles, cchFiles);

            pszFiles += cchPath + 1;
            cchFiles -= cchPath + 1;

            ASSERT((UINT)((LPBYTE)pszFiles - (LPBYTE)pdf) < cbAlloc);
        }
        ASSERT((LPSTR)pdf + cbAlloc - 1 == pszFiles);
        ASSERT(*pszFiles == 0); // zero init alloc

        return NOERROR;

    }
    return E_OUTOFMEMORY;
}