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

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

#include "kbdshow_module.h"
#include "kbdshow_decoder.h"
#include "kbdshow_config.h"
#include "kbmf_decoder.h"
#include "kbwav_decoder.h"

#ifdef _DEBUG
static const DWORD kbdshow_Version = 0x7FFFFFFF; //デバッグ時は優先度最大
#define KBDSHOW_VERSION_STR L"0x7FFFFFFF(Debug)"
#else
static const DWORD kbdshow_Version = 204;
#define KBDSHOW_VERSION_STR L"2.04"
#endif

HINSTANCE g_hKpi = NULL;
static const wchar_t kbdshow_Description[] = L"DirectShow Decoder v" KBDSHOW_VERSION_STR;
static const wchar_t kbdshow_Copyright[] =   L"Copyright (C) 2000-2021 Kobarin";
//{5B46E68A-4555-4B0A-A56C-22F91FF4F1DE}
static const GUID kbdshow_GUID = 
{ 0x5b46e68a, 0x4555, 0x4b0a, { 0xa5, 0x6c, 0x22, 0xf9, 0x1f, 0xf4, 0xf1, 0xde } }; 


static const DWORD kbdshow_MultipleInstance = KPI_MULTINST_INFINITE;//同時に何個でも作成可能
                                            //KPI_MULTINST_ZERO;  //常に別プロセス起動
                                            //KPI_MULTINST_ONE;   //2個目以降は別プロセス起動
                                            //KPI_MULTINST_UNIQUE;//プロセスをまたいでも１つまで
                                            //KPI_MULTINST_ZERO + N;//(N+1)個目以降は別プロセス起動
                                            //↑どれか１つを選択
static const wchar_t kbdshow_SupportExts[] = L"";  //実行時に決定
static const wchar_t kbdshow_MultiSongExts[] = L"";
static const DWORD kbdshow_SupportTagInfo = //KPI_DECODER_MODULEINFO::TAG_TITLE | //未対応ならコメントアウト
                                            //KPI_DECODER_MODULEINFO::TAG_EXTRA | //未対応ならコメントアウト
                                             0;
