/*******************************************************************************
*
*  (C) COPYRIGHT MICROSOFT CORP., 1998
*
*  TITLE:       MiniItem.Cpp
*
*  VERSION:     2.0
*
*  AUTHOR:      marke
*
*  DATE:        30 Aug, 1998
*
*  DESCRIPTION:
*   Implementation of the WIA test camera item methods.
*
*******************************************************************************/

#include <stdio.h>
#include <objbase.h>
#include <sti.h>

#ifdef TEST_PER_USER_DATA
#ifdef UNICODE
#include <userenv.h>
#endif
#endif

#include "testusd.h"

#include "defprop.h"

extern HINSTANCE g_hInst; // Global hInstance

/*******************************************************************************
*
*  ValidateDataTransferContext
*
*  DESCRIPTION:
*    Validate the data transfer context.
*
*  PARAMETERS:
*
*******************************************************************************/

HRESULT
ValidateDataTransferContext(
    PMINIDRV_TRANSFER_CONTEXT  pDataTransferContext)
{
   if (pDataTransferContext->lSize != sizeof(MINIDRV_TRANSFER_CONTEXT)) {
      WIAS_ERROR((g_hInst,"ValidateDataTransferContext, invalid data transfer context"));
      return E_INVALIDARG;;
   }

   //
   // for tymed file or hglobal, only WiaImgFmt_MEMORYBMP is allowed
   //

   if ((pDataTransferContext->tymed == TYMED_FILE) ||
       (pDataTransferContext->tymed == TYMED_HGLOBAL)) {

       if (pDataTransferContext->guidFormatID != WiaImgFmt_BMP && pDataTransferContext->guidFormatID != WiaAudFmt_WAV) {
          WIAS_ERROR((g_hInst,"ValidateDataTransferContext, invalid format"));
          return E_INVALIDARG;;
       }

   }

   //
   // for tymed CALLBACK, only WiaImgFmt_MEMORYBMP is allowed
   //

   if (pDataTransferContext->tymed == TYMED_CALLBACK) {

       if ((pDataTransferContext->guidFormatID != WiaImgFmt_BMP) &&
           (pDataTransferContext->guidFormatID != WiaImgFmt_MEMORYBMP)) {
          WIAS_ERROR((g_hInst,"AcquireDeviceData, invalid format"));
          return E_INVALIDARG;;
       }
   }


   //
   // callback is always double buffered, non-callback never is
   //

   if (pDataTransferContext->pTransferBuffer == NULL) {
       WIAS_ERROR((g_hInst, "AcquireDeviceData, invalid transfer buffer"));
       return E_INVALIDARG;
   }

   return S_OK;
}


/**************************************************************************\
* SendBitmapHeader
*
*
*
* Arguments:
*
*
*
* Return Value:
*
*    Status
*
* History:
*
*    11/17/1998 Original Version
*
\**************************************************************************/

HRESULT SendBitmapHeader(
    IWiaDrvItem                *pDrvItem,
    PMINIDRV_TRANSFER_CONTEXT   pTranCtx)
{
    HRESULT hr;

    WIAS_ASSERT(g_hInst, pDrvItem != NULL);
    WIAS_ASSERT(g_hInst, pTranCtx != NULL);
    WIAS_ASSERT(g_hInst, pTranCtx->tymed == TYMED_CALLBACK);

    //
    // driver is sending TOPDOWN data, must swap biHeight
    //
    // this routine assumes pTranCtx->pHeader points to a
    // BITMAPINFO header (TYMED_FILE doesn't use this path
    // and DIB is the only format supported now)
    //

    PBITMAPINFO pbmi = (PBITMAPINFO)pTranCtx->pTransferBuffer;

    if (pTranCtx->guidFormatID == WiaImgFmt_MEMORYBMP) {
        pbmi->bmiHeader.biHeight = -pbmi->bmiHeader.biHeight;
    }

    hr = pTranCtx->
            pIWiaMiniDrvCallBack->
                MiniDrvCallback(
                    IT_MSG_DATA,
                    IT_STATUS_TRANSFER_TO_CLIENT,
                    0,
                    0,
                    pTranCtx->lHeaderSize,
                    pTranCtx,
                    0);

    if (hr == S_OK) {

        //
        // advance offset for destination copy
        //

        pTranCtx->cbOffset += pTranCtx->lHeaderSize;

    }

    return hr;
}

