//
//  This file contains some shell specific property sheet related code,
// which includes:
//  1. The logic which lets shell extensions add pages.
//  2. The callback function to be called by those shell extensions.
// but does not include:
//  1. The property sheet UI code (should be in COMMCTRL).
//  2. The file system specific property sheet pages.
//
#include "shellprv.h"
#pragma  hdrstop

#ifdef CAIRO_DS
#include "dsdata.h"
#endif

//
//  This function is a callback function from property sheet page extensions.
//
BOOL CALLBACK _AddPropSheetPage(HPROPSHEETPAGE hpage, LPARAM lParam)
{
    PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;

    if (ppsh->nPages < MAX_FILE_PROP_PAGES)
    {
        ppsh->phpage[ppsh->nPages++] = hpage;
        return TRUE;
    }

    return FALSE;
}


//
//  This function enumerates all the property sheet page extensions for
// specified class and let them add pages.
//
VOID DCA_AppendClassSheetInfo(HDCA hdca, HKEY hkeyProgID, LPPROPSHEETHEADER ppsh, LPDATAOBJECT pdtobj)
{
    int i;
    for (i = 0; i < DCA_GetItemCount(hdca); i++)
    {
        LPSHELLEXTINIT psei;
        HRESULT hres = DCA_CreateInstance(hdca, i, &IID_IShellExtInit, &psei);

        if (hres==NOERROR)
        {
            IShellPropSheetExt * pspse;
            if (SUCCEEDED(psei->lpVtbl->Initialize(psei, NULL, pdtobj, hkeyProgID))
              && SUCCEEDED(psei->lpVtbl->QueryInterface(psei, &IID_IShellPropSheetExt, &pspse)))
            {
                pspse->lpVtbl->AddPages(pspse, _AddPropSheetPage, (LPARAM)ppsh);
                pspse->lpVtbl->Release(pspse);
            }
            psei->lpVtbl->Release(psei);
        }
    }
}

HWND FindStubForPidl(LPCITEMIDLIST pidl)
{
    HWND hwnd;

    for (hwnd = FindWindow(c_szStubWindowClass, NULL); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT))
    {
        TCHAR szClass[80];

        // find stub windows only
        GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
        if (lstrcmpi(szClass, c_szStubWindowClass) == 0)
        {
            int iClass;
            HANDLE hClassPidl;
            DWORD dwProcId;

            GetWindowThreadProcessId(hwnd, &dwProcId);

            hClassPidl = (HANDLE)SendMessage(hwnd, STUBM_GETDATA, 0, 0);
            if (hClassPidl)
            {
                LPBYTE lpb;

                lpb = (LPBYTE)SHLockShared(hClassPidl, dwProcId);

                if (lpb)
                {
                    iClass = *(int *)lpb;

                    if (iClass == SHELL_PROPSHEET_STUB_CLASS &&
                        ILIsEqual(pidl, (LPITEMIDLIST)(lpb+SIZEOF(int))) )
                    {
                        SHUnlockShared(lpb);
                        return hwnd;
                    }
                    SHUnlockShared(lpb);
                }
            }
        }
    }
    return NULL;
}

HWND FindOtherStub(HIDA hida)
{
    LPITEMIDLIST pidl;
    HWND hwnd = NULL;

    if (hida && (HIDA_GetCount(hida) == 1) && (NULL != (pidl = HIDA_ILClone(hida, 0)))) {
        hwnd = FindStubForPidl(pidl);
        ILFree(pidl);
    }

    return hwnd;
}

HANDLE StuffStubWindowWithPidl(HWND hwnd, LPITEMIDLIST pidlT)
{
    DWORD dwProcId;
    HANDLE  hSharedClassPidl;
    UINT uidlSize;

    uidlSize = ILGetSize(pidlT);
    GetWindowThreadProcessId(hwnd, &dwProcId);

    hSharedClassPidl = SHAllocShared(NULL, SIZEOF(int)+uidlSize, dwProcId);
    if (hSharedClassPidl)
    {
        LPBYTE lpb = SHLockShared(hSharedClassPidl, dwProcId);
        if (lpb)
        {
            *(int *)lpb = SHELL_PROPSHEET_STUB_CLASS;
            memcpy(lpb+SIZEOF(int),pidlT, uidlSize);
            SHUnlockShared(lpb);
            SendMessage(hwnd, STUBM_SETDATA, (WPARAM)hSharedClassPidl, 0);
            return hSharedClassPidl;
        }
        SHFreeShared(hSharedClassPidl, dwProcId);
    }

    return NULL;
}

