/*****************************************************************************
 *
 *  PidOp.c
 *
 *  Copyright (c) 1999 Microsoft Corporation.  All Rights Reserved.
 *
 *  Abstract:
 *
 *      PID device Operation .
 *
 *****************************************************************************/
#include "pidpr.h"

#define sqfl            ( sqflOp )

#pragma BEGIN_CONST_DATA

static PIDUSAGE    c_rgUsgGain[] =
{
    MAKE_PIDUSAGE(DEVICE_GAIN,  0x0 )
};

static PIDREPORT DeviceGain =
{
    HidP_Output,
    HID_USAGE_PAGE_PID,
    HID_USAGE_PID_DEVICE_GAIN_REPORT,
    cbX(DWORD),
    cA(c_rgUsgGain),
    c_rgUsgGain
};




PIDUSAGE    c_rgUsgOperationReport[] =
{
    MAKE_PIDUSAGE(LOOP_COUNT,               0x0),
};

static PIDREPORT OperationReport =
{
    HidP_Output,
    HID_USAGE_PAGE_PID,
    HID_USAGE_PID_EFFECT_OPERATION_REPORT,
    cbX(DWORD),
    cA(c_rgUsgOperationReport),
    c_rgUsgOperationReport
};


static PIDREPORT DeviceControlReport =
{
    HidP_Output,
    HID_USAGE_PAGE_PID,
    HID_USAGE_PID_DEVICE_CONTROL,
    0x0,
    0x0,
    NULL
};


#pragma END_CONST_DATA