static const DWORD kbdshow_SupportConfig = 1;
static void* kbdshow_Reserved1 = NULL;
static void* kbdshow_Reserved2 = NULL;
//////////////////////////////////////////////////////////////////////////////
KbDShowDecoderModule::KbDShowDecoderModule(IKpiConfig *pConfig)
    :m_ModuleInfo{
        sizeof(KPI_DECODER_MODULEINFO), //cb(=sizeof(KPI_DECODER_MODULEINFO))
        KPI_DECODER_MODULE_VERSION,     //dwModuleVersion(=KPI_DECODER_MODULE_VERSION)
        kbdshow_Version,
        kbdshow_MultipleInstance,
        kbdshow_GUID,
        kbdshow_Description,
        kbdshow_Copyright,
        kbdshow_SupportExts,
        kbdshow_MultiSongExts,
        kbdshow_Reserved1,
        kbdshow_Reserved2,
        kbdshow_SupportTagInfo,
        kbdshow_SupportConfig,
        0, 0, 0, 0                      //reserved
     }                     
{
    m_pszSupportExts = NULL;
    m_pConfig = pConfig;
    pConfig->AddRef();
    //KbMFDecoder::Initialize();//必要になったら KbMFDecoder 内部で呼ぶ
}
//////////////////////////////////////////////////////////////////////////////
KbDShowDecoderModule::~KbDShowDecoderModule(void)
{
    free(m_pszSupportExts);
    m_pConfig->Release();
    KbMFDecoder::Uninitialize();
}
//////////////////////////////////////////////////////////////////////////////
void  WINAPI KbDShowDecoderModule::GetModuleInfo(const KPI_DECODER_MODULEINFO **ppInfo)
{
    bool bTestMode;
    free(m_pszSupportExts);
    m_pszSupportExts = kbdshow_GetSupportExts(m_pConfig, &bTestMode);
    m_ModuleInfo.cszSupportExts = m_pszSupportExts;
    m_ModuleInfo.dwPluginVersion = bTestMode ? 0x7FFFFFFF : kbdshow_Version;
    *ppInfo = &m_ModuleInfo;
}
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbDShowDecoderModule::Open(const KPI_MEDIAINFO *cpRequest,
                                        IKpiFile     *pFile,   //音楽データ
                                        IKpiFolder   *pFolder, //音楽データがあるフォルダ
                                        IKpiDecoder **ppDecoder)
{
    wchar_t szFileName[MAX_PATH];
    pFile->GetFileName(szFileName, sizeof(szFileName));
    wchar_t *pszExt = wcsrchr(szFileName, L'.');
    if (!pszExt)return 0;

    DWORD dwCount = 0;
#if 1 
    if(_wcsicmp(pszExt, L".wav") == 0){
        KbWavDecoder *pWavDecoder = new KbWavDecoder;
        if((dwCount = pWavDecoder->Open(cpRequest, pFile, pFolder) != 0)){
            *ppDecoder = pWavDecoder;
            return dwCount;
        }
        delete pWavDecoder;
        pFile->Seek(0, FILE_BEGIN);
    }
#endif
    bool bSupportDS;
    bool bSupportMF;
    int  nPriorDS_MF;
    kbdshow_IsSupport(m_pConfig, pszExt, &bSupportDS, &bSupportMF, &nPriorDS_MF);
    if(bSupportMF && bSupportDS){//拡張子両対応
        if(nPriorDS_MF == 0){//MF=>DS
            KbMFDecoder* pMFDecoder = new KbMFDecoder;
            if ((dwCount = pMFDecoder->Open(cpRequest, m_pConfig, pFile, pFolder)) != 0) {
                *ppDecoder = pMFDecoder;
                return dwCount;
            }
            delete pMFDecoder;
            pFile->Seek(0, FILE_BEGIN);
            KbDShowDecoder* pDShowDecoder = new KbDShowDecoder;
            if ((dwCount = pDShowDecoder->Open(cpRequest, m_pConfig, pFile, pFolder)) != 0) {
                *ppDecoder = pDShowDecoder;
                return dwCount;
            }
            delete pDShowDecoder;
            return 0;
        }
        else  if(nPriorDS_MF == 1){//DS=>MF
            KbDShowDecoder* pDShowDecoder = new KbDShowDecoder;
            if ((dwCount = pDShowDecoder->Open(cpRequest, m_pConfig, pFile, pFolder)) != 0) {
                *ppDecoder = pDShowDecoder;
                return dwCount;
            }
            delete pDShowDecoder;
            pFile->Seek(0, FILE_BEGIN);
            KbMFDecoder* pMFDecoder = new KbMFDecoder;
            if ((dwCount = pMFDecoder->Open(cpRequest, m_pConfig, pFile, pFolder)) != 0) {
                *ppDecoder = pMFDecoder;
                return dwCount;
            }
            delete pMFDecoder;
            return 0;
        }
    }
    if(bSupportMF){
        KbMFDecoder *pMFDecoder = new KbMFDecoder;
        if((dwCount = pMFDecoder->Open(cpRequest, m_pConfig, pFile, pFolder)) != 0){
            *ppDecoder = pMFDecoder;
            return dwCount;
        }
        delete pMFDecoder;
        pFile->Seek(0, FILE_BEGIN);
    }
    if(bSupportDS){
        KbDShowDecoder* pDShowDecoder = new KbDShowDecoder;
        if ((dwCount = pDShowDecoder->Open(cpRequest, m_pConfig, pFile, pFolder)) != 0) {
            *ppDecoder = pDShowDecoder;
            return dwCount;
        }
        delete pDShowDecoder;
    }
    return 0;
}
//////////////////////////////////////////////////////////////////////////////
BOOL  WINAPI KbDShowDecoderModule::EnumConfig(IKpiConfigEnumerator *pEnumerator)
{
    return kbdshow_EnumConfig(m_pConfig, pEnumerator);
}
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbDShowDecoderModule::ApplyConfig(const wchar_t *cszSection, //セクション名
                                               const wchar_t *cszKey,     //キー名
                                               INT64 nValue,
                                               double dValue,
                                               const wchar_t *cszValue)     
{
    return kbdshow_ApplyConfig(m_pConfig, cszSection, cszKey, nValue, dValue, cszValue);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT WINAPI kpi_CreateInstance(REFIID riid, void **ppvObject, IKpiUnknown *pUnknown)
{
    *ppvObject = NULL;
    if(!IsEqualIID(riid, IID_IKpiDecoderModule)){
        return E_NOINTERFACE;
    }
    IKpiConfig *pConfig = NULL;
    kpi_CreateConfig(pUnknown, &kbdshow_GUID, NULL, &pConfig);
    IKpiDecoderModule *pDecoderModule = new KbDShowDecoderModule(pConfig);
    *ppvObject = pDecoderModule;
    pConfig->Release();//KbDShowDecoderModule 内で AddRef しているので必ず必要
    return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
//DllMain
/////////////////////////////////////////////////////////////////////////////
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH: {
        g_hKpi = hModule;
        DisableThreadLibraryCalls(hModule);
#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
        break;
    }
    case DLL_PROCESS_DETACH: {
        break;
    }
    }
    return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
