//================================================================================
// Copyright (C) 1997 Microsoft Corporation
// Author: RameshV
// Description: implements the basic structures for a list of class defintitions
// ThreadSafe: no
// Locks: none
// Please read stdinfo.txt for programming style.
//================================================================================
#include <mm.h>
#include <array.h>
#include <wchar.h>

//BeginExport(typedef)
typedef struct _M_CLASSDEF {
    DWORD                          RefCount;
    DWORD                          ClassId;
    BOOL                           IsVendor;
    DWORD                          Type;
    LPWSTR                         Name;
    LPWSTR                         Comment;
    DWORD                          nBytes;
    LPBYTE                         ActualBytes;
} M_CLASSDEF, *PM_CLASSDEF, *LPM_CLASSDEF;

typedef struct _M_CLASSDEFLIST {
    ARRAY                          ClassDefArray;
} M_CLASSDEFLIST, *PM_CLASSDEFLIST, *LPM_CLASSDEFLIST;
//EndExport(typedef)

//BeginExport(inline)
DWORD       _inline
MemClassDefListInit(
    IN OUT  PM_CLASSDEFLIST        ClassDefList
) {
    return MemArrayInit(&ClassDefList->ClassDefArray);
}
//EndExport(inline)

//BeginExport(inline)
DWORD       _inline
MemClassDefListCleanup(
    IN OUT  PM_CLASSDEFLIST        ClassDefList
) {
    return MemArrayCleanup(&ClassDefList->ClassDefArray);
}
//EndExport(inline)

//BeginExport(function)
DWORD
MemClassDefListFindClassDefInternal(              // dont use this fn outside of classdefl.c
    IN      PM_CLASSDEFLIST        ClassDefList,
    IN      DWORD                  ClassId,
    IN      LPWSTR                 Name,
    IN      LPBYTE                 ActualBytes,
    IN      DWORD                  nBytes,
    IN      LPBOOL                 pIsVendor,
    OUT     PARRAY_LOCATION        Location
) //EndExport(function)
{
    DWORD                          Error;
    PM_CLASSDEF                    ThisClassDef;

    for( Error = MemArrayInitLoc(&ClassDefList->ClassDefArray, Location)
         ; ERROR_FILE_NOT_FOUND != Error ;
         Error = MemArrayNextLoc(&ClassDefList->ClassDefArray, Location)
    ) {
        Require(ERROR_SUCCESS == Error);

        Error = MemArrayGetElement(
            &ClassDefList->ClassDefArray,
            Location,
            (LPVOID *)&ThisClassDef
        );
        Require(ERROR_SUCCESS == Error && ThisClassDef);

        if( pIsVendor != NULL && ThisClassDef->IsVendor != *pIsVendor)
            continue;

        if( ThisClassDef->ClassId == ClassId ) {
            return ERROR_SUCCESS;
        }

        if( nBytes == ThisClassDef->nBytes ) {
            if( 0 == memcmp(ActualBytes, ThisClassDef->ActualBytes, nBytes)) {
                return ERROR_SUCCESS;
            }
        }

        if( Name && 0 == wcscmp(ThisClassDef->Name, Name) ) {
            return ERROR_SUCCESS;
        }
    }

    return ERROR_FILE_NOT_FOUND;
}

//BeginExport(inline)
DWORD       _inline
MemClassDefListFindOptDef(                        // search either by ClassId or by Actual bytes and fill matched stuff
    IN      PM_CLASSDEFLIST        ClassDefList,
    IN      DWORD                  ClassId,
    IN      LPWSTR                 Name,
    IN      LPBYTE                 ActualBytes,
    IN      DWORD                  nBytes,
    OUT     PM_CLASSDEF           *ClassDef       // NULL or valid matching class def
) {
    ARRAY_LOCATION                 Location;
    DWORD                          Error;

    AssertRet(ClassDef, ERROR_INVALID_PARAMETER);

    Error = MemClassDefListFindClassDefInternal(
        ClassDefList,
        ClassId,
        Name,
        ActualBytes,
        nBytes,
        NULL,
        &Location
    );
    if( ERROR_SUCCESS != Error) return Error;

    Error = MemArrayGetElement(
        &ClassDefList->ClassDefArray,
        &Location,
        (LPVOID*)ClassDef
    );
    Require(ERROR_SUCCESS == Error);

    return Error;
}

