/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS Kernel Streaming
 * FILE:            drivers/ksfilter/ks/topoology.c
 * PURPOSE:         KS Allocator functions
 * PROGRAMMER:      Johannes Anderwald
 */

#include "precomp.h"

#define NDEBUG
#include <debug.h>

NTSTATUS
NTAPI
KspCreateObjectType(
    IN HANDLE ParentHandle,
    IN LPWSTR ObjectType,
    PVOID CreateParameters,
    UINT CreateParametersSize,
    IN  ACCESS_MASK DesiredAccess,
    OUT PHANDLE NodeHandle)
{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING Name;

    /* calculate request length */
    Name.Length = 0;
    Name.MaximumLength = (USHORT)(wcslen(ObjectType) * sizeof(WCHAR) + CreateParametersSize +  1 * sizeof(WCHAR));
    Name.MaximumLength += sizeof(WCHAR);
    /* acquire request buffer */
    Name.Buffer = AllocateItem(NonPagedPool, Name.MaximumLength);
    /* check for success */
    if (!Name.Buffer)
    {
        /* insufficient resources */
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    /* build a request which looks like {ObjectClass}\CreateParameters
     * For pins the parent is the reference string used in registration
     * For clocks it is full path for pin\{ClockGuid}\ClockCreateParams
     */
    RtlAppendUnicodeToString(&Name, ObjectType);
    RtlAppendUnicodeToString(&Name, L"\\");
    /* append create parameters */
    RtlMoveMemory(Name.Buffer + (Name.Length / sizeof(WCHAR)), CreateParameters, CreateParametersSize);
    Name.Length += CreateParametersSize;
    Name.Buffer[Name.Length / 2] = L'\0';

    InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE | OBJ_OPENIF, ParentHandle, NULL);
    /* create the instance */
    Status = IoCreateFile(NodeHandle,
                          DesiredAccess,
                          &ObjectAttributes,
                          &IoStatusBlock,
                          NULL,
                          0,
                          0,
                          FILE_OPEN,
                          0,
                          NULL,
                          0,
                          CreateFileTypeNone,
                          NULL,
                          IO_NO_PARAMETER_CHECKING | IO_FORCE_ACCESS_CHECK);

    /* free request buffer */
    FreeItem(Name.Buffer);
    return Status;
}


/*
    @implemented
*/
KSDDKAPI NTSTATUS NTAPI
KsCreateTopologyNode(
    IN  HANDLE ParentHandle,
    IN  PKSNODE_CREATE NodeCreate,
    IN  ACCESS_MASK DesiredAccess,
    OUT PHANDLE NodeHandle)
{
    return KspCreateObjectType(ParentHandle,
                               KSSTRING_TopologyNode,
                               (PVOID)NodeCreate,
                               sizeof(KSNODE_CREATE),
                               DesiredAccess,
                               NodeHandle);
}

/*
    @implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsValidateTopologyNodeCreateRequest(
    IN  PIRP Irp,
    IN  PKSTOPOLOGY Topology,
    OUT PKSNODE_CREATE* OutNodeCreate)
{
    PKSNODE_CREATE NodeCreate;
    ULONG Size;
    NTSTATUS Status;

    /* did the caller miss the topology */
    if (!Topology)
        return STATUS_INVALID_PARAMETER;

    /* set create param  size */
    Size = sizeof(KSNODE_CREATE);

    /* fetch create parameters */
    Status = KspCopyCreateRequest(Irp,
                                  KSSTRING_TopologyNode,
                                  &Size,
                                  (PVOID*)&NodeCreate);

    if (!NT_SUCCESS(Status))
        return Status;

    if (NodeCreate->CreateFlags != 0 || (NodeCreate->Node >= Topology->TopologyNodesCount && NodeCreate->Node != MAXULONG))
    {
        /* invalid node create */
        FreeItem(NodeCreate);
        return STATUS_UNSUCCESSFUL;
    }

    /* store result */
    *OutNodeCreate = NodeCreate;

    return Status;
}

/*
    @implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsTopologyPropertyHandler(
    IN  PIRP Irp,
    IN  PKSPROPERTY Property,
    IN  OUT PVOID Data,
    IN  const KSTOPOLOGY* Topology)
{
    KSP_NODE * Node;
    PIO_STACK_LOCATION IoStack;
    NTSTATUS Status;
    PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;
    LPGUID Guid;

    IoStack = IoGetCurrentIrpStackLocation(Irp);

    DPRINT("KsTopologyPropertyHandler Irp %p Property %p Data %p Topology %p OutputLength %lu PropertyId %lu\n", Irp, Property, Data, Topology, IoStack->Parameters.DeviceIoControl.OutputBufferLength, Property->Id);

    if (Property->Flags != KSPROPERTY_TYPE_GET)
    {
        Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
        Irp->IoStatus.Information = 0;
        return STATUS_NOT_IMPLEMENTED;
    }

    switch(Property->Id)
    {
        case KSPROPERTY_TOPOLOGY_CATEGORIES:
            return KsHandleSizedListQuery(Irp, Topology->CategoriesCount, sizeof(GUID), Topology->Categories);

        case KSPROPERTY_TOPOLOGY_NODES:
            return KsHandleSizedListQuery(Irp, Topology->TopologyNodesCount, sizeof(GUID), Topology->TopologyNodes);

        case KSPROPERTY_TOPOLOGY_CONNECTIONS:
            return KsHandleSizedListQuery(Irp, Topology->TopologyConnectionsCount, sizeof(KSTOPOLOGY_CONNECTION), Topology->TopologyConnections);

        case KSPROPERTY_TOPOLOGY_NAME:
            Node = (KSP_NODE*)Property;

            /* check for invalid node id */
            if (Node->NodeId >= Topology->TopologyNodesCount)
            {
                /* invalid node id */
                Irp->IoStatus.Information = 0;
                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            /* check if there is a name supplied */
            if (!IsEqualGUIDAligned(&Topology->TopologyNodesNames[Node->NodeId], &GUID_NULL))
            {
                /* node name has been supplied */
                Guid = (LPGUID)&Topology->TopologyNodesNames[Node->NodeId];
            }
            else
            {
                /* fallback to topology node type */
                Guid = (LPGUID)&Topology->TopologyNodes[Node->NodeId];
            }

            /* read topology node name */
            Status = KspReadMediaCategory(Guid, &KeyInfo);
            if (!NT_SUCCESS(Status))
            {
                Irp->IoStatus.Information = 0;
                break;
            }

            /* store result size */
            Irp->IoStatus.Information = KeyInfo->DataLength + sizeof(WCHAR);

            /* check for buffer overflow */
            if (KeyInfo->DataLength + sizeof(WCHAR) > IoStack->Parameters.DeviceIoControl.OutputBufferLength)
            {
                /* buffer too small */
                Status = STATUS_BUFFER_OVERFLOW;
                FreeItem(KeyInfo);
                break;
            }

            /* copy result buffer */
            RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, &KeyInfo->Data, KeyInfo->DataLength);

            /* zero terminate it */
            ((LPWSTR)Irp->AssociatedIrp.SystemBuffer)[KeyInfo->DataLength / sizeof(WCHAR)] = L'\0';

            /* free key info */
            FreeItem(KeyInfo);

            break;
        default:
             Irp->IoStatus.Information = 0;
           Status = STATUS_NOT_FOUND;
    }

    Irp->IoStatus.Status = Status;
    return Status;
}

