#include <windows.h>
#include <wchar.h>
#include "kpi.h"
#include "kbfpd_config.h"

#define SEC_GENERAL    L"General"
#define KEY_FPVPATH    L"FpvPath"
#define KEY_VIARUNKPI  L"ViaRunkpi"
#define VIARUNKPI_DEFAULT      1
#define SZ_VIARUNKPI_DEFAULT L"1"
#define KEY_SAMPLERATE L"SampleRate"
#define SAMPLERATE_DEFAULT      0
#define SZ_SAMPLERATE_DEFAULT L"0"
#define KEY_STEREO     L"Stereo"
#define STEREO_DEFAULT      1
#define SZ_STEREO_DEFAULT L"1"
//#define KEY_BIT        L"Bit" //8bit 再生は出来ない?
//#define BIT_DEFAULT      1
//#define SZ_BIT_DEFAULT L"1"
#define KEY_EFFECT    L"Effect"
#define EFFECT_DEFAULT 2
#define SZ_EFFECT_DEFAULT L"2"
#define KEY_SE        L"SE"
#define SE_DEFAULT      2
#define SZ_SE_DEFAULT L"2"

#define KEY_BPM      L"BPM"
#define BPM_DEFAULT      0
#define SZ_BPM_DEFAULT L"0"
#define BPM_MIN       -50
#define SZ_BPM_MIN  L"-50"
#define BPM_MAX        50
#define SZ_BPM_MAX  L"50"
#define KEY_TP      L"TP"
#define TP_DEFAULT      0
#define SZ_TP_DEFAULT L"0"
#define TP_MIN      -12
#define SZ_TP_MIN   L"-12"
#define TP_MAX        12
#define SZ_TP_MAX   L"12"

#define SEC_TEMPORARY   L"Temporary"
#define KEY_RESET L"Reset"
#define RESET_DEFAULT      1
#define SZ_RESET_DEFAULT L"1"
#define KEY_LASTUPDATED L"LastUpdated"

static const int op_s_default = 0;
static const int op_v_default = 2;
static const int op_e_default = 2;
static const int op_f_default = 5;
static const int op_l_default = 2;
static const int op_b_default = 16;
static const int op_se_default = 2;

