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

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

#define CSTR_EXPORT
#include "kbcstr_module.h"
#include "kbcstr_player.h"

#ifdef _DEBUG
static const DWORD kbcstr_Version = 0x7FFFFFFF; //デバッグ時は優先度最大
#define KBCSTR_VERSION_STR L"0x7FFFFFFF(Debug)"
#else
static const DWORD kbcstr_Version = 3;
#define KBCSTR_VERSION_STR L"0.03"
#endif

///////////////////////////////////////////////////////////////////////////////
// {36F77F7A-0584-49DE-9DFE-4E74CB43E623}
static const GUID kbcstr_GUID = 
{ 0x36f77f7a, 0x584, 0x49de, { 0x9d, 0xfe, 0x4e, 0x74, 0xcb, 0x43, 0xe6, 0x23 } };

static const wchar_t kbcstr_Description[] = L"CSTR/STR Player v" KBCSTR_VERSION_STR;
static const wchar_t kbcstr_Copyright[] = L"kbcstr.kpi Copyright (C) 2016-2019 Kobarin\n"
                                          L"PsxMC Copyright (C) 2000-2004 ふぃろ";
static const DWORD kbcstr_MultipleInstance = KPI_MULTINST_INFINITE;
static const DWORD kbcstr_SupportTagInfo = 0;
static const DWORD kbcstr_SupportConfig = 0;
static const wchar_t kbcstr_SupportExts[] = L".cstr/.str";
static const wchar_t kbcstr_MultiSongExts[] = L"";
static void* kbcstr_Reserved1 = NULL;
static void* kbcstr_Reserved2 = NULL;
///////////////////////////////////////////////////////////////////////////////
// KbCstrModule
///////////////////////////////////////////////////////////////////////////////
KbCstrModule::KbCstrModule(void)
    :m_ModuleInfo{
        sizeof(KPI_PLAYER_MODULEINFO),
        KPI_PLAYER_MODULE_VERSION,
        kbcstr_Version,
        kbcstr_MultipleInstance,
        kbcstr_GUID,
        kbcstr_Description,
        kbcstr_Copyright,
        kbcstr_SupportExts,
        kbcstr_MultiSongExts,
        kbcstr_Reserved1,
        kbcstr_Reserved2,
        kbcstr_SupportTagInfo,
        kbcstr_SupportConfig,
        0,0,0,0}
{
    WCHAR       szPsxMC[_MAX_PATH + 4];
    LONG        lErr;
    HKEY        hKey = NULL;

    lErr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                         L"SOFTWARE\\PSXMC_FAMILY\\HOME", 0, KEY_READ, &hKey);
    if(lErr == ERROR_SUCCESS) {
        DWORD cbSize = sizeof(szPsxMC);

        lErr = RegQueryValueExW(hKey, NULL, NULL, NULL,
                            (unsigned char *)szPsxMC, &cbSize);
        RegCloseKey(hKey);
        if(lErr == ERROR_SUCCESS) {
            wcscat_s(szPsxMC, L"\\PSXPUB.DLL");
        } else {
            wcscpy_s(szPsxMC, L"PSXPUB.DLL");
        }
    } else {
        wcscpy_s(szPsxMC, L"PSXPUB.DLL");
    }

    hinstancePSXPUB = LoadLibraryExW(szPsxMC, NULL,
                            LOAD_WITH_ALTERED_SEARCH_PATH);
    if(!hinstancePSXPUB)    return;

    _PlbAnalyzeFile = (PFNPlbAnaluzeFile)GetProcAddress(
                                    hinstancePSXPUB, "PlbAnalyzeFile");
    _PlbOpenPlayer  = (PFNPlbOpenPlayer)GetProcAddress(
                                    hinstancePSXPUB, "PlbOpenPlayer");
    _PlbClosePlayer = (PFNPlbClosePlayer)GetProcAddress(
                                    hinstancePSXPUB, "PlbClosePlayer");
    _PlbPlayPlayer  = (PFNPlbPlayPlayer)GetProcAddress(
                                    hinstancePSXPUB, "PlbPlayPlayer");
    _PlbStopPlayer  = (PFNPlbStopPlayer)GetProcAddress(
                                    hinstancePSXPUB, "PlbStopPlayer");
    _PlbPausePlayer = (PFNPlbPausePlayer)GetProcAddress(
                                    hinstancePSXPUB, "PlbPausePlayer");
    _PlbResumPlayer = (PFNPlbResumPlayer)GetProcAddress(
                                    hinstancePSXPUB, "PlbResumPlayer");
    _PlbSeekPlayer  = (PFNPlbSeekPlayer)GetProcAddress(
                                    hinstancePSXPUB, "PlbSeekPlayer");
    _PlbGetPlayRect = (PFNPlbGetPlayRect)GetProcAddress(
                                    hinstancePSXPUB, "PlbGetPlayRect");
    _PlbSetPlayRect = (PFNPlbSetPlayRect)GetProcAddress(
                                    hinstancePSXPUB, "PlbSetPlayRect");
    _PlbGetPlayerStatus = (PFNPlbGetPlayerStatus)GetProcAddress(
                                    hinstancePSXPUB, "PlbGetPlayerStatus");
    _PlbGetPlayPosition = (PFNPlbGetPlayPosition)GetProcAddress(
                                    hinstancePSXPUB, "PlbGetPlayPosition");
    _PlbLockFrameDIB    = (PFNPlbLockFrameDIB)GetProcAddress(
                                    hinstancePSXPUB, "PlbLockFrameDIB");
    _PlbUnlockFrameDIB  = (PFNPlbUnlockFrameDIB)GetProcAddress(
                                    hinstancePSXPUB, "PlbUnlockFrameDIB");
    _PlbSetVolume   = (PFNPlbSetVolume)GetProcAddress(
                                    hinstancePSXPUB, "PlbSetVolume");
    _PlbSetTempo    = (PFNPlbSetTempo)GetProcAddress(
                                    hinstancePSXPUB, "PlbSetTempo");
    KbCstrPlayer::Init();
}
///////////////////////////////////////////////////////////////////////////////
KbCstrModule::~KbCstrModule(void)
{
    if(hinstancePSXPUB){
        FreeLibrary(hinstancePSXPUB);
    }
    KbCstrPlayer::Deinit();
}
///////////////////////////////////////////////////////////////////////////////
void  WINAPI KbCstrModule::GetModuleInfo(const KPI_PLAYER_MODULEINFO **ppModuleInfo)
{
    *ppModuleInfo = &m_ModuleInfo;
}
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbCstrModule::Open(const KPI_MEDIAINFO *cpRequest, 
                                IKpiFile   *pFile, 
                                IKpiFolder *pFolder, 
                                IKpiPlayer **ppPlayer)
{
    *ppPlayer = NULL;
    if(!hinstancePSXPUB) {
        return 0;
    }
    KbCstrPlayer *pPlayer = new KbCstrPlayer;
    DWORD dwRet = pPlayer->Open(pFile, pFolder);
    if(dwRet == 0){
        pPlayer->Release();
        return 0;
    }
    *ppPlayer = pPlayer;
    return dwRet;
}
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI kpi_CreatePlayerModule(IKpiPlayerModule **ppModule,
                                   IKpiConfig *pConfig,
                                   DWORD dwModuleVersion,
                                   void *pvReserved)
{
    static LONG lInterlock = -1;
    if(InterlockedIncrement(&lInterlock) == 0 &&
       dwModuleVersion == KPI_PLAYER_MODULE_VERSION) {
        *ppModule = new KbCstrModule();
        return TRUE;
    } else {
        InterlockedDecrement(&lInterlock);
        *ppModule = NULL;
        return FALSE;
    }
}
///////////////////////////////////////////////////////////////////////////////
HRESULT WINAPI kpi_CreateInstance(REFIID riid, void **ppvObject, IKpiUnknown *pUnknown)
{
    static LONG lInterlock = -1;
    *ppvObject = NULL;
    if(IsEqualIID(riid, IID_IKpiPlayerModule)){
        if(InterlockedIncrement(&lInterlock) != 0){
            InterlockedDecrement(&lInterlock);
            return E_NOINTERFACE;
        }
        IKpiPlayerModule *pPlayerModule = new KbCstrModule();
        *ppvObject = pPlayerModule;
        return S_OK;
    }
    return E_NOINTERFACE;
}
///////////////////////////////////////////////////////////////////////////////
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH: {
        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;
}
///////////////////////////////////////////////////////////////////////////////
