//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include "stdafx.h"
#include "BthAPI.h"
#include "sdplib.h"
#include "util.h"

ListLock::ListLock(CRITICAL_SECTION *CS) : cs(CS)
{
    EnterCriticalSection(cs); 
}

ListLock::~ListLock()
{ 
    LeaveCriticalSection(cs); 
}

HRESULT CreateSdpNodeFromNodeData(NodeData *pData, PSDP_NODE *pNode)
{
//  BSTR string;
//  ULONG length;

    *pNode = NULL;

    switch (pData->type) {
    case SDP_TYPE_NIL:
        *pNode = SdpCreateNodeNil();
         break;

    case SDP_TYPE_BOOLEAN:
        *pNode = SdpCreateNodeBoolean(pData->u.booleanVal);
        break;

    case SDP_TYPE_UINT:
        switch (pData->specificType) {
        case SDP_ST_UINT8:
            *pNode = SdpCreateNodeUInt8(pData->u.uint8);
            break;

        case SDP_ST_UINT16:
            *pNode = SdpCreateNodeUInt16(pData->u.uint16);
            break;

        case SDP_ST_UINT32:
            *pNode = SdpCreateNodeUInt32(pData->u.uint32);
            break;

        case SDP_ST_UINT64:
            *pNode = SdpCreateNodeUInt64(pData->u.uint64);
            break;

        case SDP_ST_UINT128:
            *pNode = SdpCreateNodeUInt128(&pData->u.uint128);
            break;

        default:
            return E_INVALIDARG;
        }
		break;

    case SDP_TYPE_INT:
        switch (pData->specificType) {
        case SDP_ST_INT8:
            *pNode = SdpCreateNodeInt8(pData->u.int8);
            break;

        case SDP_ST_INT16:
            *pNode = SdpCreateNodeInt16(pData->u.int16);
            break;

        case SDP_ST_INT32:
            *pNode = SdpCreateNodeInt32(pData->u.int32);
            break;

        case SDP_ST_INT64:
            *pNode = SdpCreateNodeInt64(pData->u.int64);
            break;

        case SDP_ST_INT128:
            *pNode = SdpCreateNodeInt128(&pData->u.int128);
            break;

        default:
            return E_INVALIDARG;
        }
        break;

    case SDP_TYPE_UUID:
        switch (pData->specificType) {
        case SDP_ST_UUID128:
            *pNode = SdpCreateNodeUUID128(&pData->u.uuid128);
            break;

        case SDP_ST_UUID32:
            *pNode = SdpCreateNodeUUID32(pData->u.uuid32);
            break;

        case SDP_ST_UUID16:
            *pNode = SdpCreateNodeUUID16(pData->u.uuid16);
            break;

        default:
            return E_INVALIDARG;
        }
        break;

    case SDP_TYPE_STRING:
        *pNode = SdpCreateNodeString(pData->u.str.val, pData->u.str.length);
        break;

    case SDP_TYPE_URL:
        *pNode = SdpCreateNodeUrl(pData->u.url.val, pData->u.url.length);
        break;

    case SDP_TYPE_CONTAINER:
        if (pData->u.container == NULL) {
            return E_INVALIDARG;
        }

        *pNode = SdpCreateNode();
        if (*pNode != NULL) {
            (*pNode)->hdr.Type = SDP_TYPE_CONTAINER;
            (*pNode)->hdr.SpecificType = SDP_ST_NONE;
            (*pNode)->u.container = pData->u.container;
            pData->u.container->AddRef();
        }
        break;

    default:
        return E_INVALIDARG;
    }

    if (*pNode == NULL) {
        return E_OUTOFMEMORY;
    }

    return S_OK;
}

