//------------------------------------------------------------------------------
//  (C) COPYRIGHT MICROSOFT CORP., 1998
//
//  wiadev.cpp
//
//  Implementation of device methods for the IrTran-P USD mini driver.
//
//  Author
//     EdwardR     05-Aug-99    Initial code.
//     Modeled after code written by ReedB.
//
//------------------------------------------------------------------------------

#define __FORMATS_AND_MEDIA_TYPES__

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

#include "ircamera.h"
#include "defprop.h"
#include "resource.h"
#include <irthread.h>
#include <malloc.h>

extern HINSTANCE        g_hInst;     // Global hInstance
extern WIA_FORMAT_INFO *g_wfiTable;

//----------------------------------------------------------------------------
// IrUsdDevice::InitializWia()
//
//
//
// Arguments:
//
//
//
// Return Value:
//
//    HRESULT  S_OK
//
//----------------------------------------------------------------------------
HRESULT _stdcall IrUsdDevice::drvInitializeWia(
                                 BYTE         *pWiasContext,
                                 LONG          lFlags,
                                 BSTR          bstrDeviceID,
                                 BSTR          bstrRootFullItemName,
                                 IUnknown     *pStiDevice,
                                 IUnknown     *pIUnknownOuter,
                                 IWiaDrvItem **ppIDrvItemRoot,
                                 IUnknown    **ppIUnknownInner,
                                 LONG         *plDevErrVal )
    {
    HRESULT              hr;
    LONG                 lDevErrVal;

    WIAS_TRACE((g_hInst,"IrUsdDevice::drvInitializeWia(): Device ID: %ws", bstrDeviceID));

    *ppIDrvItemRoot = NULL;
    *ppIUnknownInner = NULL;
    *plDevErrVal = 0;

    //
    // Initialize names and STI pointer?
    //
    if (m_pStiDevice == NULL)
        {
        //
        // save STI device inteface for locking:
        //
        m_pStiDevice = (IStiDevice*)pStiDevice;

        //
        // Cache the device ID:
        //
        m_bstrDeviceID = SysAllocString(bstrDeviceID);
        if (! m_bstrDeviceID)
            {
            return E_OUTOFMEMORY;
            }

        m_bstrRootFullItemName = SysAllocString(bstrRootFullItemName);

        if (! m_bstrRootFullItemName)
            {
            return E_OUTOFMEMORY;
            }
        }

    //
    // Build the device item tree
    //
    hr = drvDeviceCommand( NULL, 0, &WIA_CMD_SYNCHRONIZE, NULL, &lDevErrVal );

    if (SUCCEEDED(hr))
        {
        *ppIDrvItemRoot = m_pIDrvItemRoot;
        }

    return hr;
    }


//----------------------------------------------------------------------------
// IrUsdDevice::drvUnInitializeWia
//
//   Gets called when a client connection is going away.
//
// Arguments:
//
//   pWiasContext    - Pointer to the WIA Root item context of the client's
//                     item tree.
//
// Return Value:
//    Status
//
// History:
//
//   30/12/1999 Original Version
//----------------------------------------------------------------------------
HRESULT _stdcall IrUsdDevice::drvUnInitializeWia(
    BYTE                *pWiasContext)
{
    return S_OK;
}


//----------------------------------------------------------------------------
//
//   Mini Driver Device Services
//
//----------------------------------------------------------------------------



/**************************************************************************\
* drvGetDeviceErrorStr
*
*     Map a device error value to a string.
*
* Arguments:
*
*
*
* Return Value:
*
*    Status
*
* History:
*
*    10/2/1998 Original Version
*
\**************************************************************************/

HRESULT _stdcall IrUsdDevice::drvGetDeviceErrorStr(
    LONG        lFlags,
    LONG        lDevErrVal,
    LPOLESTR    *ppszDevErrStr,
    LONG        *plDevErr)
{
    *plDevErr = 0;
    if (!ppszDevErrStr) {
        WIAS_ERROR((g_hInst,"drvGetDeviceErrorStr, NULL ppszDevErrStr"));
        return E_POINTER;
    }

    // Map device errors to a string to be placed in the event log.
    switch (lDevErrVal) {

        case 0:
            *ppszDevErrStr = L"No Error";
            break;

        default:
            *ppszDevErrStr = L"Device Error, Unknown Error";
            return E_FAIL;
    }
    return S_OK;
}