HANDLE StuffStubWindow(HWND hwnd, HIDA hida)
{
    LPITEMIDLIST pidlT = NULL;
    HANDLE hClassPidl = NULL;

    if (hida && (HIDA_GetCount(hida) == 1) && (NULL != (pidlT = HIDA_ILClone(hida, 0)))) {
        hClassPidl = StuffStubWindowWithPidl(hwnd, pidlT);
        ILFree(pidlT);
    }
    return hClassPidl;
}

BOOL _IsAnyDuplicatedKey(HKEY ahkeys[], UINT ckeys, HKEY hkey)
{
    UINT ikey;
    for (ikey=0; ikey<ckeys; ikey++)
    {
        if (ahkeys[ikey]==hkey) {
            return TRUE;
        }
    }
    return FALSE;
}

BOOL SHOpenPropSheet(LPCTSTR pszCaption,
                     HKEY ahkeys[], UINT ckeys,
                     const CLSID * pclsidDef,
                     LPDATAOBJECT pdtobj, IShellBrowser * psb,
                     LPCTSTR pStartPage)
{
    BOOL fSuccess = FALSE;
    PROPSHEETHEADER psh;
    HPROPSHEETPAGE ahpage[MAX_FILE_PROP_PAGES];
    HWND hwnd;
    STGMEDIUM medium;
    HANDLE hClassPidl = NULL;
    HDCA hdca = NULL;

    DataObj_GetHIDA(pdtobj, &medium);
#ifdef CAIRO_DS
    if (!medium.hGlobal)
    {
        DataObj_GetDS_HIDA (pdtobj, &medium);
    }
#endif
    if (medium.hGlobal)
    {
        if (NULL != (hwnd = FindOtherStub(medium.hGlobal)))
        {
            SHReleaseStgMedium(&medium);
            SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
            return TRUE;
        }
        else if (NULL != (hwnd = _CreateStubWindow()))
        {
            hClassPidl = StuffStubWindow(hwnd, medium.hGlobal);
        }
        HIDA_ReleaseStgMedium(NULL, &medium);
    }

    psh.hwndParent = hwnd;
    psh.dwSize = SIZEOF(psh);
    psh.dwFlags = PSH_PROPTITLE;
    psh.hInstance = HINST_THISDLL;
    psh.pszCaption = pszCaption;
    psh.nPages = 0;     // incremented in callback
    psh.nStartPage = 0;
    psh.phpage = ahpage;
    if (pStartPage)
    {
        psh.dwFlags |= PSH_USEPSTARTPAGE;
        psh.pStartPage = pStartPage;
    }

    hdca = DCA_Create();
    if (hdca)
    {
        UINT ikey;
        //
        // Always add this default extention at the top, if any.
        //
        if (pclsidDef)
        {
            DCA_AddItem(hdca, pclsidDef);
        }

        for (ikey=0; ikey<ckeys; ikey++)
        {
            if (ahkeys[ikey] && !_IsAnyDuplicatedKey(ahkeys, ikey, ahkeys[ikey]))
            {
                DCA_AddItemsFromKey(hdca, ahkeys[ikey], c_szPropSheet);
            }
        }

        // Notes: ahkeys[ckeys-1] as hkeyProgID
        Assert(ckeys);
        DCA_AppendClassSheetInfo(hdca, ahkeys[ckeys-1], &psh, pdtobj);
        DCA_Destroy(hdca);
    }

    // Open the property sheet, only if we have some pages.
    if (psh.nPages > 0)
    {
        _try
        {
            if (PropertySheet(&psh) >= 0)   // IDOK or IDCANCEL (< 0 is error)
                fSuccess = TRUE;
        }
        _except(UnhandledExceptionFilter(GetExceptionInformation()))
        {
            DebugMsg(DM_ERROR, TEXT("PRSHT: Fault in property sheet"));
        }
    }
    else
    {
        ShellMessageBox(HINST_THISDLL, NULL,
                        MAKEINTRESOURCE(IDS_NOPAGE),
                        MAKEINTRESOURCE(IDS_DESKTOP),
                        MB_OK|MB_ICONHAND);
    }

    // clean up the stub window and data
    SHFreeShared(hClassPidl,GetCurrentProcessId());
    if (psh.hwndParent)
        DestroyWindow(psh.hwndParent);

    return fSuccess;
}