#include "windows.h"
#include "shlwapi.h"
#include "shpriv.h"
#include "commctrl.h"
#include "resource.h"
#include "ccstock.h"
#include "shlguid.h"


// globals and classes

HINSTANCE g_hAppInst;           // instance information for the wizard
HWND g_hwndFrame;               // wizard frame (cached on startup)
IWebWizardExtension *g_pwe;        // IWizardExtension (we want to show)


// IWizardSite
// -----------
//
// This object is used by the wizard extension to navigate in and out of the
// main wizard.   When the wizard extension has finished displaying its set
// of pages (and recieves its final PSN_WIZNEXT or PSN_WIZBACK) it is 
// responsible for calling the site to navigate in and out of the 
// stack of pages.

class CWizSite : public IWizardSite, IServiceProvider
{
public:
    CWizSite(IPropertyBag *ppb);
    ~CWizSite();

    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
    STDMETHOD_(ULONG,AddRef)(void);
    STDMETHOD_(ULONG,Release)(void);

    // IWizardSite
    STDMETHODIMP GetPreviousPage(HPROPSHEETPAGE *phPage);
    STDMETHODIMP GetNextPage(HPROPSHEETPAGE *phPage);
    STDMETHODIMP GetCancelledPage(HPROPSHEETPAGE *phPage)
        { return E_NOTIMPL; }

    // IServiceProvider
    STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv);

private:
    LONG _cRef;
    IPropertyBag *_ppb;
};


// reference counting of the object

ULONG CWizSite::AddRef()
{
    return InterlockedIncrement(&_cRef);
}

ULONG CWizSite::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;

    delete this;
    return 0;
}

HRESULT CWizSite::QueryInterface(REFIID riid, void **ppv)
{
    static const QITAB qit[] = 
    {
        QITABENT(CWizSite, IWizardSite),       // IID_IWizardSite
        QITABENT(CWizSite, IServiceProvider),  // IID_IServiceProvider
        {0},
    };
    return QISearch(this, qit, riid, ppv);
}


// instance creation

CWizSite::CWizSite(IPropertyBag *ppb) :
    _cRef(1),
    _ppb(ppb)
{
    ppb->AddRef();
}

CWizSite::~CWizSite()
{
    ATOMICRELEASE(_ppb);
}

HRESULT CWizSite_CreateInstance(IPropertyBag *ppb, REFIID riid, void **ppv)
{
    CWizSite *pws = new CWizSite(ppb);
    if (!pws)
        return E_OUTOFMEMORY;

    HRESULT hr = pws->QueryInterface(riid, ppv);
    pws->Release();
    return hr;
}


// methods for returning our page range

HRESULT CWizSite::GetPreviousPage(HPROPSHEETPAGE *phPage)
{
    int i = PropSheet_IdToIndex(g_hwndFrame, IDD_WELCOME);
    *phPage = PropSheet_IndexToPage(g_hwndFrame, i);
    return S_OK;
}

HRESULT CWizSite::GetNextPage(HPROPSHEETPAGE *phPage)
{
    int i = PropSheet_IdToIndex(g_hwndFrame, IDD_DONE);
    *phPage = PropSheet_IndexToPage(g_hwndFrame, i);
    return S_OK;
}

// Service provider object

HRESULT CWizSite::QueryService(REFGUID guidService, REFIID riid, void **ppv)
{
    *ppv = NULL;                // no result yet

    if (guidService == SID_PublishingWizard)
    {
        if (riid == IID_IPropertyBag)
            return _ppb->QueryInterface(riid, ppv);
    }

    return E_FAIL;
}



// Sample dialog proc's for the welcome and done pages (these then call into
// the wizard extension to show their pages).

BOOL _HandleWizNextBack(HWND hwnd, HPROPSHEETPAGE hpage)
{
    PropSheet_SetCurSel(GetParent(hwnd), hpage, -1);
    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, -1);
    return TRUE;
}

INT_PTR _WelcomeDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch ( uMsg )
    {
        case WM_INITDIALOG:
            g_hwndFrame = GetParent(hwnd);
            return TRUE;

        case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR)lParam;             
            switch (pnmh->code)
            {
                case PSN_SETACTIVE:            
                    PropSheet_SetWizButtons(g_hwndFrame, PSWIZB_NEXT);
                    return TRUE;              

                case PSN_WIZNEXT:
                {
                    HPROPSHEETPAGE hpage;
                    g_pwe->GetFirstPage(&hpage);
                    return _HandleWizNextBack(hwnd, hpage);
                }
            }
            break;
        }
    }

    return FALSE;
}