//--------------------------------------------------------------------------
// IrUsdDevice::DeleteDeviceItemTree()
//
//   Recursive device item tree delete routine. Deletes the whole tree.
//
// Arguments:
//
//
//
// Return Value:
//
//    HRESULT
//
//--------------------------------------------------------------------------
HRESULT IrUsdDevice::DeleteDeviceItemTree( OUT LONG *plDevErrVal )
    {
    HRESULT hr;

    //
    // does tree exist
    //
    if (m_pIDrvItemRoot == NULL)
        {
        return S_OK;
        }

    //
    // Unlink and release the driver item tree.
    //

    hr = m_pIDrvItemRoot->UnlinkItemTree(WiaItemTypeDisconnected);

    m_pIDrvItemRoot = NULL;

    return hr;
    }

//--------------------------------------------------------------------------
// IrUsdDevice::BuildDeviceItemTree()
//
//   The device uses the IWiaDrvServices methods to build up a tree of
//   device items. The test scanner supports only a single scanning item so
//   build a device item tree with one entry.
//
// Arguments:
//
//
//
// Return Value:
//
//    HRESULT
//
//--------------------------------------------------------------------------
HRESULT IrUsdDevice::BuildDeviceItemTree( OUT LONG  *plDevErrVal )
    {
    HRESULT hr = S_OK;

    //
    // Note: This device doesn't touch hardware to build the tree.
    //

    if (plDevErrVal)
        {
        *plDevErrVal = 0;
        }

    //
    // Rebuild the new device item tree (of JPEG images):
    //
    CAMERA_STATUS camStatus;

    if (!m_pIDrvItemRoot)
        {
        hr = CamBuildImageTree( &camStatus, &m_pIDrvItemRoot );
        }

    return hr;
    }

//--------------------------------------------------------------------------
// IrUsdDevice::InitDeviceProperties()
//
//
//
// Arguments:
//
//
//
// Return Value:
//
//    HRESULT  -- S_OK
//                E_POINTER
//                E_OUTOFMEMORY
//
//--------------------------------------------------------------------------
HRESULT IrUsdDevice::InitDeviceProperties(
                         BYTE  *pWiasContext,
                         LONG  *plDevErrVal )
    {
    int         i;
    HRESULT     hr;
    BSTR        bstrFirmwreVer;
    SYSTEMTIME  camTime;
    PROPVARIANT propVar;

    //
    // This device doesn't actually touch any hardware to initialize
    // the device properties.
    //
    if (plDevErrVal)
        {
        *plDevErrVal = 0;
        }

    //
    // Parameter validation.
    //
    if (!pWiasContext)
        {
        WIAS_ERROR((g_hInst,"IrUsdDevice::InitDeviceProperties(): NULL WIAS context"));
        return E_POINTER;
        }

    //
    // Write standard property names
    //
    hr = wiasSetItemPropNames(pWiasContext,
                              sizeof(gDevicePropIDs)/sizeof(PROPID),
                              gDevicePropIDs,
                              gDevicePropNames);
    if (FAILED(hr))
        {
        WIAS_TRACE((g_hInst,"IrUsdDevice::InitDeviceProperties(): WritePropertyNames() failed: 0x%x",hr));
        return hr;
        }

    //
    // Write the properties supported by all WIA devices
    //
    bstrFirmwreVer = SysAllocString(L"02161999");
    if (bstrFirmwreVer)
        {
        wiasWritePropStr( pWiasContext,
                          WIA_DPA_FIRMWARE_VERSION,
                          bstrFirmwreVer );
        SysFreeString(bstrFirmwreVer);
        }

    wiasWritePropLong( pWiasContext, WIA_DPA_CONNECT_STATUS, 1);

    wiasWritePropLong( pWiasContext, WIA_DPC_PICTURES_TAKEN, 0);

    GetSystemTime(&camTime);
    wiasWritePropBin( pWiasContext,
                      WIA_DPA_DEVICE_TIME,
                      sizeof(SYSTEMTIME),
                      (PBYTE)&camTime );

    //
    // Write the camera properties, just default values, it may vary with items
    //

    wiasWritePropLong(
        pWiasContext, WIA_DPC_PICTURES_REMAINING, 0);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_THUMB_WIDTH, 80);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_THUMB_HEIGHT, 60);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_PICT_WIDTH, 1024);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_PICT_HEIGHT, 768);

    // Give WIA_DPC_EXPOSURE_MODE to WIA_DPC_TIMER_VALUE some default.

    wiasWritePropLong(
        pWiasContext, WIA_DPC_EXPOSURE_MODE, 0);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_FLASH_MODE, 1);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_FOCUS_MODE, 0);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_ZOOM_POSITION, 0);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_BATTERY_STATUS, 1);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_TIMER_MODE, 0);
    wiasWritePropLong(
        pWiasContext, WIA_DPC_TIMER_VALUE, 0);

    //
    // Write the WIA_DPP_TCAM_ROOT_PATH property
    //

    BSTR    bstrRootPath;
    CHAR   *pszPath = ::GetImageDirectory();   // Don't try to free...
    WCHAR   wszPath[MAX_PATH];

    if (!pszPath)
        {
        return E_OUTOFMEMORY;
        }

    mbstowcs( wszPath, pszPath, strlen(pszPath) );

    bstrRootPath = SysAllocString(wszPath);

    if (! bstrRootPath)
        {
        return E_OUTOFMEMORY;
        }

    wiasWritePropStr(pWiasContext, WIA_DPP_TCAM_ROOT_PATH, bstrRootPath);

    //
    // Use WIA services to set the property access and
    // valid value information from gDevPropInfoDefaults.
    //

    hr =  wiasSetItemPropAttribs(pWiasContext,
                                 NUM_CAM_DEV_PROPS,
                                 gDevicePropSpecDefaults,
                                 gDevPropInfoDefaults);
    return S_OK;
}