//////////////////////////////////////////////////////////////////////////////
BOOL __fastcall kbfpd_EnumConfig(IKpiConfig *pConfig, IKpiConfigEnumerator *pEnumerator)
{
    static const KPI_CFG_SECTION sec[] = {
        {SEC_GENERAL, NULL, L"kbfpd.kpi に関する一般的な設定です。"
                            L"FPV 検索フォルダ以外の設定を変更する必要はほぼありません。"},
        {SEC_TEMPORARY, L"一時的な設定", 
            L"色々設定を変えて見ると面白いかもしれませんが、通常、あまり常用すべきではないため、"
            L"変更から 24 時間経過したら初期値に戻します。"
            L"初期値に戻さないように設定することも出来ます。"},
        {NULL, NULL, NULL}
    };
    static const KPI_CFG_KEY key[] = {
        {KPI_CFG_TYPE_FOLDER, SEC_GENERAL, KEY_FPVPATH, L"FPV 検索フォルダ", 
            NULL, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"FPV ファイルの検索フォルダを指定します。FPD98 とは異なり、"
            L"ワイルドカード(*.* 等)の指定は不要です。サブフォルダも検索します。"
            L"階層の深さに制限はありません。\r\n"
            L"\r\n"
            L"xxxx is not found\r\n"
            L"Please Check PCM data Directory\r\n"
            L"\r\n"
            L"と表示されて再生出来ない場合は、再生に必要な FPV ファイルが曲データと同じフォルダに置いてありません。"
            L"ここで指定したフォルダか、曲データと同じフォルダに必要な FPV ファイルを置く必要があります。"
            L"必要な FPV ファイルが複数の曲から参照される場合は、ここで指定したフォルダに置くことでディスク容量を"
            L"節約することが出来ます。"}, //help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_VIARUNKPI, L"常に別プロセスで使用", 
            SZ_VIARUNKPI_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"true にすると kbfpd.kpi をプレイヤー本体とは別プロセスで使用されるようにします。"
            L"kbfpd.kpi は動作が不安定で頻繁に強制終了・フリーズします。別プロセスで使用すると"
            L"本体が巻き込まれることが少なくなりますが、パフォーマンスが低下します。\r\n"
            L"パフォーマンスより安定性を考え、既定値は true としています。\r\n"
            L"本体が 64bit 版の場合はこの設定は無視されます。(常に別プロセスで使用されます)\r\n"}, //help
        {KPI_CFG_TYPE_INT, SEC_GENERAL,  KEY_SAMPLERATE, L"再生周波数",
            SZ_SAMPLERATE_DEFAULT, NULL, NULL, //default/min/max
            L"0\t1\t2\t3\t4\t", //list
            L"44100\t33075\t22050\t16538\t11025", //listdesc
            L"0: 44100Hz\r\n"
            L"1: 33075Hz\r\n"
            L"2: 22050Hz\r\n"
            L"3: 16538Hz\r\n"
            L"4: 11025Hz\r\n\r\n"
            L"44100Hz 未満にすると CPU 負荷が軽減しますが、音質は劣化します。"},
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_STEREO, L"ステレオ",
            SZ_STEREO_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"false にするとモノラルで再生するので CPU 負荷が軽減しますが、音質は劣化します。"},
        /* 
        //8bit 再生はおそらく FPD98.DLL がバグっている
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_BIT, L"16bit",
            SZ_STEREO_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"false にすると 8bit で再生するので CPU 負荷が軽減しますが、音質は劣化します。"},
        */
        {KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_EFFECT, L"エフェクト",
            SZ_EFFECT_DEFAULT, NULL, NULL, //default/min/max
            L"0\t1\t2\t", //list
            L"なし\tリバーブ\tリバーブ・コーラス",
            L"0: なし\r\n"
            L"1: リバーブ\r\n"
            L"2: リバーブ・コーラス\r\n\r\n"
            L"2: リバーブ・コーラス以外を選択すると CPU 負荷が軽減しますが、音質は劣化します。"},//help
        {KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_SE, L"システムエフェクト",
            SZ_SE_DEFAULT, NULL, NULL, //default/min/max
            L"0\t1\t2\t", //list
            L"なし\tリバーブ\tリバーブ・コーラス",
            L"0: なし\r\n"
            L"1: リバーブ\r\n"
            L"2: リバーブ・コーラス\r\n\r\n"
            L"2: リバーブ・コーラス以外を選択すると CPU 負荷が軽減しますが、音質は劣化します。"},//help
        {KPI_CFG_TYPE_BOOL, SEC_TEMPORARY, KEY_RESET, L"24時間後に初期値に戻す",
            SZ_RESET_DEFAULT, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"Temporary セクションの設定は常用するべきではありません。\r\n"
            L"true にすると設定を更新してから 24 時間後に初期値に戻します。"
            L"初期値に戻したくない場合は false にします。"},//help
        {KPI_CFG_TYPE_INT, SEC_TEMPORARY, KEY_BPM, L"テンポ",
            SZ_BPM_DEFAULT, SZ_BPM_MIN, SZ_BPM_MAX, //default/min/max
            L"-50\t-40\t-30\t-20\t-10\t0\t10\t20\t30\t40\t50", //list
            L"-50\t-40\t-30\t-20\t-10\t0\t+10\t+20\t+30\t+40\t+50", //listdesc
            L"プレイヤー本体の再生速度変更機能よりも高音質低負荷ですが、再生位置の進み方が速度に連動せず、"
            L"曲の長さが変わってしまいます。\r\n"
            L"また、データによっては fpd98sub.dll 内部でゼロによる除算エラー発生して再生出来なくなって"
            L"しまうことがあります。プラグイン側で強引にエラー回避を行っていますが、失敗した場合は"
            L"プレイヤーが強制終了する可能性があります。「常に別プロセスで使用」を true にしておけば"
            L"本体が巻き込まれることが少なくなります。"},//help
        {KPI_CFG_TYPE_INT, SEC_TEMPORARY, KEY_TP, L"トランスポーズ",
            SZ_TP_DEFAULT, SZ_TP_MIN, SZ_TP_MAX, //default/min/max
            L"-12\t-9\t-6\t-5\t-4\t-3\t-2\t-1\t0\t1\t2\t3\t4\t5\t6\t9\t12", //list
            L"-12\t-9\t-6\t-5\t-4\t-3\t-2\t-1\t0\t+1\t+2\t+3\t+4\t+5\t+6\t+9\t+12", //listdesc
            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]);
    }
    kbfpd_CheckTemporary(pConfig);
    return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