/**************************************************************************\
* TestUsdDevice::drvDeleteItem
*
*   Try to delete a device item. Device items for the test scanner may
*   not be modified.
*
* Arguments:
*
*   pWiasContext    -   The context of the item to delete
*   lFlags          -   unused
*   plDevErrVal     -   unused
*
* Return Value:
*
*    Status
*
* History:
*
*    10/15/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall TestUsdDevice::drvDeleteItem(
    BYTE*       pWiasContext,
    LONG        lFlags,
    LONG*       plDevErrVal)
{
    *plDevErrVal = 0;
    return STG_E_ACCESSDENIED;
}

/**************************************************************************\
* TestUsdDevice::drvAcquireItemData
*
*   Scan data into buffer. This routine scans the entire contents into
*   the destination buffer in one call. Status will be sent back if
*   the callback routine is provided
*
* Arguments:
*
*   pWiasContext    - identifies ITEM context
*   lFlags          - unused
*   pTranCtx        - buffer and callback information
*   plDevErrVal     - error value
*
* Return Value:
*
*    Status
*
* History:
*
*    11/17/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall TestUsdDevice::drvAcquireItemData(
    BYTE                        *pWiasContext,
    LONG                        lFlags,
    PMINIDRV_TRANSFER_CONTEXT   pTranCtx,
    LONG                        *plDevErrVal)
{
    HRESULT                     hr;
// #define TEST_PER_USER_DATA 1
#ifdef TEST_PER_USER_DATA
#ifdef UNICODE
    BOOL                        bRet;
    TCHAR                       tszUserName[MAX_PATH];
    DWORD                       dwBufSize;
    HANDLE                      hToken;
    PROFILEINFO                 profileInfo;
    LONG                        lRet;
    HKEY                        hKeyCurUser;
#endif
#endif

    *plDevErrVal = 0;

    //
    // How to access per user setting in the USD
    //

#ifdef TEST_PER_USER_DATA
#ifdef UNICODE
#ifdef DEBUG

    dwBufSize = MAX_PATH;
    bRet = GetUserName(tszUserName, &dwBufSize);
#endif
#endif
#endif

#ifdef TEST_PER_USER_DATA
#ifdef UNICODE

    hToken = NULL;

    __try {

        hr = CoImpersonateClient();
        if (FAILED(hr)) {
            __leave;
        }

#ifdef NEED_USER_PROFILE

        bRet = OpenThreadToken(
                   GetCurrentThread(),
                   TOKEN_ALL_ACCESS,
                   TRUE,
                   &hToken);

        if (! bRet) {

            __leave;
        }

        //
        // Get the client's user name
        //

        dwBufSize = MAX_PATH;
        bRet = GetUserName(tszUserName, &dwBufSize);

        //
        // Revert to system account
        //

        hr = CoRevertToSelf();
        if (FAILED(hr)) {
            __leave;
        }
        hr = S_FALSE;

        //
        // Load the user profile
        //

        ZeroMemory(&profileInfo, sizeof(profileInfo));
        profileInfo.dwSize     = sizeof(profileInfo);
        profileInfo.dwFlags    = PI_NOUI;
        profileInfo.lpUserName = tszUserName;

        bRet = LoadUserProfile(hToken, &profileInfo);
        if (! bRet) {
            __leave;
        }

        //
        // Access user portion of the registry
        //
        // Use profileInfo.hProfile instead of HKEY_CURRENT_USER
        //
        //

        hKeyCurUser = (HKEY)profileInfo.hProfile;
#else

        lRet = RegOpenCurrentUser(KEY_ALL_ACCESS, &hKeyCurUser);
        if (lRet != ERROR_SUCCESS) {
            __leave;
        }

#endif

        HKEY  hKeyEnv;

        lRet = RegOpenKey(
                   hKeyCurUser,
                   TEXT("Environment"),
                   &hKeyEnv);
        if (lRet == ERROR_SUCCESS) {
            RegCloseKey(hKeyEnv);
        }
    }
    __finally {

        if (hr == S_OK) {

            CoRevertToSelf();
        }

#ifdef NEED_USER_PROFILE

        if (bRet) {

            UnloadUserProfile(hToken, profileInfo.hProfile);
        }

        if (hToken) {

            CloseHandle(hToken);
        }
#else

        if (hKeyCurUser) {

            RegCloseKey(hKeyCurUser);
        }
#endif
    }

#endif
#endif

    //
    // Get a pointer to the associated driver item.
    //

    IWiaDrvItem* pDrvItem;

    hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
    if (FAILED(hr)) {
        return hr;
    }

    //
    // Validate the data transfer context.
    //

    hr = ValidateDataTransferContext(pTranCtx);

    if (FAILED(hr)) {
        return hr;
    }

    //
    // get item specific driver data
    //

    MEMCAM_IMAGE_CONTEXT  *pMCamContext;

    pDrvItem->GetDeviceSpecContext((BYTE **)&pMCamContext);

    if (!pMCamContext) {
        WIAS_ERROR((g_hInst,"drvAcquireItemData, NULL item context"));
        return E_POINTER;
    }

    //
    // Use WIA services to fetch format specific info.
    //

    if (!IsEqualGUID (pTranCtx->guidFormatID, WiaAudFmt_WAV) )
    {
        hr = wiasGetImageInformation(pWiasContext,
                                 0,
                                 pTranCtx);
    }
    else
    {

        WIN32_FILE_ATTRIBUTE_DATA wfd;
        GetFileAttributesEx (pMCamContext->pszCameraImagePath,GetFileExInfoStandard, &wfd);
        pTranCtx->lItemSize = (LONG)wfd.nFileSizeLow;
    }


    if (hr != S_OK) {
        return hr;
    }

    //
    // determine if this is a callback or buffered transfer
    //

    if (pTranCtx->tymed == TYMED_CALLBACK) {

        //
        // For formats that have a data header, send it to the client
        //

        if (pTranCtx->lHeaderSize > 0) {

            hr = SendBitmapHeader(
                     pDrvItem,
                     pTranCtx);
        }

        if (hr == S_OK) {
            hr = CamLoadPictureCB(
                     pMCamContext,
                     pTranCtx,
                     plDevErrVal);
        }

    } else {

        //
        // inc past header
        //

        pTranCtx->cbOffset += pTranCtx->lHeaderSize;

        hr = CamLoadPicture(
                 pMCamContext,
                 pTranCtx,
                 plDevErrVal);

    }

    return hr;
}

/**************************************************************************\
* TestUsdDevice::drvInitItemProperties
*
*   Initialize the device item properties.
*
* Arguments:
*
*   pWiasContext    - Pointer to WIA item context.
*   lFLags          - unused
*   plDevErrVal     - pointer to hardware error value.
*
*
* Return Value:
*
*    Status
*
* History:
*
*    10/29/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall TestUsdDevice::drvInitItemProperties(
    BYTE        *pWiasContext,
    LONG        lFlags,
    LONG        *plDevErrVal)
{
    HRESULT                  hr;
    LONG                     lItemType;
    PMEMCAM_IMAGE_CONTEXT    pContext;

    //
    // This device doesn't touch hardware to initialize the
    // device item properties.
    //

    *plDevErrVal = 0;

    //
    // Parameter validation.
    //

    if (!pWiasContext) {
        WIAS_ERROR((g_hInst,"drvInitItemProperties, invalid input pointers"));
        return (E_INVALIDARG);
    }

    //
    // Get a pointer to the associated driver item.
    //

    IWiaDrvItem* pDrvItem;

    hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
    if (FAILED(hr)) {
        return hr;
    }

    //
    // Root item has the all the device properties
    //

    hr = pDrvItem->GetItemFlags(&lItemType);
    if (FAILED(hr)) {
        return (hr);
    }

    if (lItemType & WiaItemTypeRoot) {

        // Root item property init finishes here
        return (InitDeviceProperties(pWiasContext,
                                     plDevErrVal));
    }

    //
    // If this is a file, init the properties
    //

    if (lItemType & WiaItemTypeImage) {

        //
        // Add the item property names.
        //

        hr = wiasSetItemPropNames(pWiasContext,
                                  NUM_CAM_ITEM_PROPS,
                                  gItemPropIDs,
                                  gItemPropNames);

        if (FAILED(hr)) {
            WIAS_ERROR((g_hInst,"drvInitItemProperties, wiasSetItemPropNames() failed"));
            return (hr);
        }

        //
        // Use WIA services to set the defaul item properties.
        //


        hr = wiasWriteMultiple(pWiasContext,
                               NUM_CAM_ITEM_PROPS,
                               gPropSpecDefaults,
                               (PROPVARIANT*)gPropVarDefaults);

        if (FAILED(hr)) {
            return (hr);
        }

        hr = pDrvItem->GetDeviceSpecContext((BYTE **)&pContext);

        if (FAILED(hr)) {
            WIAS_ERROR((g_hInst,"drvInitItemProperties, GetDeviceSpecContext failed"));
            return (hr);
        }

        hr = InitImageInformation(pWiasContext,
                                  pContext,
                                  plDevErrVal);

        if (FAILED(hr)) {
            WIAS_ERROR((g_hInst,"drvInitItemProperties InitImageInformation() failed"));
            return (hr);
        }

    }
    else if (lItemType & WiaItemTypeAudio)
    {
        //
        // Add the item property names.
        //

        hr = wiasSetItemPropNames(pWiasContext,
                                  NUM_AUDIO_PROPS,
                                  gAudioPropIDs,
                                  gAudioPropNames);

        if (FAILED(hr)) {
            WIAS_ERROR((g_hInst,"drvInitItemProperties, wiasSetItemPropNames() failed"));
            return (hr);
        }

        //
        // Use WIA services to set the defaul item properties.
        //

        hr = wiasWriteMultiple(pWiasContext,
                               NUM_AUDIO_PROPS,
                               gAudioPropDefaults,
                               (PROPVARIANT*)gAudioDefaults);

        if (FAILED(hr)) {
            return (hr);
        }

        hr = pDrvItem->GetDeviceSpecContext((BYTE **)&pContext);

        if (FAILED(hr)) {
            WIAS_ERROR((g_hInst,"drvInitItemProperties, GetDeviceSpecContext failed"));
            return (hr);
        }
        hr = InitAudioInformation (pWiasContext,
                                   pContext,
                                   plDevErrVal);
    }


    return (S_OK);
}

/**************************************************************************\
* TestUsdDevice::drvValidateItemProperties
*
*   Validate the device item properties.
*
* Arguments:
*
*   pWiasContext    - wia item context
*   lFlags          - unused
*   nPropSpec       -
*   pPropSpec       -
*   plDevErrVal     - device error value
*
* Return Value:
*
*    Status
*
* History:
*
*    10/29/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall TestUsdDevice::drvValidateItemProperties(
    BYTE                *pWiasContext,
    LONG                lFlags,
    ULONG               nPropSpec,
    const PROPSPEC      *pPropSpec,
    LONG                *plDevErrVal)
{
    //
    // This device doesn't touch hardware to validate the device item properties.
    //

    *plDevErrVal = 0;

    //
    // Parameter validation.
    //

    if (!pWiasContext || !pPropSpec) {
        WIAS_ERROR((g_hInst,"drvValidateItemProperties, invalid input pointers"));
        return E_POINTER;
    }

    //
    // validate size
    //

    HRESULT hr = S_OK;

    IWiaDrvItem* pDrvItem;

    hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
    if (FAILED(hr)) {
        return hr;
    }

    LONG lItemType;

    hr = pDrvItem->GetItemFlags(&lItemType);

    if (hr == S_OK) {

        if (lItemType & WiaItemTypeImage) {

            //
            // calc item size
            //

            hr = SetItemSize(pWiasContext);

            //
            //  Change MinBufferSize property.  Need to get Tymed and
            //  ItemSize first, since MinBufferSize in dependant on these
            //  properties.
            //

            LONG        lTymed;
            LONG        lItemSize;
            LONG        lMinBufSize = 0;
            HRESULT     hr = S_OK;

            hr = wiasReadPropLong(pWiasContext, WIA_IPA_TYMED, &lTymed, NULL, TRUE);
            if (FAILED(hr)) {
                WIAS_ERROR((g_hInst,"drvValidateItemProperties, could not read TYMED property"));
                return hr;
            }

            hr = wiasReadPropLong(pWiasContext, WIA_IPA_ITEM_SIZE, &lItemSize, NULL, TRUE);
            if (SUCCEEDED(hr)) {

                //
                //  Update the MinBufferSize property.
                //

                switch (lTymed) {
                    case TYMED_CALLBACK:
                        lMinBufSize = 65535;
                        break;

                    default:
                        lMinBufSize = lItemSize;
                }
                if (lMinBufSize) {
                    hr = wiasWritePropLong(pWiasContext, WIA_IPA_MIN_BUFFER_SIZE, lMinBufSize);
                    if (FAILED(hr)) {
                        WIAS_ERROR((g_hInst, "drvValidateItemProperties, could not write value for WIA_IPA_MIN_BUFFER_SIZE"));
                    }
                }
            } else {
                WIAS_ERROR((g_hInst, "drvValidateItemProperties, could not read value for ItemSize"));
            }

        } else if (lItemType & WiaItemTypeRoot) {

            //
            // Find out whether the Root Path property is changed
            //

            for (ULONG i = 0; i < nPropSpec; i++) {

                if (((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
                          (pPropSpec[i].propid == WIA_DPP_TCAM_ROOT_PATH)) ||
                    ((pPropSpec[i].ulKind == PRSPEC_LPWSTR) &&
                          (wcscmp(pPropSpec[i].lpwstr, WIA_DPP_TCAM_ROOT_PATH_STR) == 0))) {

                    BSTR   bstrRootPath;

                    //
                    // Retrieve the new value for Root Path property
                    //

                    hr = wiasReadPropStr(
                             pWiasContext,
                             WIA_DPP_TCAM_ROOT_PATH,
                             &bstrRootPath,
                             NULL,
                             TRUE);
                    if (FAILED(hr)) {
                        return (hr);
                    }

#ifdef UNICODE
                    wcscpy(gpszPath, bstrRootPath);
#else
                    wcstombs(gpszPath, bstrRootPath, MAX_PATH);
#endif
                    //
                    // Release the Root Path bstr
                    //

                    SysFreeString(bstrRootPath);

                    //
                    // Rebuild the item tree and send event notification
                    //

                    hr = DeleteDeviceItemTree(plDevErrVal);
                    if (FAILED(hr)) {
                        break;
                    }

                    hr = BuildDeviceItemTree(plDevErrVal);
                    if (FAILED(hr)) {
                        break;
                    }

                    m_guidLastEvent = WIA_EVENT_DEVICE_CONNECTED;
                    SetEvent(m_hSignalEvent);

                    break;
                }
            }
        }
    }

    return (hr);
}

/**************************************************************************\
* TestUsdDevice::drvWriteItemProperties
*
*   Write the device item properties to the hardware.
*
* Arguments:
*
*   pWiasContext    - the corresponding wia item context
*   pmdtc           - pointer to mini driver context
*   pFlags          - unused
*   plDevErrVal     - the device error value
*
* Return Value:
*
*    Status
*
* History:
*
*    10/29/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall TestUsdDevice::drvWriteItemProperties(
    BYTE                       *pWiasContext,
    LONG                        lFlags,
    PMINIDRV_TRANSFER_CONTEXT   pmdtc,
    LONG                       *plDevErrVal)
{
    //
    // Assume no device hardware errors.
    //

    *plDevErrVal = 0;

    //
    // Parameter validation.
    //

    if ((! pWiasContext) || (! pmdtc)) {
        WIAS_ERROR((g_hInst,"drvWriteItemProperties, invalid input pointers"));
        return E_POINTER;
    }

    //
    // Get a pointer to the associated driver item.
    //

    IWiaDrvItem* pDrvItem;

    HRESULT hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
    if (FAILED(hr)) {
        return hr;
    }

    PMEMCAM_IMAGE_CONTEXT pItemContext;

    hr = pDrvItem->GetDeviceSpecContext((BYTE**)&pItemContext);

    if (FAILED(hr)) {
        WIAS_ERROR((g_hInst,"drvWriteItemProperties, NULL item context"));
        return E_POINTER;
    }

    //
    // Write the device item properties to the hardware here.
    //

    return hr;
}

/**************************************************************************\
* TestUsdDevice::drvReadItemProperties
*
*   Read the device item properties from the hardware.
*
* Arguments:
*
*   pWiasContext    - wia item context
*   lFlags          - unused
*   nPropSpec       -
*   pPropSpec       -
*   plDevErrVal     - device error value
*
* Return Value:
*
*    Status
*
* History:
*
*    10/29/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall TestUsdDevice::drvReadItemProperties(
    BYTE            *pWiasContext,
    LONG                lFlags,
    ULONG               nPropSpec,
    const PROPSPEC      *pPropSpec,
    LONG                *plDevErrVal)
{
    // For most scanner devices, item properties are stored in the driver
    // and written out at acquire image time. Some devices support properties
    // which should be updated on every property read. This can be done here.


    *plDevErrVal = 0;

    return S_OK;
}


/**************************************************************************\
* TestUsdDevice::drvLockWiaDevice
*
*   Lock access to the device.
*
* Arguments:
*
*   pWiasContext    - unused,
*   lFlags          - unused
*   plDevErrVal     - device error value
*
*
* Return Value:
*
*    Status
*
* History:
*
*    9/11/1998 Original Version
*
\**************************************************************************/

