#include <windows.h>
#include <stdio.h>
#include "kbwsr_config.h"
#include "kbwsr_decoder.h"

extern HINSTANCE g_hKpi;

//エミュレーションコアのバージョンが新しくなったら以下を修正する
#define MEDNAFEN_VERSION "v1.29.0"
#define OSWAN_VERSION    "v1.73"
#define IN_WSR_VERSION   "2006-05-03"

static const int emulation_core_count = 3;
static const wchar_t* const emulation_core_name[emulation_core_count] = { L"mednafen " MEDNAFEN_VERSION, L"Oswan " OSWAN_VERSION, L"in_wsr " IN_WSR_VERSION };
static const wchar_t* emulation_core_drive_name[emulation_core_count] = { L"wsr_player_mednafen.dll", L"wsr_player_oswan.dll", L"wsr_player_in_wsr.dll" };

/////////////////////////////////////////////////////////////////////////////
static const wchar_t SEC_GENERAL[] = L"General";
static const wchar_t KEY_EMUCORE[] = L"EmulationCore";
static const wchar_t KEY_SAMPLERATE_MEDNAFEN[] = L"SampleRate_mednafen";
static const wchar_t KEY_SAMPLERATE_OSWAN[] = L"SampleRate_oswan";
static const wchar_t KEY_SAMPLERATE_IN_WSR[] = L"SampleRate_in_wsr";
static const wchar_t KEY_VOLUME_MEDNAFEN[] = L"Volume_mednafen";
static const wchar_t KEY_VOLUME_OSWAN[] = L"Volume_oswan";
static const wchar_t KEY_VOLUME_IN_WSR[] = L"Volume_in_wsr";
static const wchar_t KEY_CHANNEL_MUTING[] = L"ChannelMuting";

static const int EMUCORE_DEFAULT = 0;
static const wchar_t SZ_EMUCORE_DEFAULT[] = L"0";

static const int SAMPLERATE_DEFAULT = 44100;
static const int SAMPLERATE_OSWAN_DEFAULT = 48000;
static const wchar_t SZ_SAMPLERATE_DEFAULT[] = L"44100";
static const wchar_t SZ_SAMPLERATE_OSWAN_DEFAULT[] = L"48000";

static const double VOLUME_DEFAULT = 100.0;
static const wchar_t SZ_VOLUME_DEFAULT[] = L"100.0";

static const wchar_t CANNEL_MUTING_DEFAULT[] = L"0000";

