#include <windows.h>
#include <stdio.h>

#include "kbwasapi_device.h"
#include "kbwasapi_module.h"
#include "kbwasapi_enum.h"
#include "kbwasapi_config.h"

// {ADB12E22-523C-49ED-9F5B-909AD54C105A}
static const GUID kbwasapi_GUID = 
{ 0xadb12e22, 0x523c, 0x49ed, { 0x9f, 0x5b, 0x90, 0x9a, 0xd5, 0x4c, 0x10, 0x5a } };

#define KBWASAPI_VERSION 10
#define KBWASAPI_VERSION_STR "0.10"

static const DWORD   kbwasapi_PluginVersion = KBWASAPI_VERSION;
static const wchar_t kbwasapi_Description[] = L"WASAPI Device v" KBWASAPI_VERSION_STR ;
static const wchar_t kbwasapi_Copyright[] = L"Copyright (C) 2016-2021 Kobarin";
static const wchar_t kbwasapi_TypeName[] = L"WASAPI";
static const DWORD   kbwasapi_SupportConfig = 1;
static const DWORD   kbwasapi_SupportFlags = //KPI_DEVICE_MODULEINFO::SUPPORT_FLAG_VOLUME |
                                             //KPI_DEVICE_MODULEINFO::SUPPORT_FLAG_RATE | 
                                             KPI_DEVICE_MODULEINFO::SUPPORT_FLAG_BITPERFECT | 
                                             0;
static const DWORD   kbwasapi_MultipleInstance = //KPI_MULTINST_INFINITE |
                                                 //KPI_MULTINST_ZERO |
                                                 //KPI_MULTINST_ONE |
                                                 KPI_MULTINST_UNIQUE |
                                                 0;

///////////////////////////////////////////////////////////////////////////////
//KbWasapiDeviceModule
///////////////////////////////////////////////////////////////////////////////
KbWasapiDeviceModule::KbWasapiDeviceModule(IKpiConfig *pConfig)
    :m_ModuleInfo{
        sizeof(KPI_DEVICE_MODULEINFO),
        KPI_OUTDEVICE_MODULE_VERSION,
        kbwasapi_PluginVersion,
        kbwasapi_MultipleInstance,
        kbwasapi_GUID,
        kbwasapi_Description,
        kbwasapi_Copyright,
        kbwasapi_TypeName,
        kbwasapi_SupportConfig,
        kbwasapi_SupportFlags,
        0, 0, 0, 0 //reservved
    }
{
    m_pConfig = pConfig;
    pConfig->AddRef();
}
///////////////////////////////////////////////////////////////////////////////
KbWasapiDeviceModule::~KbWasapiDeviceModule(void)
{
    m_pConfig->Release();
    KbWasapiDevice::unload_avrt();
}
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI KbWasapiDeviceModule::Open(const wchar_t *cszDeviceName, //デバイス名
                                       const wchar_t *cszId,         //識別文字列
                                       const KPI_DEVICE_FORMAT *cpFormat, //再生周波数等
                                       int nBufferSizeSample,     //再生バッファサイズ（サンプル単位）
                                       IKpiOutDevice **ppOutDevice)  
{
    *ppOutDevice = NULL;
    if(cpFormat->dwFormatType != KPI_MEDIAINFO::FORMAT_PCM &&
       cpFormat->dwFormatType != KPI_MEDIAINFO::FORMAT_DOP){
        return FALSE;
    }
    if(cpFormat->dwFormatType == KPI_MEDIAINFO::FORMAT_DOP){
        if(cpFormat->nBitsPerSample != 24 &&
           cpFormat->nBitsPerSample != 32){
            return FALSE;
        }
    }
    KbWasapiDevice *pDevice = new KbWasapiDevice;
    if(pDevice->Open(cszDeviceName, cszId, cpFormat, nBufferSizeSample, m_pConfig)){
        *ppOutDevice = pDevice;
    }
    else{
        pDevice->Release();
    }
    return *ppOutDevice != NULL;
}
///////////////////////////////////////////////////////////////////////////////
void WINAPI KbWasapiDeviceModule::EnumDevices(IKpiDeviceEnumerator *pEnumerator)//デバイス名の列挙
{
    KbWasapiEnumerator Enumerator(m_pConfig);
    Enumerator.EnumerateDevices(pEnumerator);
}
///////////////////////////////////////////////////////////////////////////////
void  WINAPI KbWasapiDeviceModule::GetModuleInfo(const KPI_DEVICE_MODULEINFO **ppInfo)
{
    *ppInfo = &m_ModuleInfo;
}
///////////////////////////////////////////////////////////////////////////////
BOOL  WINAPI KbWasapiDeviceModule::EnumConfig(IKpiConfigEnumerator *pEnumerator)
{
    return kbwasapi_EnumConfig(m_pConfig, pEnumerator);
}
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbWasapiDeviceModule::ApplyConfig(const wchar_t *cszSection, //セクション名
                                               const wchar_t *cszKey,     //キー名
                                               UINT64 nValue,             //変更後の値(INT)
                                               double dValue,             //変更後の値(FLOAT)
                                               const wchar_t *cszValue)   //変更後の値(STR)
{
    return kbwasapi_ApplyConfig(m_pConfig, cszSection, cszKey, nValue, dValue, cszValue);
}  
///////////////////////////////////////////////////////////////////////////////
HRESULT WINAPI kpi_CreateInstance(REFIID riid, void **ppvObject, IKpiUnknown *pUnknown)
{
    *ppvObject = NULL;
    if(!IsEqualIID(riid, IID_IKpiOutDeviceModule)){
        return E_NOINTERFACE;
    }
    IKpiConfig *pConfig;
    kpi_CreateConfig(pUnknown, &kbwasapi_GUID, NULL, &pConfig);
    KbWasapiDeviceModule *pModule = new KbWasapiDeviceModule(pConfig);
    *ppvObject = (IKpiOutDeviceModule*)pModule;
    pConfig->Release();
    return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call){
        case DLL_PROCESS_ATTACH:{
            DisableThreadLibraryCalls(hModule);
            break;
        }
        case DLL_PROCESS_DETACH:{
        }
    }
    return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
