#include <Windows.h>
#include <wchar.h>

#include "kbGuru2SMF_config.h"
#include "kpi.h"

static const wchar_t SEC_GENERAL[] = L"General";
static const wchar_t KEY_IGNORE_DLLPATH[] = L"IgnoreDllPath";
static const wchar_t KEY_GURU2SMF4_PATH[] = L"Guru2SMF4Path";
static const wchar_t KEY_DLS_PATH[] = L"DlsPath";
static const wchar_t KEY_VOLUME[] = L"Volume";
static const wchar_t KEY_PLAYOPT_LOOP[] = L"playopt_Loop";
static const wchar_t KEY_PLAYOPT_SKIP_BEGINNING[] = L"playopt_SkipBeginning";
static const wchar_t KEY_PLAYOPT_SKIP_ENDING[] = L"playopt_SkipEnding";
static const wchar_t KEY_PLAYOPT_MASTERPITCH[] = L"playopt_MasterPitch";
static const wchar_t KEY_PLAYOPT_NORMALIZE[] = L"playopt_Normalize";
static const wchar_t KEY_PLAYOPT_EXCLUSIVE[] = L"playopt_Exclusive";
static const wchar_t KEY_PLAYOPT_KEYSHIFT[] = L"playopt_KeyShift";
static const wchar_t KEY_MASTERPITCH[] = L"MasterPitch";

static const wchar_t KEY_FADEIN[] = L"FadeIn";
static const wchar_t KEY_FADEOUT[] = L"FadeOut";

static const bool IGNORE_DLLPATH_DEFAULT = true;
static const wchar_t SZ_IGNORE_DLLPATH[] = L"1";

static const wchar_t GURU2SMF4_PATH_DEFAULT[] = L"";
static const wchar_t DLS_PATH_DEFAULT[] = L"";
static const wchar_t SZ_MAX_DLS_PATH_LEN[] = L"2048";//DLS のパスの文字列の長さ(複数指定可能なので大きめにする)

static const int VOLUME_DEFAULT = 127;
static const wchar_t SZ_VOLUME_DEFAULT[] = L"127";

static const bool PLAYOPT_LOOP_DEFAULT = true;
static const wchar_t SZ_PLAYOPT_LOOP_DEFAULT[] = L"1";

static const bool PLAYOPT_SKIP_BEGINNING_DEFAULT = false;
static const wchar_t SZ_PLAYOPT_SKIP_BEGINNING_DEFAULT[] = L"0";

static const bool PLAYOPT_SKIP_ENDING_DEFAULT = false;
static const wchar_t SZ_PLAYOPT_SKIP_ENDING_DEFAULT[] = L"0";

static const bool PLAYOPT_MASTERPITCH_DEFAULT = false;
static const wchar_t SZ_PLAYOPT_MASTERPITCH_DEFAULT[] = L"0";

static const bool PLAYOPT_NORMALIZE_DEFAULT = false;
static const wchar_t SZ_PLAYOPT_NORMALIZE_DEFAULT[] = L"0";

static const bool PLAYOPT_EXCLUSIVE_DEFAULT = true;
static const wchar_t SZ_PLAYOPT_EXCLUSIVE_DEFAULT[] = L"1";

static const bool PLAYOPT_KEYSHIFT_DEFAULT = 0;
static const wchar_t SZ_PLAYOPT_KEYSHIFT_DEFAULT[] = L"0";

static const int MASTERPITCH_DEFAULT = 0;
static const wchar_t SZ_MASTERPITCH_DEFAULT[] = L"0";

static const DWORD FADE_DEFAULT = 0;
static const wchar_t SZ_FADE_DEFAULT[] = L"0";
static const DWORD FADE_MAX = 30000;
static const wchar_t SZ_FADE_MAX[] = L"30000";

