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

static const wchar_t SEC_GENERAL[] = L"General";
static const wchar_t SEC_SUPPORTEXT[] = L"SupportExt";

//SupportExt
static const wchar_t KEY_HIGH_PRIORITY[] = L"HighPriority";
static const wchar_t KEY_ENABLE_AY[] =  L"EnableAY";
static const wchar_t KEY_ENABLE_GBS[] = L"EnableGBS";
static const wchar_t KEY_ENABLE_GYM[] = L"EnableGYM";
static const wchar_t KEY_ENABLE_HES[] = L"EnableHES";
static const wchar_t KEY_ENABLE_KSS[] = L"EnableKSS";
static const wchar_t KEY_ENABLE_NSF[] = L"EnableNSF";
static const wchar_t KEY_ENABLE_SAP[] = L"EnableSAP";
static const wchar_t KEY_ENABLE_SPC[] = L"EnableSPC";
static const wchar_t KEY_ENABLE_VGM[] = L"EnableVGM";

static const bool HIGH_PRIORITY_DEFAULT = false;
static const wchar_t SZ_HIGH_PRIORITY_DEFAULT[] = L"0";
static const bool ENABLE_AY_DEFAULT = true;
static const wchar_t SZ_ENABLE_AY_DEFAULT[] = L"1";
static const bool ENABLE_GBS_DEFAULT = true;
static const wchar_t SZ_ENABLE_GBS_DEFAULT[] = L"1";
static const bool ENABLE_GYM_DEFAULT = false;
static const wchar_t SZ_ENABLE_GYM_DEFAULT[] = L"0";
static const bool ENABLE_HES_DEFAULT = false;
static const wchar_t SZ_ENABLE_KSS_DEFAULT[] = L"0";
static const bool ENABLE_KSS_DEFAULT = false;
static const wchar_t SZ_ENABLE_HES_DEFAULT[] = L"0";
static const bool ENABLE_NSF_DEFAULT = false;
static const wchar_t SZ_ENABLE_NSF_DEFAULT[] = L"0";
static const bool ENABLE_SAP_DEFAULT = false;
static const wchar_t SZ_ENABLE_SAP_DEFAULT[] = L"0";
static const bool ENABLE_SPC_DEFAULT = false;
static const wchar_t SZ_ENABLE_SPC_DEFAULT[] = L"0";
static const bool ENABLE_VGM_DEFAULT = false;
static const wchar_t SZ_ENABLE_VGM_DEFAULT[] = L"0";

//General
static const wchar_t KEY_SAMPLERATE[] = L"SampleRate";
static const wchar_t KEY_ENABLE_ACCURACY[] = L"EnableAccuracy";
static const wchar_t KEY_STEREO_DEPTH[] = L"StereoDepth";
static const wchar_t KEY_TREBLE[] = L"Treble";
static const wchar_t KEY_BASS[] = L"Bass";

static const DWORD SAMPLERATE_DEFAULT = 44100;
static const wchar_t SZ_SAMPLERATE_DEFAULT[] = L"44100";
static const bool ENABLE_ACCURACY_DEFAULT = true;
static const wchar_t SZ_ENABLE_ACCURACY_DEFAULT[] = L"1";
static const double STEREO_DEPTH_DEFAULT = 0.0;
static const wchar_t SZ_STEREO_DEPTH_DEFAULT[] = L"0.0";
static const double TREBLE_DEFAULT = 0.0;
static const wchar_t SZ_TREBLE_DEFAULT[] = L"0.0";
static const double BASS_DEFAULT = 90.0;
static const wchar_t SZ_BASS_DEFAULT[] = L"90.0";