//BeginExport(function)
DWORD
MemClassDefListAddClassDef(                       // Add or replace option
    IN OUT  PM_CLASSDEFLIST        ClassDefList,
    IN      DWORD                  ClassId,
    IN      BOOL                   IsVendor,
    IN      DWORD                  Type,
    IN      LPWSTR                 Name,
    IN      LPWSTR                 Comment,
    IN      LPBYTE                 ActualBytes,
    IN      DWORD                  nBytes
) //EndExport(function)
{
    ARRAY_LOCATION                 Location;
    DWORD                          Error;
    DWORD                          Size;
    PM_CLASSDEF                    ThisClassDef;
    PM_CLASSDEF                    OldClassDef;

    AssertRet(ClassDefList && ClassId && Name && ActualBytes && nBytes, ERROR_INVALID_PARAMETER );

    Error = MemClassDefListFindClassDefInternal(
        ClassDefList,
        ClassId,
        Name,
        ActualBytes,
        nBytes,
        &IsVendor,
        &Location
    );

    Size = sizeof(M_CLASSDEF)+nBytes;
    Size = ROUND_UP_COUNT(Size, ALIGN_WORST);
    Size += (1+wcslen(Name))*sizeof(WCHAR);
    if( Comment ) Size += (1+wcslen(Comment))*sizeof(WCHAR);

    ThisClassDef = MemAlloc(Size);
    if( NULL == ThisClassDef ) return ERROR_NOT_ENOUGH_MEMORY;

    ThisClassDef->RefCount = 1;
    ThisClassDef->ClassId = ClassId;
    ThisClassDef->IsVendor = IsVendor;
    ThisClassDef->Type = Type;
    ThisClassDef->nBytes = nBytes;
    ThisClassDef->ActualBytes = sizeof(M_CLASSDEF) + (LPBYTE)ThisClassDef;
    memcpy(ThisClassDef->ActualBytes, ActualBytes, nBytes);
    ThisClassDef->Name = (LPWSTR)(ROUND_UP_COUNT(sizeof(M_CLASSDEF)+nBytes, ALIGN_WORST) + (LPBYTE)ThisClassDef);
    wcscpy(ThisClassDef->Name, Name);
    if( Comment ) {
        ThisClassDef->Comment = 1 + wcslen(Name) + ThisClassDef->Name;
        wcscpy(ThisClassDef->Comment, Comment);
    } else {
        ThisClassDef->Comment = NULL;
    }

    if( ERROR_SUCCESS == Error ) {
        DebugPrint2("Overwriting class definition for class-id 0x%lx\n", ClassId);
        Error = MemArrayGetElement(
            &ClassDefList->ClassDefArray,
            &Location,
            (LPVOID *)&OldClassDef
        );
        Require(ERROR_SUCCESS == Error);
        MemFree(OldClassDef);

        Error = MemArraySetElement(
            &ClassDefList->ClassDefArray,
            &Location,
            ThisClassDef
        );
        Require(ERROR_SUCCESS == Error);
        return Error;
    }

    Error = MemArrayAddElement(
        &ClassDefList->ClassDefArray,
        ThisClassDef
    );
    if( ERROR_SUCCESS != Error ) MemFree(ThisClassDef);

    return Error;
}


//BeginExport(inline)
DWORD       _inline
MemClassDefListDelClassDef(
    IN OUT  PM_CLASSDEFLIST        ClassDefList,
    IN      DWORD                  ClassId,
    IN      LPWSTR                 Name,
    IN      LPBYTE                 ActualBytes,
    IN      DWORD                  nBytes
) {
    ARRAY_LOCATION                 Location;
    DWORD                          Error;
    PM_CLASSDEF                    ThisClassDef;

    Error = MemClassDefListFindClassDefInternal(
        ClassDefList,
        ClassId,
        Name,
        ActualBytes,
        nBytes,
        NULL,
        &Location
    );
    if( ERROR_SUCCESS != Error ) return Error;

    Error = MemArrayDelElement(
        &ClassDefList->ClassDefArray,
        &Location,
        &ThisClassDef
    );
    Require(ERROR_SUCCESS == Error && ThisClassDef);

    MemFree(ThisClassDef);
    return ERROR_SUCCESS;
}
//EndExport(inline)

//BeginExport(inline)
DWORD       _inline
MemClassDefListGetRefCount(
    IN      PM_CLASSDEF            ThisClassDef
) {
    return ThisClassDef->RefCount;
}
//EndExport(inline)

//BeginExport(inline)
DWORD       _inline
MemClassDefListIncRefCount(                       // return increased by one value
    IN      PM_CLASSDEF            ThisClassDef
) {
    return ++ThisClassDef->RefCount;
}
//EndExport(inline)

//BeginExport(inline)
DWORD       _inline
MemClassDefListDecRefCount(                       // return decreased by one value
    IN      PM_CLASSDEF            ThisClassDef
) {
    return --ThisClassDef->RefCount;
}
//EndExport(inline)

ULONG                  ClassIdRunningCount = 100;

//BeginExport(function)
DWORD
MemNewClassId(
    VOID
) //EndExport(function)
{
    return InterlockedIncrement(&ClassIdRunningCount);
}

//================================================================================
// end of file
//================================================================================