//--------------------------------------------------------------------------
// IrUsdDevice::drvDeviceCommand()
//
//
//
// Arguments:
//
//
//
// Return Value:
//
//    HRESULT   S_OK
//              E_NOTIMPL
//
//--------------------------------------------------------------------------
HRESULT _stdcall IrUsdDevice::drvDeviceCommand(
                                 BYTE         *pWiasContext,
                                 LONG          lFlags,
                                 const GUID   *plCommand,
                                 IWiaDrvItem **ppWiaDrvItem,
                                 LONG         *plErr)
    {
    HRESULT hr;

    //
    // init return value
    //
    if (ppWiaDrvItem != NULL)
        {
        *ppWiaDrvItem = NULL;
        }

    //
    // dispatch command
    //
    if (*plCommand == WIA_CMD_SYNCHRONIZE)
        {
        WIAS_TRACE((g_hInst,"IrUsdDevice::drvDeviceCommand(): WIA_CMD_SYNCHRONIZE"));

        hr = drvLockWiaDevice(pWiasContext, lFlags, plErr);
        if (FAILED(hr))
            {
            return (hr);
            }

        //
        // SYNCHRONIZE - make sure tree is up to date with device
        //
        // The dirver's responsibility is to make sure the tree is accurate.
        //

        hr = BuildDeviceItemTree(plErr);

        drvUnLockWiaDevice( pWiasContext, lFlags, plErr );
        }
    else
        {
        WIAS_TRACE((g_hInst,"drvDeviceCommand: Unsupported command"));

        hr = E_NOTIMPL;
        }

    return hr;
}

//--------------------------------------------------------------------------
// LoadStringResource()
//
//
//--------------------------------------------------------------------------
int LoadStringResource( IN  HINSTANCE hInst,
                        IN  UINT      uID,
                        OUT WCHAR    *pwszBuff,
                        IN  int       iBuffMax )
    {
    #ifdef UNICODE
    return LoadString(hInst,uID,pwszBuff,iBuffMax);
    #else
    CHAR *pszBuff = (CHAR*)_alloca(sizeof(CHAR)*iBuffMax);

    int  iCount = LoadString(hInst,uID,pszBuff,iBuffMax);

    if (iCount > 0)
        {
        MultiByteToWideChar( CP_ACP, 0, pszBuff, -1, pwszBuff, iBuffMax );
        }

    return iCount;

    #endif
    }

//--------------------------------------------------------------------------
// IrUsdDevice::InitializeCapabilities()
//
// This helper function is called by IrUsdDevice::drvGetCapabilities() to
// make sure that the string resources are setup in the gCapabilities[]
// array before it is passed back to WIA.
//
//--------------------------------------------------------------------------
void IrUsdDevice::InitializeCapabilities()
    {
    int    i;
    UINT   uIDS;
    WCHAR *pwszName;
    WCHAR *pwszDescription;
#   define MAX_IDS_WSTR      64

    //
    // If we have entries already, then this function was already called
    // and  we can just return:
    //
    if (gCapabilities[0].wszName)
        {
        return;
        }

    for (i=0; i<NUM_CAP_ENTRIES; i++)
        {
        //
        // Get the string table string ID for this entry:
        //
        uIDS = gCapabilityIDS[i];

        //
        // Get the name string for this entry fron the resource file:
        //
        pwszName = new WCHAR [MAX_IDS_WSTR];
        if (  (pwszName)
           && (LoadStringResource(g_hInst,uIDS,pwszName,MAX_IDS_WSTR)))
            {
            gCapabilities[i].wszName = pwszName;
            }
        else
            {
            gCapabilities[i].wszName = gDefaultStrings[i];
            }

        //
        // Get the Discription string for this entry from the resource file:
        //
        pwszDescription = new WCHAR [MAX_IDS_WSTR];
        if (  (pwszDescription)
           && (LoadStringResource(g_hInst,uIDS,pwszDescription,MAX_IDS_WSTR)))
            {
            gCapabilities[i].wszDescription = pwszDescription;
            }
        else
            {
            gCapabilities[i].wszDescription = gDefaultStrings[i];
            }
        }
    }