STDMETHODIMP
    PID_EffectOperation
    (
    IDirectInputEffectDriver *ped, 
    DWORD dwId, 
    DWORD dwEffect,
    DWORD dwMode, 
    DWORD dwCount,
	BOOL  bBlocking,
	UINT  blockNr,
	UINT  totalBlocks
    )
{
    CPidDrv *this = (CPidDrv *)ped;
    HRESULT hres = S_OK;

    EnterProcI( PID_EffectOperation, (_"xxxxx", ped, dwId, dwEffect, dwMode, dwCount ));

    hres = PID_ValidateEffectIndex(ped, dwEffect);
    // Allocate Memory for the report 
    if( SUCCEEDED(hres) )
    {
        USHORT  cbReport;
        PUCHAR  pReport;

        USHORT LinkCollection;
        AssertF(OperationReport.HidP_Type == HidP_Output);

        cbReport = this->cbReport[OperationReport.HidP_Type];
        pReport = this->pReport[OperationReport.HidP_Type];

        PID_GetLinkCollectionIndex(ped, OperationReport.UsagePage, OperationReport.Collection, 0x0, &LinkCollection );
        // Set the Effect Structure 
        if( SUCCEEDED(hres) )
        {
            ZeroBuf(pReport, cbReport);

            // Set Effect Operation
            if( SUCCEEDED(hres) )
            {
                USAGE   Usage;
                USAGE   UsagePage;
                NTSTATUS  ntStat;
                UINT    nUsages = 0x1;
                USAGE   LinkCollection0;
                PEFFECTSTATE    pEffectState =  PeffectStateFromBlockIndex(this,dwEffect); 

                UsagePage = OperationReport.UsagePage;

                PID_GetLinkCollectionIndex(ped, OperationReport.UsagePage, HID_USAGE_PID_EFFECT_OPERATION, 0x0, &LinkCollection0);

                if( dwMode & DIES_SOLO )
                {
                    Usage = HID_USAGE_PID_OP_EFFECT_START_SOLO;
                    pEffectState->lEfState |= PID_EFFECT_STARTED_SOLO;
                } else if( dwMode & PID_DIES_START )
                {
                    Usage = HID_USAGE_PID_OP_EFFECT_START;
                    pEffectState->lEfState |= PID_EFFECT_STARTED;
                } else if(dwMode & PID_DIES_STOP )
                {
                    Usage = HID_USAGE_PID_OP_EFFECT_STOP;
                    pEffectState->lEfState &= ~(PID_EFFECT_STARTED | PID_EFFECT_STARTED_SOLO);
                } else
                {
                    SquirtSqflPtszV(sqfl | sqflError,
                                    TEXT("%s: FAIL Could not understand dwMode=0x%x"),
                                    s_tszProc, dwMode ); 

                    hres = E_NOTIMPL;
                }

                ntStat = HidP_SetUsages 
                         (
                         OperationReport.HidP_Type,
                         UsagePage,
                         LinkCollection0,
                         &Usage,
                         &nUsages,
                         this->ppd,
                         pReport,
                         cbReport);

                if( FAILED(hres) )
                {
                    SquirtSqflPtszV(sqfl | sqflBenign,
                                    TEXT("%s: FAIL HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
                                    s_tszProc, ntStat, 
                                    LinkCollection0, UsagePage, Usage,
                                    PIDUSAGETXT(UsagePage,Usage) );
                } else
                {
                    SquirtSqflPtszV(sqfl | sqflVerbose,
                                    TEXT("%s: HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
                                    s_tszProc, ntStat, 
                                    LinkCollection0, UsagePage, Usage,
                                    PIDUSAGETXT(UsagePage,Usage) );
                }
            }

            // Set the Loop Count
            if( SUCCEEDED(hres) )
            {
                PID_PackValue
                    (
                    ped,
                    &OperationReport,
                    LinkCollection,
                    &dwCount,
                    cbX(dwCount),
                    pReport,
                    cbReport
                    );

                // Set the Block Index
                PID_PackValue
                    (
                    ped,
                    &g_BlockIndex,
                    LinkCollection,
                    &dwEffect,
                    cbX(dwEffect),
                    pReport,
                    cbReport
                    );
            }

            if( SUCCEEDED(hres) )
            {
				hres = PID_SendReport(ped, pReport, cbReport, OperationReport.HidP_Type, bBlocking, blockNr, totalBlocks); 
            }
        }
    }
    ExitOleProc();
    return hres;
}


/*****************************************************************************
 *
 *      PID_SetGain
 *
 *          Set the overall device gain.
 *
 *  dwId
 *
 *          The joystick ID number being used.
 *
 *  dwGain
 *
 *          The new gain value.
 *
 *          If the value is out of range for the device, the device
 *          should use the nearest supported value and return
 *          DI_TRUNCATED.
 *
 *  Returns:
 *
 *
 *          S_OK if the operation completed successfully.
 *
 *          DI_TRUNCATED if the value was out of range and was
 *          changed to the nearest supported value.
 *
 *          Any DIERR_* error code may be returned.
 *
 *          Private driver-specific error codes in the range
 *          DIERR_DRIVERFIRST through DIERR_DRIVERLAST
 *          may be returned.
 *
 *****************************************************************************/
STDMETHODIMP
    PID_SetGain
    (
    IDirectInputEffectDriver *ped, 
    DWORD dwId, 
    DWORD dwGain
    )
{
    CPidDrv *this = (CPidDrv *)ped;
    HRESULT hres = S_OK;

    EnterProc( PID_SetGain, (_"xxx", ped, dwId, dwGain));
    DllEnterCrit();

    // Allocate Memory for the report 
    if( SUCCEEDED(hres) )
    {
        USHORT  cbReport;
        PUCHAR  pReport;

        USHORT LinkCollection;

        AssertF(DeviceGain.HidP_Type == HidP_Output);
        cbReport = this->cbReport[DeviceGain.HidP_Type];
        pReport = this->pReport[DeviceGain.HidP_Type];

        PID_GetLinkCollectionIndex(ped, DeviceGain.UsagePage, DeviceGain.Collection, 0x0, &LinkCollection );
        // Set the Effect Structure 
        if( SUCCEEDED(hres) )
        {
            ZeroBuf(pReport, cbReport);

            // Set the Loop Count
            if( SUCCEEDED(hres) )
            {
                hres = PID_PackValue
                       (
                       ped,
                       &DeviceGain,
                       LinkCollection,
                       &dwGain,
                       cbX(dwGain),
                       pReport,
                       cbReport
                       );
            }

            if( SUCCEEDED(hres) )
            {
                hres = PID_SendReport(ped, pReport, cbReport, DeviceGain.HidP_Type, FALSE, 0, 1); 
            }
        }
    }
    DllLeaveCrit();

    ExitOleProc();
    return hres;
}


/*****************************************************************************
 *
 *      PID_SendForceFeedbackCommand
 *
 *          Send a command to the device.
 *
 *  dwId
 *
 *          The external joystick number being addressed.
 *
 *  dwCommand
 *
 *          A DISFFC_* value specifying the command to send.
 *
 *  Returns:
 *
 *          S_OK on success.
 *
 *          Any DIERR_* error code may be returned.
 *
 *          Private driver-specific error codes in the range
 *          DIERR_DRIVERFIRST through DIERR_DRIVERLAST
 *          may be returned.
 *
 *****************************************************************************/
STDMETHODIMP
    PID_SendForceFeedbackCommand
    (
    IDirectInputEffectDriver *ped,
    DWORD dwId, 
    DWORD dwCommand
    )
{
    CPidDrv *this = (CPidDrv *)ped;
    HRESULT hres = S_OK;
    USAGE Usages[6];
    PUSAGE pUsages = &Usages[0];
    UINT nUsages;

    EnterProcI( PID_SendForceFeedbackCommand, (_"xxx", ped, dwId, dwCommand));

    DllEnterCrit();

    if( dwCommand & DISFFC_RESET )
    {
        DWORD indx;
        *pUsages++ = HID_USAGE_PID_DC_DEVICE_RESET;
        for(indx = 1 ; 
           (indx <= this->cMaxEffects) && (((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->cEfDownloaded != 0x0 );
           indx++ )
        {
            PID_DestroyEffect(ped, dwId, indx);
        }
        this->dwState = DIGFFS_STOPPED;
    }
    if( dwCommand & DISFFC_STOPALL )
    {
        *pUsages++ = HID_USAGE_PID_DC_STOP_ALL_EFFECTS;
        this->dwState = DIGFFS_STOPPED;
    }
    if( dwCommand & DISFFC_PAUSE )
    {
        *pUsages++ = HID_USAGE_PID_DC_DEVICE_PAUSE;
    }
    if( dwCommand & DISFFC_CONTINUE )
    {
        *pUsages++ = HID_USAGE_PID_DC_DEVICE_CONTINUE;
    }
    if( dwCommand & DISFFC_SETACTUATORSON)
    {
        *pUsages++ = HID_USAGE_PID_DC_ENABLE_ACTUATORS;
    }
    if(dwCommand & DISFFC_SETACTUATORSOFF)
    {
        *pUsages++ = HID_USAGE_PID_DC_DISABLE_ACTUATORS;
    }

    nUsages = (UINT)(pUsages - &Usages[0]);
    if(nUsages == 0x0 )
    {
        hres = E_NOTIMPL;
        SquirtSqflPtszV(sqfl | sqflError,
                        TEXT("%s: FAIL Do not understand dwCommand(0x%x) "),
                        s_tszProc, dwCommand);
    }

    if( SUCCEEDED(hres) )
    {
        USHORT  cbReport;
        PUCHAR  pReport;
        USHORT  LinkCollection;

        AssertF(DeviceControlReport.HidP_Type == HidP_Output);

        cbReport = this->cbReport[DeviceControlReport.HidP_Type];
        pReport =  this->pReport[DeviceControlReport.HidP_Type];

        PID_GetLinkCollectionIndex(ped, DeviceControlReport.UsagePage, DeviceControlReport.Collection, 0x0, &LinkCollection );
        // Set the Effect Structure 
        if( SUCCEEDED(hres) )
        {
            USHORT  UsagePage;
            NTSTATUS    ntStat;
            ZeroBuf(pReport, cbReport);
            UsagePage = OperationReport.UsagePage;

            ntStat = HidP_SetUsages 
                     (
                     OperationReport.HidP_Type,
                     UsagePage,
                     LinkCollection,
                     &Usages[0],
                     &nUsages,
                     this->ppd,
                     pReport,
                     cbReport);

            if( FAILED(ntStat) )
            {
                SquirtSqflPtszV(sqfl | sqflError,
                                TEXT("%s: FAIL HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
                                s_tszProc, ntStat, 
                                LinkCollection, UsagePage, Usages[0],
                                PIDUSAGETXT(UsagePage,Usages[0]) );
                hres = ntStat;
            } else
            {
                SquirtSqflPtszV(sqfl | sqflVerbose,
                                TEXT("%s: HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
                                s_tszProc, ntStat, 
                                LinkCollection,UsagePage, Usages[0],
                                PIDUSAGETXT(UsagePage,Usages[0]) );
            }

            if( SUCCEEDED(hres) )
            {
                hres = PID_SendReport(ped, pReport, cbReport, OperationReport.HidP_Type, TRUE, 0, 1); //we block on this call
            }
        }
    }

    DllLeaveCrit();

    ExitOleProc();
    return hres;
}