extern HINSTANCE g_hKpi;
static void GetDefaultDllPath(wchar_t *pszPath, int nSize)
{//プラグインと同じフォルダの GuruGuruSMF4.dll
    wchar_t szPath[MAX_PATH];
    GetModuleFileName(g_hKpi, szPath, _countof(szPath));
    wchar_t* path_delimiter = wcsrchr(szPath, L'\\');
    if (path_delimiter) {
        path_delimiter[1] = 0;
    }
    wcsncat_s(szPath, L"GuruGuruSMF4.dll", _TRUNCATE);
    wcsncpy_s(pszPath, nSize, szPath, _TRUNCATE);
}
//////////////////////////////////////////////////////////////////////////////
BOOL __fastcall kbguru2smf_EnumConfig(IKpiConfig* pConfig, IKpiConfigEnumerator* pEnumerator)
{
    KPI_CFG_KEY key[] = {
    //   type, section, key, keydesc, default, min, max, list, listdesc, help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_IGNORE_DLLPATH, L"同梱の GuruGuruSMF4.dll を使う",
            SZ_IGNORE_DLLPATH, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"true にするとプラグイン同梱の GuruGuruSMF4.dll を使用します。\r\n"
            L"false にすると「GuruGuruSMF4.dll のパス」で指定する DLL を使用します。\r\n"
            L""}, //help
        {KPI_CFG_TYPE_FILE, SEC_GENERAL, KEY_GURU2SMF4_PATH, L"GuruGuruSMF4.dll のパス",
            NULL, L"GuruGuruSMF4.dll(*.dll)\t*.dll", NULL, //default/min/max
            GURU2SMF4_PATH_DEFAULT, L"同梱の GuruGuruSMF4.dll", //list/listdesc
            L"GuruGuruSMF4.dll のパスを指定します。\r\n"
            L"「同梱の GuruGuruSMF4.dll を使う」が false のときに有効です。\r\n"
            L"未指定にすると本プラグイン同梱の GuruGuruSMF4.dll を使用します。\r\n"
            L"(「同梱の GuruGuruSMF4.dll を使う」が true のときと同じ動作となります)\r\n"
            L"\r\n"
            L"http://gurugurusmf.migmig.net/\r\n"
            L"\r\n"
            L"公式版は v4.0.8 以降の使用をお勧めします。\r\n"
            L""}, //help
        {KPI_CFG_TYPE_FILE, SEC_GENERAL, KEY_DLS_PATH, L"DLS のパス",
            NULL, L"DLS File(*.dls)\t*.dls", SZ_MAX_DLS_PATH_LEN, //default/min/max
            DLS_PATH_DEFAULT, NULL, //list/listdesc
            L"DLS のパスを指定します。\r\n"
            L"曲データと同じフォルダに DLS ファイルがあればそれも使用します。\r\n"
            L"\r\n"
            L"複数指定する場合は | で区切ります。\r\n"
            L"複数指定で2つ目以降をファイル名だけで指定すると1つ前と同じフォルダから検索します。\r\n"
            L"(C:\\DLS\\foo.dls|bar.dls は C:\\DLS\\foo.dls|C:\\DLS\\bar.dls と同じ)\r\n"
            L"\r\n"
            L"複数指定で音色が被る場合は先に記述した方が優先されます。\r\n"
            L"\r\n"
            L"音色が被る場合の優先順位は\r\n"
            L"曲データと同じフォルダ(見つかった順) => 本設定(記述順)\r\n"
            L"となります。\r\n"
            L""}, //help
         
        {KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_VOLUME, L"音量(0〜127)",
            SZ_VOLUME_DEFAULT, L"0", L"127", //default/min/max
            NULL, NULL, //list/listdesc
            L"音量を指定します。(最大127、最小0)\r\n"
            L"通常は最大値(127)のまま変更しないで下さい。\r\n"
            L"\r\n"
            L"他の MIDI 対応プラグインと比べて音量が大きいと感じる場合の調整用ですが、\r\n"
            L"音量設定はコントロールチェンジのチャンネル・ボリュームで実現しているので\r\n"
            L"チャンネルごとに128段階しか音量を設定することが出来ません。\r\n"
            L"最大値より下げると全体的に音量バランスが悪くなる場合があります。\r\n"
            L""}, //help
        
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_PLAYOPT_LOOP, L"非ループ曲のループ再生",
            SZ_PLAYOPT_LOOP_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"本体の再生モードが単曲リピートのとき、非ループ曲のループに\r\n"
            L"GuruGuruSMF のループ機能を使うかの設定です。\r\n"
            L"ループ曲とは CC111 を含む曲データのことです。\r\n"
            L"\r\n"
            L"true:  非ループ曲でもループ機能を使います。(GuruGuruSMF に任せる)\r\n"
            L"false: 非ループ曲ではループ機能を使いません。(本体に任せる)\r\n"
            L"ループ曲では常に GuruGuruSMF のループ機能を使います。\r\n"
            L"\r\n"
            L"true にした方が非ループ曲のループがスムーズになります。\r\n"
            L"true にすると非ループ曲の2周目以降が不自然になる場合は false にします。\r\n"
            L"\r\n"
            L"この設定を true にしたらループ再生になる訳ではないことに注意して下さい。\r\n"
            L"ループ再生したいときは本体の再生モードを単曲リピートにする必要があります。\r\n"
            L"本体の再生モードが単曲リピートでない場合、本設定は無視されます。\r\n"
            L""}, //help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_PLAYOPT_SKIP_BEGINNING, L"冒頭の空白をスキップ",
            SZ_PLAYOPT_SKIP_BEGINNING_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"曲データによっては再生が不自然になることがある為、あまりお勧めしません。\r\n"
            L"\r\n"
            L"GuruGuruSMF のヘルプより\r\n"
            L"\r\n"
            L"冒頭の空白をスキップします。\r\n"
            L"CC111 なしでループするときにも有効です。\r\n"
            L""}, //help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_PLAYOPT_SKIP_ENDING, L"末尾の空白をスキップ",
            SZ_PLAYOPT_SKIP_ENDING_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"GuruGuruSMF のヘルプより\r\n"
            L"\r\n"
            L"末尾の空白をスキップします。\r\n"
            L"ツクール向けのデータではそこも計算されていることが多いので通常は非推奨です。\r\n"
            L""}, //help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_PLAYOPT_MASTERPITCH, L"マスターピッチ",
            SZ_PLAYOPT_MASTERPITCH_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"GuruGuruSMF のヘルプより\r\n"
            L"\r\n"
            L"マスターピッチ機能を有効にします。\r\n"
            L"ピッチベンドが送信されているデータで正常再生されなくなったりします。\r\n"
            L"特殊効果が必要な時のみonにしてください。\r\n"
            L""}, //help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_PLAYOPT_NORMALIZE, L"ノーマライズ",
            SZ_PLAYOPT_NORMALIZE_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"GuruGuruSMF のヘルプより\r\n"
            L"\r\n"
            L"解析時にボリュームデータを調べて、最大値が127に満たない時に\r\n"
            L"127が最大になるように全体を持ち上げます。\r\n"
            L"音量が小さいデータに有効な場合があります。\r\n"
            L"また、音が割れるようになる場合もあります。\r\n"
            L""}, //help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_PLAYOPT_EXCLUSIVE, L"エクスクルーシブ",
            SZ_PLAYOPT_EXCLUSIVE_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"GuruGuruSMF のヘルプより\r\n"
            L"\r\n"
            L"データ中のエクスクルーシブを送信するようになります。\r\n"
            L"複数リズムパートを使っているデータなどに有効です。\r\n"
            L"ただし、これをonにしてもリセットメッセージと\r\n"
            L"マスターボリュームメッセージは送りません。\r\n"
            L"\r\n"
            L"プラグイン同梱の GuruGuruSMF4.dll ではマスターボリュームをコントロールチェンジの\r\n"
            L"チャンネル・ボリュームに置き換えて送信します。\r\n"
            L"その為、マスターボリュームを使用する MIDI データでは公式版と再生結果が変わります。\r\n"
            L""}, //help
        { KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_PLAYOPT_KEYSHIFT, L"キーシフト",
            SZ_PLAYOPT_KEYSHIFT_DEFAULT, L"-16", L"16", //default/min/max
            NULL, NULL, //list/listdesc
            L"-16〜16 の範囲で指定出来ます。\r\n"
            L"GuruGuruSMF4.dll の機能のテスト用です。通常は 0 にします。\r\n"
            L"\r\n"
            L"GuruGuruSMF のヘルプより\r\n"
            L"\r\n"
            L"半音単位で移調して再生します。\r\n"
            L"再生中に変えることはできません。\r\n"
            L"リズムパートはなるべく自動判定します。\r\n"
            L"" }, //help
        { KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_MASTERPITCH, L"ピッチ",
            SZ_MASTERPITCH_DEFAULT, L"-8192", L"8191", //default/min/max
            NULL, NULL, //list/listdesc
            L"-8192〜8192 の範囲で指定出来ます。\r\n"
            L"GuruGuruSMF4.dll の機能のテスト用です。通常は 0 にします。\r\n"
            L"\r\n"
            L"GuruGuruSMF のヘルプより\r\n"
            L"\r\n"
            L"再生中にピッチを動的に変化させます。\r\n"
            L"ピッチベンドにより実装されてます。\r\n"
            L"「マスターピッチ」が true の時に有効です。\r\n"
            L"\r\n"
            L"有効範囲は-8192〜8191で正負それぞれ1オクターブの範囲で変化します。\r\n"
            L"" }, //help
        { KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_FADEIN, L"フェードイン(ms)",
            SZ_FADE_DEFAULT, L"0", SZ_FADE_MAX, //default/min/max
            L"0\t1000\t2000\t3000\t4000\t5000\t6000\t7000\t8000\t9000\t10000", NULL, //list/listdesc
            L"演奏開始時に指定した時間をかけてフェードインします。\r\n"
            L"単位はミリ秒です。(5秒なら5000)\r\n"
            L"\r\n"
            L"GuruGuruSMF4.dll の機能のテスト用です。通常は 0 にします。\r\n"
            L"0 以外にすると本体のフェードイン機能と競合して不自然な動作になってしまいます。\r\n"
            L"" }, //help
        { KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_FADEOUT, L"フェードアウト(ms)",
            SZ_FADE_DEFAULT, L"0", SZ_FADE_MAX, //default/min/max
            L"0\t1000\t2000\t3000\t4000\t5000\t6000\t7000\t8000\t9000\t10000", NULL, //list/listdesc
            L"演奏停止時に指定した時間をかけてフェードアウトします。\r\n"
            L"0 の場合は直ちに演奏終了します。\r\n"
            L"単位はミリ秒です。(5秒なら5000)\r\n"
            L"\r\n"
            L"GuruGuruSMF4.dll の機能のテスト用です。通常は 0 にします。\r\n"
            L"0 以外にすると本体のフェードアウト機能と競合して不自然な動作になってしまいます。\r\n"
            L"" }, //help

        {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
    };
    for(int i = 0; key[i].cszSection; i++){
        pEnumerator->EnumKey(&key[i]);
    }
    return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