//--------------------------------------------------------------------------
// IrUsdDevice::drvGetCapabilities()
//
//
//
// Arguments:
//
//
//
// Return Value:
//
//    HRESULT   -- S_OK
//              -- E_INVALIDARG
//
//--------------------------------------------------------------------------
HRESULT _stdcall IrUsdDevice::drvGetCapabilities(
                                 BYTE             *pWiasContext,
                                 LONG              ulFlags,
                                 LONG             *pCelt,
                                 WIA_DEV_CAP_DRV **ppCapabilities,
                                 LONG             *plDevErrVal )
    {
    HRESULT  hr = S_OK;

    *plDevErrVal = 0;

    //
    // Make sure the device capabilities array is setup
    //
    InitializeCapabilities();

    //
    // Return Commmand and/or Events depending on flags:
    //
    switch (ulFlags)
        {
        case WIA_DEVICE_COMMANDS:
            //
            //  Only asked for commands:
            //
            *pCelt = NUM_CAP_ENTRIES - NUM_EVENTS;
            *ppCapabilities = &gCapabilities[NUM_EVENTS];
            break;

        case WIA_DEVICE_EVENTS:
            //
            //  Return only events:
            //
            *pCelt = NUM_EVENTS;
            *ppCapabilities = gCapabilities;
            break;

        case (WIA_DEVICE_COMMANDS | WIA_DEVICE_EVENTS):
            //
            //  Return both events and commands:
            //
            *pCelt = NUM_CAP_ENTRIES;
            *ppCapabilities = gCapabilities;
            break;

        default:
            //
            // Flags is invalid
            //
            WIAS_ERROR((g_hInst, "drvGetCapabilities, flags was invalid"));
            hr = E_INVALIDARG;
        }

    return hr;
    }

//--------------------------------------------------------------------------
// IrUsdDevice::drvGetFormatEtc()
//
//     Return an array of the supported formats and mediatypes.
//
// Arguments:
//
//     pWiasConext -
//     ulFlags     -
//     plNumFE     - Number of returned formats.
//     ppFE        - Pointer to array of FORMATETC for supported formats
//                   and mediatypes.
//     plDevErrVal - Return error number.
//
// Return Value:
//
//     HRESULT  - S_OK
//
//--------------------------------------------------------------------------
HRESULT _stdcall IrUsdDevice::drvGetWiaFormatInfo(
                                 IN  BYTE       *pWiasContext,
                                 IN  LONG        ulFlags,
                                 OUT LONG       *plNumWFI,
                                 OUT WIA_FORMAT_INFO **ppWFI,
                                 OUT LONG       *plDevErrVal)
    {
    #define NUM_WIA_FORMAT_INFO  2

    WIAS_TRACE((g_hInst, "IrUsdDevice::drvGetWiaFormatInfo()"));

    //
    // If necessary, setup the g_wfiTable.
    //
    if (!g_wfiTable)
        {
        g_wfiTable = (WIA_FORMAT_INFO*) CoTaskMemAlloc(sizeof(WIA_FORMAT_INFO) * NUM_WIA_FORMAT_INFO);
        if (!g_wfiTable)
            {
            WIAS_ERROR((g_hInst, "drvGetWiaFormatInfo(): out of memory"));
            return E_OUTOFMEMORY;
            }

        //
        // Set the format/tymed pairs:
        //
        g_wfiTable[0].guidFormatID = WiaImgFmt_JPEG;
        g_wfiTable[0].lTymed = TYMED_CALLBACK;
        g_wfiTable[1].guidFormatID = WiaImgFmt_JPEG;
        g_wfiTable[1].lTymed = TYMED_FILE;
        }

    *plNumWFI = NUM_WIA_FORMAT_INFO;
    *ppWFI = g_wfiTable;
    *plDevErrVal = 0;

    return S_OK;
    }

//--------------------------------------------------------------------------
// IrUsdDevice::drvNotifyPnpEvent()
//
// Notify PnP event received by device manager.
//
//--------------------------------------------------------------------------
HRESULT _stdcall IrUsdDevice::drvNotifyPnpEvent(
                                 IN const GUID *pEventGUID,
                                 IN BSTR        bstrDeviceID,
                                 IN ULONG       ulReserved )
    {
    return S_OK;
    }