#include "precomp.hxx"

#include "process.h"
#include "shellapi.h"
#include "urlmon.h"

class CBindStatusCallback : public IBindStatusCallback, public ICodeInstall
{
public:
    // IUnknown methods
    STDMETHODIMP         QueryInterface(REFIID riid,void ** ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // IBindStatusCallback methods
    STDMETHODIMP         OnStartBinding(DWORD grfBSCOption, IBinding* pbinding);
    STDMETHODIMP         GetPriority(LONG* pnPriority);
    STDMETHODIMP         OnLowResource(DWORD dwReserved);
    STDMETHODIMP         OnProgress(ULONG ulProgress,
                                    ULONG ulProgressMax,
                                    ULONG ulStatusCode,
                                    LPCWSTR szStatusText);

    STDMETHODIMP         OnStopBinding(HRESULT hrResult, LPCWSTR szError);
    STDMETHODIMP         GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo);
    STDMETHODIMP         OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pfmtetc,
                                         STGMEDIUM* pstgmed);
    STDMETHODIMP         OnObjectAvailable(REFIID riid, IUnknown* punk);

    // ICodeInstall methods
    STDMETHODIMP         GetWindow(REFGUID guidReason, HWND * phwnd);
    STDMETHODIMP         OnCodeInstallProblem(ULONG ulStatusCode,
                                              LPCWSTR szDestination,
                                              LPCWSTR szSource,
                                              DWORD dwReserved);

    // constructors/destructors
    CBindStatusCallback();
    ~CBindStatusCallback();

private:
    IBindStatusCallback * m_pIBSC;
    long    _cRef;
    HRESULT _hr;
};

extern HINSTANCE ghInstance;

#include "..\appmgr\resource.h"

HRESULT
CAB_FILE::InstallIntoRegistry( HKEY  * RegistryKey)
{
    // UNDONE - put in dialog box informing the user that we will
    // install the cab file here.
#if 0
    TCHAR szCaption[256];
    TCHAR szBuffer[256];
    ::LoadString(ghInstance, IDS_CABCAPTION, szCaption, 256);
    ::LoadString(ghInstance, IDS_CABWARNING, szBuffer, 256);
    int iReturn = ::MessageBox(NULL, szBuffer,
                               szCaption,
                               MB_YESNO);
    if (iReturn == IDNO)
    {
        return E_FAIL;
    }
#endif
    // Go ahead and install the cab file
    IBindCtx *pBC=NULL;

    //Synchronous call with bind context.
    CBindStatusCallback *pCallback = new CBindStatusCallback();
    if (!pCallback)
    {
        exit (-1);
    }
    HRESULT hr;

    hr = CreateAsyncBindCtx(0, pCallback, NULL, &pBC);


    char * szName = GetPackageName();
    WCHAR wszName[_MAX_PATH];
    MultiByteToWideChar(CP_ACP, 0, szName, -1, wszName, sizeof(wszName)/sizeof(wszName[0]));

    IUnknown* punk=NULL;
#if 1
    typedef HRESULT (STDAPICALLTYPE *PFNASYNCGETCLASSBITS)(
                                                          REFCLSID rclsid,                    // CLSID
                                                          LPCWSTR, LPCWSTR,
                                                          DWORD dwFileVersionMS,              // CODE=http://foo#Version=a,b,c,d
                                                          DWORD dwFileVersionLS,              // MAKEDWORD(c,b) of above
                                                          LPCWSTR szURL,                      // CODE= in INSERT tag
                                                          IBindCtx *pbc,                      // bind ctx
                                                          DWORD dwClsContext,                 // CLSCTX flags
                                                          LPVOID pvReserved,                  // Must be NULL
                                                          REFIID riid,                        // Usually IID_IClassFactory
                                                          DWORD flags);

    PFNASYNCGETCLASSBITS pfnAsyncGetClassBits=NULL;

    HINSTANCE hDll;

    hDll = LoadLibraryEx("URLMON.DLL", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);

    if (hDll != 0)
    {
        pfnAsyncGetClassBits = (PFNASYNCGETCLASSBITS)
                               GetProcAddress(hDll, "AsyncGetClassBits");

        if (pfnAsyncGetClassBits == 0)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());

            IUnknown* punk=NULL;
            hr=CoGetClassObjectFromURL (
                                       IID_IUnknown,
                                       wszName,
                                       (DWORD) -1,(DWORD) -1, L"", pBC, CLSCTX_SERVER, NULL, IID_IUnknown, (void**) &punk);
            if (punk)
            {
                punk->Release();
            }

        }
        else
        {
            hr = (*pfnAsyncGetClassBits)(
                                        IID_IUnknown,           // bogus CLSID
                                        NULL, NULL,
                                        (DWORD) -1,(DWORD) -1,  // don't care about version #
                                        wszName,                // URL/Path
                                        pBC,                    // Bind context with IBSC
                                        CLSCTX_SERVER,
                                        NULL,
                                        IID_IClassFactory,
                                        1);                     // 1 == CD_FLAGS_FORCE_DOWNLOAD
        }
        FreeLibrary(hDll);
    }
#else
    hr=CoGetClassObjectFromURL (
                               IID_IUnknown,
                               wszName,
                               (DWORD) -1,(DWORD) -1, L"", pBC, CLSCTX_SERVER, NULL, IID_IUnknown, (void**) &punk);
    if (punk)
    {
        punk->Release();
    }