void CreateNodeDataFromSdpNode(PSDP_NODE pNode, NodeData *pData)
{
    RtlZeroMemory(pData, sizeof(NodeData));

    pData->type = pNode->hdr.Type;
    pData->specificType = pNode->hdr.SpecificType;

    switch (pData->type) {
    case SDP_TYPE_NIL:
        break;

    case SDP_TYPE_UINT:
        switch (pData->specificType) {
        case SDP_ST_UINT8:
            pData->u.uint8 = pNode->u.uint8;    
            break;

        case SDP_ST_UINT16:
            pData->u.uint16 = pNode->u.uint16; 
            break;

        case SDP_ST_UINT32:
            pData->u.uint32 = pNode->u.uint32; 
            break;

        case SDP_ST_UINT64:
            pData->u.uint64 = pNode->u.uint64; 
            break;

        case SDP_ST_UINT128:
            memcpy(&pData->u.uint128, &pNode->u.uint128, sizeof(pData->u.uint128));
            break;
        }
        break;

    case SDP_TYPE_INT:
        switch (pData->specificType) {
        case SDP_ST_INT8:
            pData->u.int8 = pNode->u.int8;    
            break;

        case SDP_ST_INT16:
            pData->u.int16 = pNode->u.int16; 
            break;

        case SDP_ST_INT32:
            pData->u.int32 = pNode->u.int32; 
            break;

        case SDP_ST_INT64:
            pData->u.int64 = pNode->u.int64; 
            break;

        case SDP_ST_INT128:
            memcpy(&pData->u.int128, &pNode->u.int128, sizeof(pData->u.int128));
            break;
        }
        break;

    case SDP_TYPE_UUID:
        switch (pData->specificType) {
        case SDP_ST_UUID16:
            pData->u.uuid16 = pNode->u.uuid16;
            break;

        case SDP_ST_UUID32:
            pData->u.uuid32 = pNode->u.uuid32;
            break;

        case SDP_ST_UUID128:
            memcpy(&pData->u.uuid128, &pNode->u.uuid128, sizeof(pData->u.uuid128));
            break;
        }
        break;

    case SDP_TYPE_STRING:
        pData->u.str.length = pNode->DataSize;
        pData->u.str.val = NULL;
        break;

    case SDP_TYPE_URL:
        pData->u.url.length = pNode->DataSize;
        pData->u.url.val = NULL;
        break;

    case SDP_TYPE_BOOLEAN:
        pData->u.booleanVal = pNode->u.boolean;
        break;

    case SDP_TYPE_CONTAINER:
        pData->specificType = SDP_ST_NONE;
        pData->u.container = pNode->u.container;
        break;
    }
}

HRESULT CopyStringDataToNodeData(CHAR *str, ULONG strLength, SdpString *pString)
{
    ULONG length;
    HRESULT err = S_OK;

    if (pString->val) {
        if (pString->length >= strLength) {
            length = strLength;
        }
        else {
            length = pString->length;
            err = S_FALSE;
        }

    }
    else {
        pString->val = (CHAR*) CoTaskMemAlloc(strLength);
        if (pString->val == NULL) {
            return E_OUTOFMEMORY;
        }
        else {
            ZeroMemory(pString->val, strLength);
            length = pString->length = strLength;
        }
    }

    memcpy(pString->val, str, length);
    return err;
}

void SdpReleaseContainer(ISdpNodeContainer *container)
{
    container->Release();
}

HRESULT MapNtStatusToHresult(NTSTATUS Status)
{
    if (NT_SUCCESS(Status)) {
        return S_OK;
    }

    switch (Status) {
    case STATUS_INVALID_PARAMETER:  return E_INVALIDARG;
    case STATUS_INSUFFICIENT_RESOURCES:  return E_OUTOFMEMORY;
    case STATUS_NOT_FOUND: return E_FAIL; // bugbug, create a better error code
    default: return E_FAIL;
    }
}

#if ! (defined (UNDER_CE) || defined (WINCE_EMULATION))
#define INDEX_DEFAULT IDI_DEFAULT_MAJOR_CLASS 

