#include <windows.h>
#include <wchar.h>

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

#include "kbusf_module.h"
#include "kbusf_decoder.h"
#include "kbusf_config.h"

#ifdef _DEBUG
static const DWORD kbusf_Version = 0x7FFFFFFF; //デバッグ時は優先度最大
#define KBUSF_VERSION_STR L"0x7FFFFFFF(Debug)"
#else
static const DWORD kbusf_Version = 1;
#define KBUSF_VERSION_STR L"0.01"
#endif

// {98ADE046-2E5B-4BA0-ADE7-65CB17495D28}
static const GUID kbusf_GUID =
{ 0x98ade046, 0x2e5b, 0x4ba0, { 0xad, 0xe7, 0x65, 0xcb, 0x17, 0x49, 0x5d, 0x28 } };

static const wchar_t kbusf_Description[] = L"Nintendo Ultra 64 Sound Format(USF) Decoder v" KBUSF_VERSION_STR " based on LazyUSF2(2019/5/3)";
static const wchar_t kbusf_Copyright[] = L"kbusf.kpi Copyright (C) 2019 Kobarin\n"
                                         L"LazyUSF2 Copyright (C) Unknown author\n"
                                         L"PSFLIB Copyright (c) 2012-2015 Christopher Snowhill\n"
                                         L"mupen64plus\n"
                                         L"    Richard Goedeken(Richard42)\n"
                                         L"    Bobby Smiles\n"
                                         L"    John Chadwick(NMN)\n"
                                         L"    James Hood(Ebenblues)\n"
                                         L"    Scott Gorman(okaygo)\n"
                                         L"    Scott Knauert(Tillin9)\n"
                                         L"    Jesse Dean(DarkJezter)\n"
                                         L"    Louai Al - Khanji(slougi)\n"
                                         L"    Bob Forder(orbitaldecay)\n"
                                         L"    Jason Espinosa(hasone)\n"
                                         L"    HyperHacker\n"
                                         L"    and others.\n"
                                         L"mupen64\n"
                                         L"    Hacktarux\n"
                                         L"    Dave2001\n"
                                         L"    Zilmar\n"
                                         L"    Gregor Anich (Blight)\n"
                                         L"    Juha Luotio (JttL)\n"
                                         L"    and others."
                                         "\n"
                                         L"zlib (C) 1995-2017 Jean-loup Gailly and Mark Adler";
//////////////////////////////////////////////////////////////////////////////
static const DWORD kbusf_MultipleInstance = KPI_MULTINST_INFINITE;
static const wchar_t kbusf_SupportExts[] = L".usf/.miniusf";
static const wchar_t kbusf_MultiSongExts[] = L"";
static const DWORD kbusf_SupportTagInfo = //KPI_DECODER_MODULEINFO::TAG_TITLE |
                                          //KPI_DECODER_MODULEINFO::TAG_EXTRA |
                                           0;
static const DWORD kbusf_SupportConfig = 1;
static void* kbusf_Reserved1 = NULL;
static void* kbusf_Reserved2 = NULL;
HINSTANCE g_hKpi = NULL;
//////////////////////////////////////////////////////////////////////////////
KbUsfDecoderModule::KbUsfDecoderModule(IKpiConfig *pConfig)
    :m_ModuleInfo{
        sizeof(KPI_DECODER_MODULEINFO), //cb(=sizeof(KPI_DECODER_MODULEINFO))
        KPI_DECODER_MODULE_VERSION,     //dwModuleVersion(=KPI_DECODER_MODULE_VERSION)
        kbusf_Version,                 //dwPluginVersion
        kbusf_MultipleInstance,
        kbusf_GUID,
        kbusf_Description,
        kbusf_Copyright,
        kbusf_SupportExts,
        kbusf_MultiSongExts,
        kbusf_Reserved1,
        kbusf_Reserved2,
        kbusf_SupportTagInfo,
        kbusf_SupportConfig,
        0, 0, 0, 0                      //reserved
     }
{
    m_pConfig = pConfig;
    KbUsfDecoder::Init(pConfig);
    pConfig->AddRef();
}
//////////////////////////////////////////////////////////////////////////////
KbUsfDecoderModule::~KbUsfDecoderModule(void)
{
    KbUsfDecoder::Deinit();
    m_pConfig->Release();
}
//////////////////////////////////////////////////////////////////////////////
void  WINAPI KbUsfDecoderModule::GetModuleInfo(const KPI_DECODER_MODULEINFO **ppInfo)
{
    *ppInfo = &m_ModuleInfo;
}
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbUsfDecoderModule::Open(const KPI_MEDIAINFO *cpRequest,
                                         IKpiFile     *pFile,   //音楽データ
                                         IKpiFolder   *pFolder, //音楽データがあるフォルダ
                                         IKpiDecoder **ppDecoder)
{
    KbUsfDecoder *pDecoder = new KbUsfDecoder(m_pConfig);
    DWORD dwCount = pDecoder->Open(cpRequest, pFile, pFolder);
    if(dwCount == 0){
        *ppDecoder = NULL;
        delete pDecoder;
        return 0;
    }
    *ppDecoder = pDecoder;
    return dwCount;
}
//////////////////////////////////////////////////////////////////////////////
BOOL  WINAPI KbUsfDecoderModule::EnumConfig(IKpiConfigEnumerator *pEnumerator)
{
    return kbusf_EnumConfig(pEnumerator);
}
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbUsfDecoderModule::ApplyConfig(const wchar_t *cszSection,
                                             const wchar_t *cszKey,
                                             INT64 nValue,
                                             double dValue,
                                             const wchar_t *cszValue)
{
    return kbusf_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, &kbusf_GUID, NULL, &pConfig);
    IKpiDecoderModule *pDecoderModule = new KbUsfDecoderModule(pConfig);
    *ppvObject = pDecoderModule;
    pConfig->Release();//KbUsfDecoderModule 内で AddRef しているので必ず必要
    return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
//DllMain
/////////////////////////////////////////////////////////////////////////////
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
        g_hKpi = hModule;
        DisableThreadLibraryCalls(hModule);
        break;
    }
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
