/*++

Copyright (c) Microsoft Corporation

Module Name:

    FxCollectionApi.cpp

Abstract:

    This module implements the "C" interface to the collection object.

Author:



Environment:

    Both kernel and user mode

Revision History:

--*/

#include "fxsupportpch.hpp"

extern "C" {
// #include "FxCollectionApi.tmh"
}

//
// Extern the entire file
//
extern "C" {
_Must_inspect_result_
__drv_maxIRQL(DISPATCH_LEVEL)
NTSTATUS
STDCALL
WDFEXPORT(WdfCollectionCreate)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in_opt
    PWDF_OBJECT_ATTRIBUTES CollectionAttributes,
    __out
    WDFCOLLECTION *Collection
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    NTSTATUS status;
    FxCollection *pCollection;
    WDFCOLLECTION hCol;

    pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals);

    //
    // Get the parent's globals if it is present
    //
    if (NT_SUCCESS(FxValidateObjectAttributesForParentHandle(
                        pFxDriverGlobals, CollectionAttributes))) {
        FxObject* pParent;

        FxObjectHandleGetPtrAndGlobals(pFxDriverGlobals,
                                       CollectionAttributes->ParentObject,
                                       FX_TYPE_OBJECT,
                                       (PVOID*)&pParent,
                                       &pFxDriverGlobals);
    }

    FxPointerNotNull(pFxDriverGlobals, Collection);

    *Collection = NULL;

    status = FxValidateObjectAttributes(pFxDriverGlobals, CollectionAttributes);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    pCollection = new (pFxDriverGlobals, CollectionAttributes)
        FxCollection(pFxDriverGlobals);

    if (pCollection != NULL) {
        status = pCollection->Commit(CollectionAttributes, (WDFOBJECT*)&hCol);

        if (NT_SUCCESS(status)) {
            *Collection = hCol;
        }
        else {
            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
                                "Could not create collection object: %!STATUS!",
                                status);

            pCollection->DeleteFromFailedCreate();
        }
    }
    else {
        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
                            "Could not create collection object: "
                            "STATUS_INSUFFICIENT_RESOURCES" );
        status = STATUS_INSUFFICIENT_RESOURCES;
    }

    return status;
}

__drv_maxIRQL(DISPATCH_LEVEL)
ULONG
STDCALL
WDFEXPORT(WdfCollectionGetCount)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFCOLLECTION Collection
    )
{
    DDI_ENTRY();

    FxCollection *pCollection;
    KIRQL irql;
    ULONG count;

    FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
                         Collection,
                         FX_TYPE_COLLECTION,
                         (PVOID *)&pCollection);

    pCollection->Lock(&irql);
    count = pCollection->Count();
    pCollection->Unlock(irql);

    return count;
}

_Must_inspect_result_
__drv_maxIRQL(DISPATCH_LEVEL)
NTSTATUS
STDCALL
WDFEXPORT(WdfCollectionAdd)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFCOLLECTION Collection,
    __in
    WDFOBJECT Object
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxCollection *pCollection;
    FxObject *pObject;
    NTSTATUS status;
    KIRQL irql;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Collection,
                                   FX_TYPE_COLLECTION,
                                   (PVOID*) &pCollection,
                                   &pFxDriverGlobals);

    FxObjectHandleGetPtr(pFxDriverGlobals,
                         Object,
                         FX_TYPE_OBJECT,
                         (PVOID*) &pObject);

    pCollection->Lock(&irql);
    status = pCollection->Add(pObject) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
    pCollection->Unlock(irql);

    return status;
}