MinorDeviceInfo ComputerMinorInfo[] =
{
    { COD_COMPUTER_MINOR_UNCLASSIFIED, IDI_PC, IDS_COD_COMPUTER /* IDS_COD_UNCLASSIFIED */ },
    { COD_COMPUTER_MINOR_DESKTOP,      IDI_PC, IDS_COD_COMPUTER_DESKTOP},
    { COD_COMPUTER_MINOR_SERVER,       IDI_PC, IDS_COD_COMPUTER_SERVER},
    { COD_COMPUTER_MINOR_LAPTOP,       INDEX_DEFAULT, IDS_COD_COMPUTER_LAPTOP},
    { COD_COMPUTER_MINOR_HANDHELD,     INDEX_DEFAULT, IDS_COD_COMPUTER_HANDHELD},
    { COD_COMPUTER_MINOR_PALM,         INDEX_DEFAULT, IDS_COD_COMPUTER_PALM},
};

MinorDeviceInfo PhoneMinorInfo[] =
{
    { COD_PHONE_MINOR_UNCLASSIFIED, INDEX_DEFAULT, IDS_COD_PHONE /* IDS_COD_UNCLASSIFIED */ },
    { COD_PHONE_MINOR_CELLULAR,     INDEX_DEFAULT, IDS_COD_PHONE_CELLULAR }, 
    { COD_PHONE_MINOR_CORDLESS,     INDEX_DEFAULT, IDS_COD_PHONE_CORDLESS }, 
    { COD_PHONE_MINOR_SMART,        INDEX_DEFAULT, IDS_COD_PHONE_SMART }, 
    { COD_PHONE_MINOR_WIRED_MODEM,  INDEX_DEFAULT, IDS_COD_PHONE_WIRED_MODEM }, 
};

MinorDeviceInfo LanMinorInfo[] =
{
    { COD_LAN_ACCESS_0_USED,  INDEX_DEFAULT, IDS_COD_LAN_ACCESS_0_USED },
    { COD_LAN_ACCESS_17_USED, INDEX_DEFAULT, IDS_COD_LAN_ACCESS_17_USED },
    { COD_LAN_ACCESS_33_USED, INDEX_DEFAULT, IDS_COD_LAN_ACCESS_33_USED },
    { COD_LAN_ACCESS_50_USED, INDEX_DEFAULT, IDS_COD_LAN_ACCESS_50_USED },
    { COD_LAN_ACCESS_67_USED, INDEX_DEFAULT, IDS_COD_LAN_ACCESS_67_USED },
    { COD_LAN_ACCESS_83_USED, INDEX_DEFAULT, IDS_COD_LAN_ACCESS_83_USED },
    { COD_LAN_ACCESS_99_USED, INDEX_DEFAULT, IDS_COD_LAN_ACCESS_99_USED },
    { COD_LAN_ACCESS_FULL,    INDEX_DEFAULT, IDS_COD_LAN_ACCESS_FULL },
};

MinorDeviceInfo AudioMinorInfo[] =
{
    { COD_AUDIO_MINOR_UNCLASSIFIED, IDI_SPEAKER, IDS_COD_AUDIO /* IDS_COD_UNCLASSIFIED */ },
    { COD_AUDIO_MINOR_HEADSET,      INDEX_DEFAULT, IDS_COD_AUDIO_HEADSET },
};

//
// defaults for minor mask (MM) and minor shift (MS)
//
#define MM_DEFAULT      (COD_MINOR_MASK) 
#define MS_DEFAULT      (COD_MINOR_BIT_OFFSET)

MajorDeviceInfo ClassOfDeviceInfo[] =
{
    { COD_MAJOR_MISCELLANEOUS, MM_DEFAULT,         MS_DEFAULT,                INDEX_DEFAULT, IDS_COD_MISCELLANEOUS, NULL, 0 },
    { COD_MAJOR_COMPUTER,      MM_DEFAULT,         MS_DEFAULT,                IDI_PC,        IDS_COD_COMPUTER,      ComputerMinorInfo, ARRAY_SIZE(ComputerMinorInfo) }, 
    { COD_MAJOR_PHONE,         MM_DEFAULT,         MS_DEFAULT,                INDEX_DEFAULT, IDS_COD_PHONE,         PhoneMinorInfo,    ARRAY_SIZE(PhoneMinorInfo) }, 
    { COD_MAJOR_LAN_ACCESS,    COD_LAN_ACCESS_MASK, COD_LAN_ACCESS_BIT_OFFSET, IDI_NET,      IDS_COD_LAN_ACCESS,    LanMinorInfo,      ARRAY_SIZE(LanMinorInfo) }, 
    { COD_MAJOR_AUDIO,         MM_DEFAULT,         MS_DEFAULT,                IDI_SPEAKER,   IDS_COD_AUDIO,         AudioMinorInfo,    ARRAY_SIZE(AudioMinorInfo) }, 
    { COD_MAJOR_PERIPHERAL,    MM_DEFAULT,         MS_DEFAULT,                INDEX_DEFAULT, IDS_COD_PERIPHERAL,    NULL, 0 }, 
    { COD_MAJOR_UNCLASSIFIED,  MM_DEFAULT,         MS_DEFAULT,                INDEX_DEFAULT, IDS_COD_UNCLASSIFIED,  NULL, 0 },
};