DWORD __fastcall kbguru2smf_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;
    //セクションごとに一通りの設定が終わったら最後に cszKey[0] == 0 として呼ばれる
    //このとき KPI_CFGRET_OK を返さないと KEY_VOLUME で KPI_CFGRET_NOTIFY を返した意味がなくなってしまう
    //我ながら分かりづらい仕様

    if(wcscmp(cszSection, SEC_GENERAL) == 0){
        if(wcscmp(cszKey, KEY_GURU2SMF4_PATH) == 0){
        //プラグインと同じフォルダの GuruGuruSMF4.dll の場合は未指定にする
        //(バージョンアップ時に古い版の DLL を参照してしまうので)
            wchar_t szPath[MAX_PATH];
            GetDefaultDllPath(szPath, _countof(szPath));
            if(_wcsicmp(szPath, cszValue) == 0){
                pConfig->SetStr(SEC_GENERAL, KEY_GURU2SMF4_PATH, L"");//未指定にする
            }
            else{
                pConfig->SetStr(SEC_GENERAL, KEY_GURU2SMF4_PATH, cszValue);
            }
            return KPI_CFGRET_RELOAD_KPI;
        }
        else{
            pConfig->SetStr(SEC_GENERAL, cszKey, cszValue);
            if(wcscmp(cszKey, KEY_VOLUME) == 0 ||
               wcscmp(cszKey, KEY_MASTERPITCH) == 0 ||
               wcscmp(cszKey, KEY_FADEOUT) == 0){
                return KPI_CFGRET_NOTIFY;
            }
            if (wcscmp(cszKey, KEY_IGNORE_DLLPATH) == 0) {
                return KPI_CFGRET_RELOAD_KPI;
            }
            return KPI_CFGRET_RELOAD_DATA;
        }
    }
    return KPI_CFGRET_OK;
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall kbguru2smf_GetSettings(IKpiConfig* pConfig, kbguru2smf_Settings* pSettings)
{
    pSettings->bIgnoreDllPath = pConfig->GetInt(SEC_GENERAL, KEY_IGNORE_DLLPATH, IGNORE_DLLPATH_DEFAULT) != 0;
    pConfig->GetStr(SEC_GENERAL, KEY_GURU2SMF4_PATH, pSettings->szDllPath, sizeof(pSettings->szDllPath), L"");
    if(pSettings->bIgnoreDllPath || !pSettings->szDllPath[0]){
    //プラグインと同じフォルダの GuruGuruSMF4.dll
        GetDefaultDllPath(pSettings->szDllPath, _countof(pSettings->szDllPath));
    }
    pConfig->GetStr(SEC_GENERAL, KEY_DLS_PATH, pSettings->szDlsPath, sizeof(pSettings->szDlsPath), L"");
    pSettings->nVolume = pConfig->GetInt(SEC_GENERAL, KEY_VOLUME, VOLUME_DEFAULT);
    if(pSettings->nVolume < 0){
        pSettings->nVolume = 0;
    }
    else if(pSettings->nVolume > 127){
        pSettings->nVolume = 127;
    }
    pSettings->bLoop = pConfig->GetInt(SEC_GENERAL, KEY_PLAYOPT_LOOP, PLAYOPT_LOOP_DEFAULT) != 0;
    pSettings->bSkipBeginning = pConfig->GetInt(SEC_GENERAL, KEY_PLAYOPT_SKIP_BEGINNING, PLAYOPT_SKIP_BEGINNING_DEFAULT) != 0;
    pSettings->bSkipEnding = pConfig->GetInt(SEC_GENERAL, KEY_PLAYOPT_SKIP_ENDING, PLAYOPT_SKIP_ENDING_DEFAULT) != 0;
    pSettings->bMasterPitch = pConfig->GetInt(SEC_GENERAL, KEY_PLAYOPT_MASTERPITCH, PLAYOPT_MASTERPITCH_DEFAULT) != 0;
    pSettings->bNormalize = pConfig->GetInt(SEC_GENERAL, KEY_PLAYOPT_NORMALIZE, PLAYOPT_NORMALIZE_DEFAULT) != 0;
    pSettings->bExclusive = pConfig->GetInt(SEC_GENERAL, KEY_PLAYOPT_EXCLUSIVE, PLAYOPT_EXCLUSIVE_DEFAULT) != 0;
    pSettings->nKeyShift = pConfig->GetInt(SEC_GENERAL, KEY_PLAYOPT_KEYSHIFT, PLAYOPT_KEYSHIFT_DEFAULT);
    if(pSettings->nKeyShift < -16){
        pSettings->nKeyShift = -16;
    }
    else if(pSettings->nKeyShift > 16){
        pSettings->nKeyShift = 16;
    }
    pSettings->nMasterPitch = pConfig->GetInt(SEC_GENERAL, KEY_MASTERPITCH, MASTERPITCH_DEFAULT);
    if (pSettings->nKeyShift < -8192) {
        pSettings->nKeyShift = -8192;
    }
    else if (pSettings->nMasterPitch > 8191) {
        pSettings->nMasterPitch = 8191;
    }
    pSettings->dwFadeIn = pConfig->GetInt(SEC_GENERAL, KEY_FADEIN, FADE_DEFAULT);
    if (pSettings->dwFadeIn > FADE_MAX) {
        pSettings->dwFadeIn = FADE_MAX;
    }
    pSettings->dwFadeOut = pConfig->GetInt(SEC_GENERAL, KEY_FADEOUT, FADE_DEFAULT);
    if(pSettings->dwFadeOut > FADE_MAX){
        pSettings->dwFadeOut = FADE_MAX;
    }
}