INT_PTR _DoneDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch ( uMsg )
    {
        case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR)lParam;             
            switch (pnmh->code)
            {
                case PSN_SETACTIVE:            
                    PropSheet_SetWizButtons(g_hwndFrame, PSWIZB_FINISH | PSWIZB_BACK);
                    return TRUE;              

                case PSN_WIZBACK:
                {
                    HPROPSHEETPAGE hpage;
                    g_pwe->GetLastPage(&hpage);
                    return _HandleWizNextBack(hwnd, hpage);
                }
            }
            break;
        }
    }

    return FALSE;
}



// Our Wizard, its very simple.

#define WIZDLG(name, dlgproc, dwFlags)   \
    { MAKEINTRESOURCE(IDD_##name##), dlgproc, MAKEINTRESOURCE(IDS_##name##), MAKEINTRESOURCE(IDS_##name##_SUB), dwFlags }

const struct
{
    LPCWSTR idPage;
    DLGPROC pDlgProc;
    LPCWSTR pHeading;
    LPCWSTR pSubHeading;
    DWORD dwFlags;
}
c_pages[] =
{
    WIZDLG(WELCOME, _WelcomeDlgProc, 0x0),
    WIZDLG(DONE,    _DoneDlgProc,    0x0),
};

HRESULT _InitExtensionSite(IWizardExtension *pwe)
{
    IPropertyBag *ppb;
    HRESULT hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &ppb));
    if (SUCCEEDED(hr))
    {
        IWizardSite *pws;
        hr = CWizSite_CreateInstance(ppb, IID_PPV_ARG(IWizardSite, &pws));
        if (SUCCEEDED(hr))
        {
            IUnknown_SetSite(pwe, pws);
            pws->Release();
        }
        ppb->Release();
    }
    return hr;
}

int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    g_hAppInst = hInstance;

    CoInitialize(NULL);
    InitCommonControls();

    // For our example we will create the Publishing Wizard, it supports IWizardExtension and
    // we will host its pages within ours.

    HRESULT hr = CoCreateInstance(CLSID_WebWizardHost, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IWebWizardExtension, &g_pwe));
    if (SUCCEEDED(hr))
    {
        hr = _InitExtensionSite(g_pwe);
        if (SUCCEEDED(hr))
        {
            // Create our pages, these are placed at the top of the array,
            // the extensions are placed after.

            HPROPSHEETPAGE hpages[10] = { 0 };
            for (int i = 0; i < ARRAYSIZE(c_pages) ; i++ )
            {                           
                PROPSHEETPAGE psp = { 0 };
                psp.dwSize = sizeof(psp);
                psp.hInstance = g_hAppInst;
                psp.dwFlags = PSP_USETITLE | PSP_DEFAULT | 
                              PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE |
                              c_pages[i].dwFlags;

                psp.pszTemplate = c_pages[i].idPage;
                psp.pfnDlgProc = c_pages[i].pDlgProc;
                psp.pszTitle = TEXT("Wizard Hosting Example");
                psp.pszHeaderTitle = c_pages[i].pHeading;
                psp.pszHeaderSubTitle = c_pages[i].pSubHeading;
                hpages[i] = CreatePropertySheetPage(&psp);
            }


            // Fill out the structure for the property sheet, we indicate
            // that we want this to behave like a wizard.
        
            PROPSHEETHEADER psh = { 0 };
            psh.dwSize = sizeof(psh);
            psh.hInstance = g_hAppInst;
            psh.dwFlags = PSH_WIZARD | PSH_WIZARD97 | PSH_HEADER;
            psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER);
            psh.phpage = hpages;
            psh.nPages = i;


            // Let the extension add its pages it will append an array of
            // HPROPSHEETPAGE to the structure, it also return the count.

            UINT nPages;
            hr = g_pwe->AddPages(&hpages[i], ARRAYSIZE(hpages)-i, &nPages);
            if (SUCCEEDED(hr))
            {
                psh.nPages = i+nPages;

                TCHAR szPath[MAX_PATH];
                GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath));
                PathRenameExtension(szPath, TEXT(".htm"));

                WCHAR szURL[128];
                DWORD cch = ARRAYSIZE(szURL);
                UrlCreateFromPath(szPath, szURL, &cch, 0);

                g_pwe->SetInitialURL(szURL);

                PropertySheet(&psh);
            }
        }
        g_pwe->Release();
        g_pwe = NULL;
    }

    CoUninitialize();

    return 0;
}