#include <windows.h>
#include <stdio.h>
#include <dsound.h>
#ifdef _DEBUG
#include <assert.h>
#endif

#include "kbdsound_device.h"
#include "kbdsound_enum.h"

static const wchar_t g_cszGuidNullStr[] = L"{00000000-0000-0000-0000-000000000000}";

typedef HRESULT (WINAPI *pfnDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID);

extern pfnDirectSoundEnumerate fnDirectSoundEnumerate;

///////////////////////////////////////////////////////////////////////////////
static void __fastcall GuidToStr(GUID *pGuid, wchar_t* pszDest, int nSize)
{//GUID を文字列に変換
    DWORD data1 = pGuid->Data1;
    DWORD data2 = pGuid->Data2;
    DWORD data3 = pGuid->Data3;
    DWORD data4[8];
    int i;
    for(i = 0 ; i < 8; i++){
        data4[i] = pGuid->Data4[i];
    }
    _snwprintf_s(pszDest, nSize, _TRUNCATE, 
                 L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
                 data1, data2, data3, data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7]);
    wchar_t *psz = pszDest;
    while(*psz){
        if(L'a' <= *psz && *psz <= L'f'){
            *psz += (L'A' - L'a');
        }
        psz++;
    }
#ifdef _DEBUG
    wchar_t szTemp[128];
    StringFromGUID2(*pGuid, szTemp, _countof(szTemp));
    assert(wcscmp(pszDest, szTemp) == 0);
#endif
}
///////////////////////////////////////////////////////////////////////////////
static void __fastcall StrToGuid(const wchar_t *cszSrc, GUID *pGuid)
{//文字列を GUID に変換（変換可能なら）
    ZeroMemory(pGuid, sizeof(GUID));
    static const int nGuidLen = sizeof(g_cszGuidNullStr)/sizeof(g_cszGuidNullStr[0]) - 1;
    int nSrcLen = wcslen(cszSrc);
    if(nSrcLen != nGuidLen){
        return;
    }
    if(cszSrc[0] == L'{' && cszSrc[nGuidLen-1] == L'}'){
        DWORD data1 = 0;
        DWORD data2 = 0;
        DWORD data3 = 0;
        DWORD data4[8] = {0};
         
        swscanf_s(cszSrc, L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
                    &data1, &data2, &data3, 
                    &data4[0],&data4[1],&data4[2],&data4[3],&data4[4],&data4[5],&data4[6],&data4[7]);
        pGuid->Data1 = data1;
        pGuid->Data2 = (WORD)data2;
        pGuid->Data3 = (WORD)data3;
        int i;
        for(i = 0; i < 8; i++){
            pGuid->Data4[i] = (BYTE)data4[i];
        }
    }
    
}
////////////////////////////////////////////////////////////////
KbDSoundEnumerator::KbDSoundEnumerator(void)
{
    m_nCount = 0;
    m_pDevices = NULL;
    KbDSoundDevice::LoadDSoundDll();
}
////////////////////////////////////////////////////////////////
KbDSoundEnumerator::~KbDSoundEnumerator(void)
{
    free(m_pDevices);
}
////////////////////////////////////////////////////////////////
BOOL CALLBACK KbDSoundEnumerator::DSoundEnumCallback(GUID* pGUID, 
                                                     LPCWSTR strDesc, 
                                                     LPCWSTR strDrvName,
                                                     VOID *pContext)
{
    KbDSoundEnumerator *pEnumerator = (KbDSoundEnumerator*)pContext;
    return pEnumerator->DSoundEnumCallback(pGUID, strDesc, strDrvName);
}
////////////////////////////////////////////////////////////////
BOOL __fastcall KbDSoundEnumerator::DSoundEnumCallback(GUID* pGUID, 
                                                        LPCWSTR strDesc, 
                                                        LPCWSTR strDrvName)
{
    const int nIndex = m_nCount++;
    m_pDevices = (DSOUND_DEVICE*)realloc(m_pDevices, sizeof(DSOUND_DEVICE) * m_nCount);
    if(pGUID){
        m_pDevices[nIndex].DriverGuid = *pGUID;
    }
    else{
        ZeroMemory(&m_pDevices[nIndex].DriverGuid, sizeof(GUID));
    }
    wcsncpy_s(m_pDevices[nIndex].szDeviceName, strDesc, _TRUNCATE);
    return TRUE;
}
////////////////////////////////////////////////////////////////
BOOL __fastcall KbDSoundEnumerator::GetDriverGuidFromDeviceName(const wchar_t *cszDeviceName, const wchar_t *cszId, GUID *pGuid)
{
    ZeroMemory(pGuid, sizeof(GUID));
    //デバイスの一覧を取得
    EnumerateDevices(NULL);
    int i;
    for(i = 0; i < m_nCount; i++){
        if(wcscmp(m_pDevices[i].szDeviceName, cszDeviceName) == 0){
            GUID guid;
            StrToGuid(cszId, &guid);
            if(m_pDevices[i].DriverGuid == guid){
                *pGuid = guid;
                return TRUE;
            }
        }
    }
    return FALSE;
}
////////////////////////////////////////////////////////////////
void __fastcall KbDSoundEnumerator::EnumerateDevices(IKpiDeviceEnumerator *pDeviceEnumerator)
{
    int i;
    if(!m_pDevices){//一覧を未取得
        //デバイスの一覧を取得
        if(fnDirectSoundEnumerate){
            fnDirectSoundEnumerate(DSoundEnumCallback, this);
        }
    }
    if(!pDeviceEnumerator){
        return;
    }
    for(i = 0; i < m_nCount; i++){
        wchar_t szId[128];
        GuidToStr(&m_pDevices[i].DriverGuid, szId, _countof(szId));
        pDeviceEnumerator->Enumerate(m_pDevices[i].szDeviceName, szId);
    }
}
////////////////////////////////////////////////////////////////
void __fastcall KbDSoundEnumerator::GetDeviceName(int nIndex, 
                                  wchar_t *pszDeviceName, int nSizeDevice,
                                  wchar_t *pszId, int nSizeId)
{
    if(nIndex < 0 || nIndex >= m_nCount){
        pszDeviceName[0] = pszId[0] = 0;
        return;
    }
    wcsncpy_s(pszDeviceName, nSizeDevice, m_pDevices[nIndex].szDeviceName, _TRUNCATE);
    GuidToStr(&m_pDevices[nIndex].DriverGuid, pszId, nSizeId);
}
////////////////////////////////////////////////////////////////
