#include <windows.h>
#include <stdio.h>
#include "kpi.h"
#include "kbasio_module.h"
#include "kbasio_device.h"
#include "kbasio_enum.h"
#include "kbasio_config.h"

#ifdef _DEBUG
#define LEAK_CHECK //メモリリーク検出（しない場合はコメントアウト）
#endif
#ifdef LEAK_CHECK
#include <crtdbg.h>
#endif

// {902E5BCB-D62A-4137-A9DB-167F3663D917}
static const GUID kbasio_GUID = 
{ 0x902e5bcb, 0xd62a, 0x4137, { 0xa9, 0xdb, 0x16, 0x7f, 0x36, 0x63, 0xd9, 0x17 } };

#define KBASIO_VERSION 11
#define KBASIO_VERSION_STR "0.11"

static const DWORD   kbasio_PluginVersion = KBASIO_VERSION;
static const wchar_t kbasio_Description[] = L"ASIO Device v" KBASIO_VERSION_STR ;
static const wchar_t kbasio_Copyright[] = L"Copyright (C) 2016-2024 Kobarin";
static const wchar_t kbasio_TypeName[] = L"ASIO";
static const DWORD   kbasio_SupportConfig = 1;
static const DWORD   kbasio_SupportFlags =  //KPI_DEVICE_MODULEINFO::SUPPORT_FLAG_VOLUME |
                                            //KPI_DEVICE_MODULEINFO::SUPPORT_FLAG_RATE | 
                                            KPI_DEVICE_MODULEINFO::SUPPORT_FLAG_BITPERFECT | 
                                            0;
static const DWORD   kbasio_MultipleInstance = //KPI_MULTINST_INFINITE |
                                               //KPI_MULTINST_ZERO |
                                               //KPI_MULTINST_ONE |
                                               KPI_MULTINST_UNIQUE |
                                               0;

///////////////////////////////////////////////////////////////////////////////
//KbAsioDeviceModule
///////////////////////////////////////////////////////////////////////////////
KbAsioDeviceModule::KbAsioDeviceModule(IKpiConfig *pConfig)
    :m_ModuleInfo{
        sizeof(KPI_DEVICE_MODULEINFO),
        KPI_OUTDEVICE_MODULE_VERSION,
        kbasio_PluginVersion,
        kbasio_MultipleInstance,
        kbasio_GUID,
        kbasio_Description,
        kbasio_Copyright,
        kbasio_TypeName,
        kbasio_SupportConfig,
        kbasio_SupportFlags,
        0, 0, 0, 0 //reservved
    }
{
    m_pConfig = pConfig;
    pConfig->AddRef();
}
///////////////////////////////////////////////////////////////////////////////
KbAsioDeviceModule::~KbAsioDeviceModule(void)
{
    m_pConfig->Release();
}
///////////////////////////////////////////////////////////////////////////////
void  WINAPI KbAsioDeviceModule::GetModuleInfo(const KPI_DEVICE_MODULEINFO **ppInfo)
{
    *ppInfo = &m_ModuleInfo;
}
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI KbAsioDeviceModule::Open(const wchar_t *cszDeviceName, //デバイス名
                                     const wchar_t *cszId,         //識別文字列
                                     const KPI_DEVICE_FORMAT *cpFormat, //再生周波数等
                                     int nBufferSizeSample,      //再生バッファサイズ（サンプル単位）
                                     IKpiOutDevice **ppOutDevice)
{
    KbAsioDevice *pDevice = new KbAsioDevice;
    if(pDevice->Open(cszDeviceName, cszId, cpFormat, nBufferSizeSample, m_pConfig)){
        *ppOutDevice = pDevice;
    }
    else{
        pDevice->Release();
        *ppOutDevice = NULL;
    }
    return *ppOutDevice != NULL;
}
///////////////////////////////////////////////////////////////////////////////
void WINAPI KbAsioDeviceModule::EnumDevices(IKpiDeviceEnumerator *pEnumerator)
{//デバイス名の列挙
    KbAsioEnumerator asioEnumerator;
    asioEnumerator.Enumerate(pEnumerator);
}
///////////////////////////////////////////////////////////////////////////////
BOOL  WINAPI KbAsioDeviceModule::EnumConfig(IKpiConfigEnumerator *pEnumerator)
{
    return kbasio_EnumConfig(pEnumerator);
}
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbAsioDeviceModule::ApplyConfig(const wchar_t *cszSection, //セクション名
                                             const wchar_t *cszKey,     //キー名
                                             UINT64 nValue,             //変更後の値(INT)
                                             double dValue,             //変更後の値(FLOAT)
                                             const wchar_t *cszValue)  //変更後の値(STR)  
{
    return kbasio_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, &kbasio_GUID, NULL, &pConfig);
    if(!pConfig){
        return E_NOINTERFACE;
    }
    KbAsioDeviceModule *pModule = new KbAsioDeviceModule(pConfig);
    *ppvObject = (IKpiOutDeviceModule*)pModule;
    pConfig->Release();
    return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call){
    case DLL_PROCESS_ATTACH:
    {
#ifdef LEAK_CHECK
        //メモリリーク検出
        int tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
        //tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
        tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
        //tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF;
        _CrtSetDbgFlag(tmpDbgFlag);
#endif
        DisableThreadLibraryCalls(hModule);
        break;
    }
    case DLL_PROCESS_DETACH:
    {
        break;
    }

    }
    return TRUE;
}
