//
//    Copyright (C) Microsoft.  All rights reserved.
//
#ifndef _FXSELFMANAGEDIOSTATEMACHINE_H_
#define _FXSELFMANAGEDIOSTATEMACHINE_H_

//
// This is a magical number based on inspection.  If the queue overflows,
// it is OK to increase these numbers without fear of either dependencies or
// weird side affects.
//
const UCHAR FxSelfManagedIoEventQueueDepth = 8;

enum FxSelfManagedIoEvents {
    SelfManagedIoEventInvalid   = 0x00,
    SelfManagedIoEventStart     = 0x01,
    SelfManagedIoEventCleanup   = 0x02,
    SelfManagedIoEventSuspend   = 0x04,
    SelfManagedIoEventFlush     = 0x08,
    SelfManagedIoEventNull      = 0xFF,
};

enum FxSelfManagedIoStates {
    FxSelfManagedIoInvalid = 0,
    FxSelfManagedIoCreated,
    FxSelfManagedIoInit,
    FxSelfManagedIoInitFailed,
    FxSelfManagedIoStarted,
    FxSelfManagedIoSuspending,
    FxSelfManagedIoStopped,
    FxSelfManagedIoRestarting,
    FxSelfManagedIoFailed,
    FxSelfManagedIoFlushing,
    FxSelfManagedIoFlushed,
    FxSelfManagedIoCleanup,
    FxSelfManagedIoFinal,
    FxSelfManagedIoMax,
};

typedef
_Must_inspect_result_
FxSelfManagedIoStates
(*PFN_SELF_MANAGED_IO_STATE_ENTRY_FUNCTION)(
    __in  FxSelfManagedIoMachine*,
    __out PNTSTATUS Status
    );

struct FxSelfManagedIoTargetState {
    FxSelfManagedIoEvents SelfManagedIoEvent;

    FxSelfManagedIoStates SelfManagedIoState;

#if FX_SUPER_DBG
    BOOLEAN EventDebugged;
#endif
};

//
// This type of union is done so that we can
// 1) shrink the array element to the smallest size possible
// 2) keep types within the structure so we can dump it in the debugger
//
union FxSelfManagedIoMachineEventHistory {
    struct {
        FxSelfManagedIoEvents Event1 : 8;
        FxSelfManagedIoEvents Event2 : 8;
        FxSelfManagedIoEvents Event3 : 8;
        FxSelfManagedIoEvents Event4 : 8;
        FxSelfManagedIoEvents Event5 : 8;
        FxSelfManagedIoEvents Event6 : 8;
        FxSelfManagedIoEvents Event7 : 8;
        FxSelfManagedIoEvents Event8 : 8;
    } E;

    UCHAR History[FxSelfManagedIoEventQueueDepth];
};

//
// This type of union is done so that we can
// 1) shrink the array element to the smallest size possible
// 2) keep types within the structure so we can dump it in the debugger
//
union FxSelfManagedIoMachineStateHistory {
    struct {
        FxSelfManagedIoStates State1 : 8;
        FxSelfManagedIoStates State2 : 8;
        FxSelfManagedIoStates State3 : 8;
        FxSelfManagedIoStates State4 : 8;
        FxSelfManagedIoStates State5 : 8;
        FxSelfManagedIoStates State6 : 8;
        FxSelfManagedIoStates State7 : 8;
        FxSelfManagedIoStates State8 : 8;
    } S;

    UCHAR History[FxSelfManagedIoEventQueueDepth];
};

struct FxSelfManagedIoStateTable {
    PFN_SELF_MANAGED_IO_STATE_ENTRY_FUNCTION StateFunc;

    const FxSelfManagedIoTargetState* TargetStates;

    ULONG TargetStatesCount;
};

class FxSelfManagedIoMachine : public FxStump {

public:
    FxSelfManagedIoMachine(
        __in FxPkgPnp* PkgPnp
        );

    static
    NTSTATUS
    _CreateAndInit(
        __deref_out FxSelfManagedIoMachine** SelfManagedIoMachine,
        __in FxPkgPnp* PkgPnp
        );


    //
    // Sets event callbacks
    //
    VOID
    InitializeMachine(
        __in PWDF_PNPPOWER_EVENT_CALLBACKS Callbacks
        );

    _Must_inspect_result_
    NTSTATUS
    Start(
        VOID
        )
    {
        return ProcessEvent(SelfManagedIoEventStart);
    }

    _Must_inspect_result_
    NTSTATUS
    Suspend(
        VOID
        )
    {
        return ProcessEvent(SelfManagedIoEventSuspend);
    }

    VOID
    Flush(
        VOID
        )
    {
        (void) ProcessEvent(SelfManagedIoEventFlush);
    }

    VOID
    Cleanup(
        VOID
        )
    {
        (void) ProcessEvent(SelfManagedIoEventCleanup);
    }

protected:
    _Must_inspect_result_
    NTSTATUS
    ProcessEvent(
        __in FxSelfManagedIoEvents Event
        );

    static
    FxSelfManagedIoStates
    Init(
        __in  FxSelfManagedIoMachine* This,
        __out PNTSTATUS Status
        );

    static
    FxSelfManagedIoStates
    Suspending(
        __in  FxSelfManagedIoMachine* This,
        __out PNTSTATUS Status
        );

    static
    FxSelfManagedIoStates
    Restarting(
        __in  FxSelfManagedIoMachine* This,
        __out PNTSTATUS Status
        );

    static
    FxSelfManagedIoStates
    Flushing(
        __in  FxSelfManagedIoMachine* This,
        __out PNTSTATUS Status
        );

    static
    FxSelfManagedIoStates
    Cleanup(
        __in  FxSelfManagedIoMachine* This,
        __out PNTSTATUS Status
        );

    WDFDEVICE
    GetDeviceHandle(
        VOID
        );

public:
    FxPnpDeviceSelfManagedIoCleanup     m_DeviceSelfManagedIoCleanup;
    FxPnpDeviceSelfManagedIoFlush       m_DeviceSelfManagedIoFlush;
    FxPnpDeviceSelfManagedIoInit        m_DeviceSelfManagedIoInit;
    FxPnpDeviceSelfManagedIoSuspend     m_DeviceSelfManagedIoSuspend;
    FxPnpDeviceSelfManagedIoRestart     m_DeviceSelfManagedIoRestart;

protected:
    FxWaitLockInternal m_StateMachineLock;

    FxPkgPnp* m_PkgPnp;

    // uses FxSelfManagedIoStates values
    BYTE m_CurrentState;

    UCHAR m_EventHistoryIndex;

    UCHAR m_StateHistoryIndex;

    // extra padded byte is put in here by the compiler

    FxSelfManagedIoMachineEventHistory m_Events;

    FxSelfManagedIoMachineStateHistory m_States;

    static const FxSelfManagedIoStateTable m_StateTable[];

    static const FxSelfManagedIoTargetState m_CreatedStates[];
    static const FxSelfManagedIoTargetState m_InitFailedStates[];
    static const FxSelfManagedIoTargetState m_StartedStates[];
    static const FxSelfManagedIoTargetState m_StoppedStates[];
    static const FxSelfManagedIoTargetState m_FailedStates[];
    static const FxSelfManagedIoTargetState m_FlushedStates[];
};

#endif // _FXSELFMANAGEDIOSTATEMACHINE_H_