#endif
    if (hr==MK_S_ASYNCHRONOUS)
    {
        MSG msg;
        while (GetMessage(&msg, NULL, 0,0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        hr=msg.wParam;
    }

    if (hr==REGDB_E_CLASSNOTREG)
    {
        hr=S_OK; // Ignore instantiation error since we are asking for IID_IUnknown...
    }

    pCallback->Release();
    pBC->Release();

    //    i = _spawnlp( P_WAIT,
    //                  "cabinst.exe",     // argument
    //                  GetPackageName(),  // argument
    //                  0 );               // argument

    return hr;
}

HRESULT
CAB_FILE::InitRegistryKeyToInstallInto(
    HKEY   * phKey )
{
    return CreateMappedRegistryKey( phKey );
}

HRESULT
CAB_FILE::RestoreRegistryKey( HKEY * phKey)
{
    return RestoreMappedRegistryKey( phKey);
}

HRESULT
CAB_FILE::DeleteTempKey(HKEY hKey, FILETIME ftLow, FILETIME ftHigh)
{
    CleanMappedRegistryKey(hKey, ftLow, ftHigh);
    return S_OK;
}

// ===========================================================================
//                     CBindStatusCallback Implementation
// ===========================================================================

//+---------------------------------------------------------------------------
//
//  Method:     CBindStatusCallback::CBindStatusCallback
//
//  Synopsis:   Creates a bind status callback object.
//
//----------------------------------------------------------------------------
CBindStatusCallback::CBindStatusCallback()
: _cRef(1), _hr(MK_S_ASYNCHRONOUS)
{
    CreateStdProgressIndicator(GetDesktopWindow(), NULL, NULL, &m_pIBSC);
}

CBindStatusCallback::~CBindStatusCallback()
{
    m_pIBSC->Release();
}

//+---------------------------------------------------------------------------
//
//  Method:     CBindStatusCallback::QueryInterface
//
//  Synopsis:   Gets an interface pointer to the bind status callback object.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::QueryInterface(REFIID riid, void** ppv)
{
    HRESULT hr = S_OK;

    *ppv = NULL;

    if (IsEqualIID(riid, IID_IUnknown) ||
        IsEqualIID(riid, IID_IBindStatusCallback))
    {
        AddRef();
        *ppv = (IBindStatusCallback *) this;
    }
    else
        if (IsEqualIID(riid, IID_ICodeInstall))
    {
        AddRef();
        *ppv = (ICodeInstall *) this;
    }
    else
    {
        hr = E_NOINTERFACE;
    }
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CBindStatusCallback::AddRef
//
//  Synopsis:   Increments the reference count.
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CBindStatusCallback::AddRef()
{
    InterlockedIncrement((long *) &_cRef);
    return _cRef;
}

//+---------------------------------------------------------------------------
//
//  Method:     CBindStatusCallback::Release
//
//  Synopsis:   Decrements the reference count.
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CBindStatusCallback::Release()
{
    LONG count = _cRef - 1;

    if (0 == InterlockedDecrement((long *) &_cRef))
    {
        delete this;
        count = 0;
    }

    return count;
}

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStartBinding
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnStartBinding(DWORD grfBSCOption, IBinding* pbinding)
{
    return m_pIBSC->OnStartBinding(grfBSCOption, pbinding);
}

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetPriority
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::GetPriority(LONG* pnPriority)
{
    return m_pIBSC->GetPriority(pnPriority);
}

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnLowResource
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnLowResource(DWORD dwReserved)
{
    return m_pIBSC->OnLowResource(dwReserved);
}

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStopBinding
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnStopBinding(HRESULT hrStatus, LPCWSTR pszError)
{
    _hr = hrStatus;
    PostQuitMessage(hrStatus);
    return m_pIBSC->OnStopBinding(hrStatus, pszError);
}

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetBindInfo
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo)
{
    return m_pIBSC->GetBindInfo(pgrfBINDF, pbindInfo);
}

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnDataAvailable
// This function is called whenever data starts arriving. When the file download is
// complete then the BSCF_LASTDATANOTIFICATION comes and you can get the local cached
// File Name.
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnDataAvailable
(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC* pfmtetc,
STGMEDIUM* pstgmed
)
{
    return m_pIBSC->OnDataAvailable(grfBSCF, dwSize, pfmtetc, pstgmed);
}


// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnObjectAvailable
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnObjectAvailable(REFIID riid, IUnknown* punk)
{
    return m_pIBSC->OnObjectAvailable(riid, punk);
}

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnProgress
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnProgress
(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR pwszStatusText
)
{
    return m_pIBSC->OnProgress(ulProgress, ulProgressMax, ulStatusCode, pwszStatusText);
}

STDMETHODIMP
CBindStatusCallback::GetWindow(REFGUID guidReason, HWND * phwnd)
{
    *phwnd = GetDesktopWindow();
    return S_OK;
}

STDMETHODIMP
CBindStatusCallback::OnCodeInstallProblem(ULONG ulStatusCode,
                                          LPCWSTR szDestination,
                                          LPCWSTR szSource,
                                          DWORD dwReserved)
{
    HRESULT hr = E_ABORT;
    switch (ulStatusCode)
    {
    case CIP_OLDER_VERSION_EXISTS:
    case CIP_NEWER_VERSION_EXISTS:
    case CIP_NAME_CONFLICT:
    case CIP_TRUST_VERIFICATION_COMPONENT_MISSING:
        hr = S_OK;
        break;
        //case CIP_DRIVE_FULL:
    case CIP_ACCESS_DENIED:
        default:
        break;
    }
    return hr;
}