extern HINSTANCE g_hKpi;
/////////////////////////////////////////////////////////////////////////////
BOOL __fastcall kbgme_EnumConfig(IKpiConfigEnumerator *pEnumerator)
{
    static const KPI_CFG_SECTION sec[] = {
        {SEC_GENERAL, NULL, L"kbgme.kpi に関する一般的な設定です。"},
        {SEC_SUPPORTEXT, L"対応拡張子", L"対応拡張子に関する設定です。"},
        {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_SAMPLERATE, L"再生周波数",
            SZ_SAMPLERATE_DEFAULT, NULL, NULL, //default/min/max
            L"44100\t48000\t96000\t192000\t-44100\t-48000\t-96000\t-192000", //list
            L"44100Hz\t48000Hz\t96000Hz\t192000Hz\t"
            L"44100Hz(要求を無視)\t48000Hz(要求を無視)\t96000Hz(要求を無視)\t192000Hz(要求を無視)", //listdesc
            L"本体からの要求が 0 の場合の再生周波数です。\r\n"
            L"\r\n"
            L"負の値を指定すると本体からの要求を無視します。\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_ENABLE_ACCURACY, NULL,
            SZ_ENABLE_ACCURACY_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にするとエミュレーションの精度が向上します。\r\n"
            L"と思うのですが実際の効果は不明です。\r\n"
            L"SPC でのみ有効なようです。\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_FLOAT, SEC_GENERAL, KEY_STEREO_DEPTH, NULL,
            SZ_STEREO_DEPTH_DEFAULT, L"0.0", L"1.0", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"0.0～1.0 の範囲で指定します。\r\n"
            L"GYM, SPC, Sega Genesis VGM では無効です。\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_FLOAT, SEC_GENERAL, KEY_TREBLE, NULL,
            SZ_TREBLE_DEFAULT, L"-50.0", L"5.0", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"-50.0～5.0 の範囲で指定します。\r\n"
            L"-50.0 = muffled, 0 = flat, +5.0 = extra-crisp\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_FLOAT, SEC_GENERAL, KEY_BASS, NULL,
            SZ_BASS_DEFAULT, L"1.0", L"16000", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"1.0～16000 の範囲で指定します。\r\n"
            L"1 = full bass, 90 = average, 16000 = almost no bass\r\n"
            L""            
            },//help
//SupportExt
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_HIGH_PRIORITY, NULL,
            SZ_HIGH_PRIORITY_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると本プラグインが他のプラグインより優先的に使用されるようにします。\r\n"
            L"通常は false にして下さい。\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_AY, NULL,
            SZ_ENABLE_AY_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .ay を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L"\r\n"
            L".ay は本プラグインの主要な対応拡張子です。通常は true にします。\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_GBS, NULL,
            SZ_ENABLE_GBS_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .gbs を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_GBS, NULL,
            SZ_ENABLE_GBS_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .gbs を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_GYM, NULL,
            SZ_ENABLE_GYM_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .gym を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_HES, NULL,
            SZ_ENABLE_HES_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .hes を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_KSS, NULL,
            SZ_ENABLE_KSS_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .kss を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_NSF, NULL,
            SZ_ENABLE_NSF_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .nsf/.nsfe を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_SAP, NULL,
            SZ_ENABLE_SAP_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .sap を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L""            
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_SPC, NULL,
            SZ_ENABLE_SAP_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .spc を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_SUPPORTEXT, KEY_ENABLE_VGM, NULL,
            SZ_ENABLE_VGM_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"true にすると対応拡張子に .vgm/.vgz を追加します。\r\n"
            L"本プラグインより使用優先度が高いプラグインがあると true にしても再生出来ません。\r\n"
            L"その場合はそちらのプラグインの無効化も必要となります。\r\n"
            L"(プラグイン自体の無効化 or 対応拡張子の無効化)\r\n"
            L"\r\n"
            L"本プラグインでは再生出来ない(音が鳴らない) VGM データが多いようです。\r\n"
            L""            
            },//help
       {NULL, 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 kbgme_ApplyConfig(IKpiConfig *pConfig,
                               const wchar_t *cszSection,//セクション名
                               const wchar_t *cszKey,    //キー名
                               INT64  nValue, //変更後の値(BOOL/INT の場合)
                               double dValue, //変更後の値(FLOAT の場合)
                               const wchar_t *cszValue)////変更後の値(文字列)
{
    pConfig->SetStr(cszSection, cszKey, cszValue);
    if(wcscmp(cszSection, SEC_SUPPORTEXT) == 0){
        return KPI_CFGRET_RELOAD_KPI;
    }
    if(wcscmp(cszSection, SEC_GENERAL) == 0){
        return KPI_CFGRET_RELOAD_DATA;
    }
/*
    KPI_CFGRET_OK,          //設定は即座に反映される
    KPI_CFGRET_NOTIFY,      //デコーダインスタンス等への変更通知が必要(IKpiXXX::Update()が呼ばれたら反映)
    KPI_CFGRET_RELOAD_DATA, //データの再ロードが必要(次にIKpiXXXModule::Open()が呼ばれた分から反映)
    KPI_CFGRET_RELOAD_KPI,  //プラグインの再ロードが必要
    KPI_CFGRET_REBOOT_PLAYER,//プレイヤー本体の再起動が必要(特別な事情がない限り
                             //RELOAD_KPI で足りる筈だが)
    KPI_CFGRET_REBOOT_OS,    //OS の再起動が必要
    KPI_CFGRET_UNKNOWN = (DWORD)-1
                             //不明(外部プログラムを呼び出す等、設定関係の依存
                             //コードの詳細が分からない等)
*/
    return KPI_CFGRET_OK;
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbgme_GetSettings(IKpiConfig *pConfig, kbgme_Settings *pSettings)
{
    pSettings->nSampleRate = pConfig->GetInt(SEC_GENERAL, KEY_SAMPLERATE, SAMPLERATE_DEFAULT);
    //範囲チェック
    switch(pSettings->nSampleRate){
    case 44100: case 48000: case 96000: case 192000:
    case -44100: case -48000: case -96000: case -192000:break;
    default: pSettings->nSampleRate = 44100;break;
    }
    pSettings->bEnableAccuracy = pConfig->GetInt(SEC_GENERAL, KEY_ENABLE_ACCURACY, ENABLE_ACCURACY_DEFAULT)!=0;
    pSettings->dStereoDepth = pConfig->GetFloat(SEC_GENERAL, KEY_STEREO_DEPTH, STEREO_DEPTH_DEFAULT);
    if(pSettings->dStereoDepth < 0.0){
        pSettings->dStereoDepth = 0.0;
    }
    else if(pSettings->dStereoDepth > 1.0){
        pSettings->dStereoDepth = 1.0;
    }
    pSettings->dTreble = pConfig->GetFloat(SEC_GENERAL, KEY_TREBLE,TREBLE_DEFAULT);
    if(pSettings->dTreble < -50.0){
        pSettings->dTreble = -50.0;
    }
    else if(pSettings->dTreble > 5.0){
        pSettings->dTreble = 5.0;
    }
    pSettings->dBass = pConfig->GetFloat(SEC_GENERAL, KEY_BASS, BASS_DEFAULT);
    if(pSettings->dBass < 1.0){
        pSettings->dBass = 1.0;
    }
    else if(pSettings->dBass > 16000){
        pSettings->dBass = 16000;
    }
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbgme_GetSupportExts(IKpiConfig *pConfig, 
                                     wchar_t **ppszSupportExts, 
                                     bool *pbHighPriority)
{//ppszSupportExts の解放は呼び出し側で行う
    static const wchar_t cszExts[] = L"";
    static const struct{
        const wchar_t *cszKey;
        const wchar_t *cszExts;
        bool bDefault;
    }s[] = {
        {KEY_ENABLE_AY,  L".ay",  ENABLE_AY_DEFAULT},
        {KEY_ENABLE_GBS, L".gbs", ENABLE_GBS_DEFAULT},
        {KEY_ENABLE_GYM, L".gym", ENABLE_GYM_DEFAULT},
        {KEY_ENABLE_HES, L".hes", ENABLE_HES_DEFAULT},
        {KEY_ENABLE_KSS, L".kss", ENABLE_KSS_DEFAULT},
        {KEY_ENABLE_NSF, L".nsf/.nsfe", ENABLE_NSF_DEFAULT},
        {KEY_ENABLE_SAP, L".sap", ENABLE_SAP_DEFAULT},
        {KEY_ENABLE_SPC, L".spc", ENABLE_SPC_DEFAULT},
        {KEY_ENABLE_VGM, L".vgm/.vgz", ENABLE_VGM_DEFAULT},
        {NULL, NULL, false}
    };
    wchar_t *ret = _wcsdup(cszExts);
    for(int i = 0; s[i].cszKey; i++){
        bool bEnable = pConfig->GetInt(SEC_SUPPORTEXT, s[i].cszKey, s[i].bDefault) != 0;
        if(!bEnable)continue;
        int len = wcslen(ret) + wcslen(s[i].cszExts) + 2;// L'/' + '\0'
        ret = (wchar_t*)realloc(ret, len * sizeof(wchar_t));
        if(ret[0]){
            wcsncat_s(ret, len, L"/", _TRUNCATE);
        }
        wcsncat_s(ret, len , s[i].cszExts, _TRUNCATE);
    }
    *ppszSupportExts = ret;
    *pbHighPriority = pConfig->GetInt(SEC_SUPPORTEXT, KEY_HIGH_PRIORITY, HIGH_PRIORITY_DEFAULT) != 0;
}