__drv_maxIRQL(DISPATCH_LEVEL)
VOID
STDCALL
WDFEXPORT(WdfCollectionRemoveItem)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFCOLLECTION Collection,
    __in
    ULONG Index
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxCollection* pCollection;
    FxCollectionEntry* pEntry;
    FxObject* pObject;
    NTSTATUS status;
    KIRQL irql;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Collection,
                                   FX_TYPE_COLLECTION,
                                   (PVOID*) &pCollection,
                                   &pFxDriverGlobals);

    pCollection->Lock(&irql);

    pEntry = pCollection->FindEntry(Index);

    if (pEntry != NULL) {
        pObject = pEntry->m_Object;
        pCollection->CleanupEntry(pEntry);
        status = STATUS_SUCCESS;
    }
    else {
        pObject = NULL;
        status = STATUS_NOT_FOUND;
    }

    pCollection->Unlock(irql);

    if (pObject != NULL) {
        pCollection->CleanupEntryObject(pObject);
    }

    if (!NT_SUCCESS(status)) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
            "Index %d is not valid in WDFCOLLECTION %p (count is %d), %!STATUS!",
            Index, Collection, pCollection->Count(), status);
        FxVerifierDbgBreakPoint(pFxDriverGlobals);
    }
}

__drv_maxIRQL(DISPATCH_LEVEL)
VOID
STDCALL
WDFEXPORT(WdfCollectionRemove)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFCOLLECTION Collection,
    __in
    WDFOBJECT Item
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxCollection *pCollection;
    FxCollectionEntry *pEntry;
    FxObject* pObject;
    NTSTATUS status;
    KIRQL irql;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Collection,
                                   FX_TYPE_COLLECTION,
                                   (PVOID*) &pCollection,
                                   &pFxDriverGlobals);

    FxObjectHandleGetPtr(pFxDriverGlobals,
                         Item,
                         FX_TYPE_OBJECT,
                         (PVOID*) &pObject);

    pCollection->Lock(&irql);

    pEntry = pCollection->FindEntryByObject(pObject);

    if (pEntry != NULL) {
        pCollection->CleanupEntry(pEntry);
        status = STATUS_SUCCESS;
    }
    else {
        pObject = NULL;
        status = STATUS_NOT_FOUND;
    }

    pCollection->Unlock(irql);

    if (pObject != NULL) {
        pCollection->CleanupEntryObject(pObject);
    }

    if (!NT_SUCCESS(status)) {
        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
                            "WDFOBJECT %p not in WDFCOLLECTION %p, %!STATUS!",
                            Item, Collection, status);
        FxVerifierDbgBreakPoint(pFxDriverGlobals);
    }
}

__drv_maxIRQL(DISPATCH_LEVEL)
WDFOBJECT
STDCALL
WDFEXPORT(WdfCollectionGetItem)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFCOLLECTION Collection,
    __in
    ULONG Index
    )
{
    DDI_ENTRY();

    FxCollection *pCollection;
    FxObject *pObject;
    KIRQL irql;

    FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
                         Collection,
                         FX_TYPE_COLLECTION,
                         (PVOID*) &pCollection);

    pCollection->Lock(&irql);
    pObject = pCollection->GetItem(Index);
    pCollection->Unlock(irql);

    if (pObject == NULL) {
        return NULL;
    }

    return pObject->GetObjectHandle();
}

__drv_maxIRQL(DISPATCH_LEVEL)
WDFOBJECT
STDCALL
WDFEXPORT(WdfCollectionGetFirstItem)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFCOLLECTION Collection
    )
{
    DDI_ENTRY();

    FxCollection *pCollection;
    FxObject* pObject;
    KIRQL irql;

    FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
                         Collection,
                         FX_TYPE_COLLECTION,
                         (PVOID*) &pCollection);

    pCollection->Lock(&irql);
    pObject = pCollection->GetFirstItem();
    pCollection->Unlock(irql);

    if (pObject != NULL) {
        return pObject->GetObjectHandle();
    }
    else {
        return NULL;
    }
}

__drv_maxIRQL(DISPATCH_LEVEL)
WDFOBJECT
STDCALL
WDFEXPORT(WdfCollectionGetLastItem)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFCOLLECTION Collection
    )
{
    DDI_ENTRY();

    FxCollection *pCollection;
    FxObject* pObject;
    KIRQL irql;

    FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
                         Collection,
                         FX_TYPE_COLLECTION,
                         (PVOID*) &pCollection);

    pCollection->Lock(&irql);
    pObject = pCollection->GetLastItem();
    pCollection->Unlock(irql);

    if (pObject != NULL) {
        return pObject->GetObjectHandle();
    }
    else {
        return NULL;
    }
}

} // extern "C" of entire file