DWORD __fastcall kbfpd_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(_wcsicmp(cszKey, KEY_VIARUNKPI) == 0){
        return KPI_CFGRET_RELOAD_KPI;
    }
    if(_wcsicmp(cszSection, SEC_TEMPORARY) == 0){
        UINT64 qwCurrentTime;
        //Temporary セクションが既定値(false)以外の場合は
        //最後に設定を更新してから 24 時間以上経過後に初期値に戻す
        SYSTEMTIME st;
        FILETIME ft;
        GetLocalTime(&st);
        SystemTimeToFileTime(&st, &ft);
        qwCurrentTime = *(UINT64*)&ft;
        pConfig->SetInt(SEC_TEMPORARY, KEY_LASTUPDATED, qwCurrentTime);
    }
    return KPI_CFGRET_RELOAD_DATA;//どの設定項目もファイルの再オープンが必要
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall kbfpd_CheckTemporary(IKpiConfig *pConfig)
{//最終更新時刻から 24 時間以上経過してたら Temporary セクションをリセットする
    static const wchar_t *cszTemporaryKeys[] = {KEY_BPM, KEY_TP, NULL};
    const UINT64 qwResetTime = 24ui64 * 60 * 60 * 1000 * 10000;
    BOOL bReset = pConfig->GetInt(SEC_TEMPORARY, KEY_RESET, RESET_DEFAULT) != 0;
    if(!bReset){
        return;
    }
    UINT64 qwLastUpdated = pConfig->GetInt(SEC_TEMPORARY, KEY_LASTUPDATED, 0);
    if(qwLastUpdated != 0){
        SYSTEMTIME st;
        FILETIME ft;
        GetLocalTime(&st);
        SystemTimeToFileTime(&st, &ft);
        UINT64 qwElapsed = *(UINT64*)&ft - qwLastUpdated;
        if(qwElapsed > qwResetTime){
            for(int i = 0; cszTemporaryKeys[i]; i++){
                pConfig->SetInt(SEC_TEMPORARY, cszTemporaryKeys[i], 0);
            }
            pConfig->SetInt(SEC_TEMPORARY, KEY_LASTUPDATED, 0);
        }
    }
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall kbfpd_GetSettings(IKpiConfig *pConfig, KbFpd_Settings *pSettings)
{
    pSettings->op_s = pConfig->GetInt(SEC_GENERAL, KEY_SAMPLERATE, SAMPLERATE_DEFAULT);
    pSettings->op_v = pConfig->GetInt(SEC_GENERAL, KEY_STEREO, STEREO_DEFAULT) != 0 + 1;
    pSettings->op_e = pConfig->GetInt(SEC_GENERAL, KEY_EFFECT, EFFECT_DEFAULT);
    pSettings->op_f = op_f_default;//pConfig->GetInt(SEC_GENERAL, L"op_f", op_f_default);
    pSettings->op_l = op_l_default;//pConfig->GetInt(SEC_GENERAL, L"op_l", op_l_default);
    pSettings->op_b = op_b_default;//(pConfig->GetInt(SEC_GENERAL, KEY_BIT, BIT_DEFAULT) != 0 + 1) * 8;//おそらく 8bit 再生はバグっている
    pSettings->op_se = pConfig->GetInt(SEC_GENERAL, KEY_SE, SE_DEFAULT);
    pSettings->bpm = pConfig->GetInt(SEC_TEMPORARY, KEY_BPM, BPM_DEFAULT);
    pSettings->tp = pConfig->GetInt(SEC_TEMPORARY, KEY_TP, TP_DEFAULT);

    if(pSettings->op_s < 0 || pSettings->op_s > 4){
        pSettings->op_s = op_s_default;
    }
    if(pSettings->op_v < 1 || pSettings->op_v > 2){
        pSettings->op_v = op_v_default;
    }
    if(pSettings->op_e < 0 || pSettings->op_e > 2){
        pSettings->op_e = op_e_default;
    }
    if(pSettings->op_f < 0 || pSettings->op_f > 4){
        pSettings->op_f = op_f_default;
    }
    if(pSettings->op_l < 1 || pSettings->op_l > 36){
        pSettings->op_l = op_l_default;
    }
    if((pSettings->op_b) != 8 && (pSettings->op_b != 16)){
        pSettings->op_b = op_b_default;
    }
    if(pSettings->op_se < 0 || pSettings->op_se > 2){
        pSettings->op_se = op_se_default;
    }
    if(pSettings->bpm < BPM_MIN)pSettings->bpm = BPM_MIN;
    if(pSettings->bpm > BPM_MAX)pSettings->bpm = BPM_MAX;
    if(pSettings->tp < TP_MIN)pSettings->tp = TP_MIN;
    if(pSettings->tp > TP_MAX)pSettings->tp = TP_MAX;
}
//////////////////////////////////////////////////////////////////////////////
bool __fastcall kbfpd_GetViaRunkpi(IKpiConfig *pConfig)
{
    return pConfig->GetInt(SEC_GENERAL, KEY_VIARUNKPI, VIARUNKPI_DEFAULT) != 0;
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall kbfpd_GetFpvPath(IKpiConfig *pConfig, wchar_t *pszPath, int nSize)//nSize は文字数単位
{
    nSize *= sizeof(wchar_t);//IKpiConfig に渡すサイズはバッファサイズ
    pConfig->GetStr(SEC_GENERAL, KEY_FPVPATH, pszPath, nSize, L"");
}
//////////////////////////////////////////////////////////////////////////////