HRESULT TestUsdDevice::drvLockWiaDevice(
    BYTE        *pWiasContext,
    LONG        lFlags,
    LONG        *plDevErrVal)
{
    *plDevErrVal = 0;
    if (m_pStiDevice)
    {
        return m_pStiDevice->LockDevice(100);
    }
    return S_OK;

}

/**************************************************************************\
* TestUsdDevice::drvUnLockWiaDevice
*
*   Unlock access to the device.
*
* Arguments:
*
*   pWiasContext    - unused
*   lFlags          - unused
*   plDevErrVal     - device error value
*
*
* Return Value:
*
*    Status
*
* History:
*
*    9/11/1998 Original Version
*
\**************************************************************************/

HRESULT TestUsdDevice::drvUnLockWiaDevice(
    BYTE        *pWiasContext,
    LONG        lFlags,
    LONG        *plDevErrVal)
{
    plDevErrVal = 0;
    return m_pStiDevice->UnLockDevice();
}

/**************************************************************************\
* TestUsdDevice::drvAnalyzeItem
*
*   The test camera does not support imag analysis.
*
* Arguments:
*
*   pWiasContext    - Pointer to the device item context to be analyzed.
*   lFlags          - Operation flags.
*   plDevErrVal     - device error value
*
* Return Value:
*
*    Status
*
* History:
*
*    10/15/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall TestUsdDevice::drvAnalyzeItem(
    BYTE                *pWiasContext,
    LONG                lFlags,
    LONG                *plDevErrVal)
{
    *plDevErrVal = 0;
    return E_NOTIMPL;
}

/**************************************************************************\
* TestUsdDevice::drvFreeDrvItemContext
*
*   The test scanner does not support imag analysis.
*
* Arguments:
*
*   lFlags          - unused
*   pSpecContext    - Pointer to item specific context.
*   plDevErrVal     - device error value
*
* Return Value:
*
*    Status
*
* History:
*
*    10/15/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall TestUsdDevice::drvFreeDrvItemContext(
    LONG                lFlags,
    BYTE                *pSpecContext,
    LONG                *plDevErrVal)
{
    PMEMCAM_IMAGE_CONTEXT pContext = (PMEMCAM_IMAGE_CONTEXT)pSpecContext;

    if (pContext != NULL) {
        if (pContext->pszCameraImagePath != NULL) {

            free(pContext->pszCameraImagePath);
            pContext->pszCameraImagePath = NULL;
        }
    }

    return S_OK;
}