/////////////////////////////////////////////////////////////////////////////
static void __fastcall CheckMaskStr(wchar_t* pszDest, int nDestSize, const wchar_t* cszSrc, int nDigits)//nDigits=桁数
{//パートマスクの文字列を検証
 //0 と 1 以外の文字を含む場合、数字であれば 1 に置換、数字以外なら 0 に置換
 //(nDigits+1) 桁目以降の文字は無視
 //nDigits 桁未満の場合、以降は 0 に置換(最大nDigits桁)
    if (nDestSize <= nDigits) {
        pszDest[0] = 0;
        return;
    }
    wcsncpy_s(pszDest, nDestSize, cszSrc, _TRUNCATE);
    int i;
    for (i = 0; i < nDigits; i++) {
        if (!pszDest[i]) {
            for (int j = i; j < nDigits; j++) {
                pszDest[j] = L'0';
            }
            break;
        }
        if (pszDest[i] == L'0') {
            continue;
        }
        if (L'1' <= pszDest[i] && pszDest[i] <= L'9') {//0 以外の数字は 1 に置換
            pszDest[i] = L'1';
        }
        else {//数字以外は 0 に置換
            pszDest[i] = L'0';
        }
    }
    pszDest[nDigits] = 0;
}
/////////////////////////////////////////////////////////////////////////////
static int __fastcall GetChannelMutingFromMaskStr(const wchar_t *cszMask)
{
    int ret = 0;
    for(int i = 0; cszMask[i] && i < 4; i++){
        ret |= ((cszMask[i] != L'0') << i);
    }
    return ret;
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall GetCoreSearchPath(wchar_t *pszSearchPath, int nSize)
{//Core DLL の検索パス(kbwsr.kpi と同じフォルダを取得)
    GetModuleFileName(g_hKpi, pszSearchPath, nSize);
    wchar_t *pszPathDelimiter = wcsrchr(pszSearchPath, L'\\');
    if(pszPathDelimiter){
        pszPathDelimiter[1] = 0;
    }
}
/////////////////////////////////////////////////////////////////////////////
BOOL __fastcall kbwsr_EnumConfig(IKpiConfigEnumerator *pEnumerator)
{
    static const KPI_CFG_SECTION sec[] = {
        {SEC_GENERAL, NULL, L"kbwsr.kpi に関する一般的な設定です。"},
        {NULL, NULL, NULL}
    };
    static const KPI_CFG_KEY key[] = {
    //   type, section, key, keydesc, default, min, max, list, listdesc, help
        {KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_EMUCORE, L"エミュレーションコア",
            SZ_EMUCORE_DEFAULT, L"0", L"2", //default/min/max
            L"0\t1\t2\t", L"mednafen\toswan\tin_wsr", //list/listdesc
            L"エミュレーションコアを選択します。\r\n"
            L"0: mednafen " MEDNAFEN_VERSION "\r\n"
            L"1: Oswan " OSWAN_VERSION "\r\n"
            L"2: in_wsr " IN_WSR_VERSION "\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_SAMPLERATE_MEDNAFEN, L"再生周波数(mednafen)",
            SZ_SAMPLERATE_DEFAULT, L"-192000", L"192000", //default/min/max
            L"44100\t48000\t96000\t192000\t-44100\t-48000\t-96000\t-192000", //list
            NULL, //listdesc
            L"本体から要求がない場合の再生周波数(mednafen)\r\n"
            L"負の値を指定すると本体からの要求を無視します。\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_SAMPLERATE_OSWAN, L"再生周波数(oswan)",
            SZ_SAMPLERATE_OSWAN_DEFAULT, NULL, NULL, //default/min/max
            L"12000\t24000\t48000\t96000\t-12000\t-24000\t-48000\t-96000", //list
            NULL, //listdesc
            L"本体から要求がない場合の再生周波数(oswan)\r\n"
            L"負の値を指定すると本体からの要求を無視します。\r\n"
            L"oswan は 12000/24000/48000/96000 のみ対応しています。\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_SAMPLERATE_IN_WSR, L"再生周波数(in_wsr)",
            SZ_SAMPLERATE_DEFAULT, L"-192000", L"192000", //default/min/max
            L"44100\t48000\t96000\t192000\t-44100\t-48000\t-96000\t-192000", //list
            NULL, //listdesc
            L"本体から要求がない場合の再生周波数(in_wsr)\r\n"
            L"負の値を指定すると本体からの要求を無視します。\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_FLOAT, SEC_GENERAL, KEY_VOLUME_MEDNAFEN, L"音量(%)(mednafen)",
            SZ_VOLUME_DEFAULT, L"10.0", L"500.0", //default/min/max
            L"200\t150\t125\t100\t90\t80\t70\t60\t50\t40\t30\t20\t10", //list
            L"200%\t150%\t125%\t100%\t90%\t80%\t70%\t60%\t50%\t40%\t30%\t20%\t10%", //listdesc
            L"音量(%)を指定します。\r\n"
            L"エミュレーションコアごとに指定することが出来ます。\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_FLOAT, SEC_GENERAL, KEY_VOLUME_OSWAN, L"音量(%)(oswan)",
            SZ_VOLUME_DEFAULT, L"10.0", L"500.0", //default/min/max
            L"200\t150\t125\t100\t90\t80\t70\t60\t50\t40\t30\t20\t10", //list
            L"200%\t150%\t125%\t100%\t90%\t80%\t70%\t60%\t50%\t40%\t30%\t20%\t10%", //listdesc
            L"音量(%)を指定します。\r\n"
            L"エミュレーションコアごとに指定することが出来ます。\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_FLOAT, SEC_GENERAL, KEY_VOLUME_IN_WSR, L"音量(%)(in_wsr)",
            SZ_VOLUME_DEFAULT, L"10.0", L"500.0", //default/min/max
            L"200\t150\t125\t100\t90\t80\t70\t60\t50\t40\t30\t20\t10", //list
            L"200%\t150%\t125%\t100%\t90%\t80%\t70%\t60%\t50%\t40%\t30%\t20%\t10%", //listdesc
            L"音量(%)を指定します。\r\n"
            L"エミュレーションコアごとに指定することが出来ます。\r\n"
            L""
            },//help
        { KPI_CFG_TYPE_STR, SEC_GENERAL, KEY_CHANNEL_MUTING, NULL,
            CANNEL_MUTING_DEFAULT, NULL, L"5", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"4桁の文字列でミュートするチャンネルを 1 にします。\r\n"
            L"(例： 1010)\r\n"
            L"\r\n"
            L"設定の変更が反映されるまで時間がかかる場合は本体の\r\n"
            L"「ツール」-「環境設定」-「優先度」-「先読みバッファのサイズ」と\r\n"
            L"「デバイス設定」の「再生バッファサイズ」\r\n"
            L"の値を小さくして下さい。\r\n"
            L"" },
        {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
    };
    for(int i = 0; sec[i].cszSection; i++){
        pEnumerator->EnumSection(&sec[i]);
    }
    for(int i = 0; key[i].cszSection; i++){
        pEnumerator->EnumKey(&key[i]);
    }
    return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
DWORD __fastcall kbwsr_ApplyConfig(IKpiConfig *pConfig,
                               const wchar_t *cszSection,//セクション名
                               const wchar_t *cszKey,    //キー名
                               INT64  nValue, //変更後の値(BOOL/INT の場合)
                               double dValue, //変更後の値(FLOAT の場合)
                               const wchar_t *cszValue)////変更後の値(文字列)
{
    if(!cszKey[0]){
        return KPI_CFGRET_OK;
    }
    if (wcscmp(cszKey, KEY_CHANNEL_MUTING) == 0) {
        wchar_t szMask[5];
        CheckMaskStr(szMask, _countof(szMask), cszValue, 4);
        pConfig->SetStr(cszSection, cszKey, szMask);
        return KPI_CFGRET_NOTIFY;
    }
    pConfig->SetStr(cszSection, cszKey, cszValue);
    if(wcscmp(cszKey, KEY_EMUCORE) == 0){
        return KPI_CFGRET_RELOAD_KPI;
    }
    /*else if(wcscmp(cszKey, KEY_VOLUME_MEDNAFEN) == 0 || //音量はファイルを開き直してから反映させる
            wcscmp(cszKey, KEY_VOLUME_OSWAN) == 0 ||
            wcscmp(cszKey, KEY_VOLUME_IN_WSR) == 0){
        return KPI_CFGRET_NOTIFY;
    }*/
    else{
        return KPI_CFGRET_RELOAD_DATA;
    }
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbwsr_GetSettings(IKpiConfig *pConfig, kbwsr_Settings *pSettings)
{
    pSettings->nEmulationCore = pConfig->GetInt(SEC_GENERAL, KEY_EMUCORE, EMUCORE_DEFAULT);
    switch(pSettings->nEmulationCore){
        case 0 : case 1: case 2:break;
        pSettings->nEmulationCore = EMUCORE_DEFAULT;break;
    }
    wchar_t szMask[32];
    pConfig->GetStr(SEC_GENERAL, KEY_CHANNEL_MUTING, szMask, sizeof(szMask), CANNEL_MUTING_DEFAULT);
    pSettings->nChannnelMuting = GetChannelMutingFromMaskStr(szMask);
    const wchar_t *keys_SampleRate[] = { KEY_SAMPLERATE_MEDNAFEN, KEY_SAMPLERATE_OSWAN, KEY_SAMPLERATE_IN_WSR };
    const wchar_t *keys_Volume[] = { KEY_VOLUME_MEDNAFEN, KEY_VOLUME_OSWAN, KEY_VOLUME_IN_WSR };
    const int samplerates_default[] = { SAMPLERATE_DEFAULT, SAMPLERATE_OSWAN_DEFAULT, SAMPLERATE_DEFAULT };
    for(int i = 0; i < emulation_core_count; i++){
        pSettings->nSampleRate[i] = pConfig->GetInt(SEC_GENERAL, keys_SampleRate[i], samplerates_default[i]);
        pSettings->dVolume[i] = pConfig->GetFloat(SEC_GENERAL, keys_Volume[i], VOLUME_DEFAULT) / 100.0;
        if(i == 1){//oswan
            switch(pSettings->nSampleRate[i]){
                case  12000: case  24000: case  48000: case  96000:
                case -12000: case -24000: case -48000: case -96000: break;
                default: pSettings->nSampleRate[i] = SAMPLERATE_OSWAN_DEFAULT;break;
            }
        }
        else{
            if (pSettings->nSampleRate[i] == 0) {
                pSettings->nSampleRate[i] = samplerates_default[i];
            }
            else if(pSettings->nSampleRate[i] > 192000){
                pSettings->nSampleRate[i] = 192000;
            }
            else if(pSettings->nSampleRate[i] < -192000){
                pSettings->nSampleRate[i] = -192000;
            }
        }
        if(pSettings->dVolume[i] < 0.1){
            pSettings->dVolume[i] = 0.1;
        }
        else if(pSettings->dVolume[i] > 5.0){
            pSettings->dVolume[i] = 5.0;
        }
    }
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbwsr_GetCorePath(IKpiConfig *pConfig, wchar_t *pszPath, int nSize)
{//使用する Core DLL ファイル名(フルパス)を取得
    wchar_t szPath[MAX_PATH];
    GetModuleFileName(g_hKpi, szPath, _countof(szPath));
    wchar_t *last_path_delimiter = wcsrchr(szPath, L'\\');
    if (last_path_delimiter) {
        last_path_delimiter[1] = 0;
    }
    int nEmuCore = pConfig->GetInt(SEC_GENERAL, KEY_EMUCORE, EMUCORE_DEFAULT);
    switch (nEmuCore) {
    case 0: case 1: case 2:break;
    default: nEmuCore = EMUCORE_DEFAULT; break;
    };
    wcsncat_s(szPath, emulation_core_drive_name[nEmuCore], _TRUNCATE);
    wcsncpy_s(pszPath, nSize, szPath, _TRUNCATE);
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbwsr_GetCoreName(IKpiConfig* pConfig, wchar_t* pszName, int nSize)
{
    int nEmuCore = pConfig->GetInt(SEC_GENERAL, KEY_EMUCORE, EMUCORE_DEFAULT);
    switch (nEmuCore) {
    case 0: case 1: case 2:break;
    default: nEmuCore = EMUCORE_DEFAULT; break;
    };
    wcsncpy_s(pszName, nSize, emulation_core_name[nEmuCore], _TRUNCATE);
}