#define NUM_CLASS_OF_DEVICE_INFO (ARRAY_SIZE(ClassOfDeviceInfo))

struct ServiceDeviceInfo {
    UINT stringID;
    USHORT serviceClass;
};

ServiceDeviceInfo ServiceInfo[] =
{
    { IDS_COD_SERVICE_LIMITED,      COD_SERVICE_LIMITED     },
    { IDS_COD_SERVICE_NETWORKING,   COD_SERVICE_NETWORKING  },
    { IDS_COD_SERVICE_RENDERING,    COD_SERVICE_RENDERING   },
    { IDS_COD_SERVICE_CAPTURING,    COD_SERVICE_CAPTURING   },
    { IDS_COD_SERVICE_OBJECT_XFER,  COD_SERVICE_OBJECT_XFER },
    { IDS_COD_SERVICE_AUDIO,        COD_SERVICE_AUDIO       },
    { IDS_COD_SERVICE_TELEPHONY,    COD_SERVICE_TELEPHONY   },
    { IDS_COD_SERVICE_INFORMATION,  COD_SERVICE_INFORMATION },
};

#define NUM_SERVICE_INFO (ARRAY_SIZE(ServiceInfo))

MajorDeviceInfo* GetMajorClassInfo(bt_cod cod)
{
    //
    // Format is encoded in the bottom 2 bits of the first octet and we only 
    // understand format 00
    //
    if (GET_COD_FORMAT(cod) != COD_VERSION) {
        return NULL;
    }

    DWORD majorClass = GET_COD_MAJOR(cod); 

    for (int i = 0; i < NUM_CLASS_OF_DEVICE_INFO; i++) {
        if (ClassOfDeviceInfo[i].majorClass == majorClass) {
            return ClassOfDeviceInfo +  i;
        }
    }

    return NULL;
}

MinorDeviceInfo* GetMinorClassInfo(bt_cod cod, MajorDeviceInfo* MajorInfo)
{
    UINT i;
    ULONG minorClass;

    minorClass = (cod & MajorInfo->minorMask) >> MajorInfo->minorShift;

    for (i = 0; i < MajorInfo->numMinorDeviceInfo; i++) {
        if (minorClass == MajorInfo->minorDeviceInfo[i].minorClass) {
            return MajorInfo->minorDeviceInfo + i;
        }
    }

    return NULL;
}
#endif // UNDER_CE

HRESULT pNormalizeUuid(NodeData *pDataUuid, GUID* pNormalizedUuid)
{
    if (pDataUuid == NULL || pDataUuid->type != SDP_TYPE_UUID) {
        return E_INVALIDARG;
    }

//  GUID guid;

    switch (pDataUuid->specificType) {
    case SDP_ST_UUID16:
        *pNormalizedUuid = Bluetooth_Base_UUID;
        pNormalizedUuid->Data1 += pDataUuid->u.uuid16;
        return S_OK;

    case SDP_ST_UUID32:
        *pNormalizedUuid = Bluetooth_Base_UUID;
        pNormalizedUuid->Data1 += pDataUuid->u.uuid32;
        return S_OK;

    case SDP_ST_UUID128:
        *pNormalizedUuid =  pDataUuid->u.uuid128;
        return S_OK;

    default:
        return E_INVALIDARG;
    }
}
