#include <windows.h>
#include <wchar.h>

#include "kpi.h"
#include "kbfmpmd_config.h"
#include "kbfmp_config.h"

//セクション名
static const wchar_t SEC_VOLUME[] = L"VolumeFMP";
static const wchar_t SEC_PARTMASK[] = L"PartMaskFMP";

//Volume セクション
//WinFMP.dll での音量指定は最小値は全て0 だが最大値は設定ごとに異なる
static const int     VOL_MIN = 0;
static const wchar_t SZ_VOL_MIN[] = L"0";
//FM
static const wchar_t KEY_VOL_FM[] = L"FM";
static const wchar_t VOL_FM_DEFAULT = 0;
static const wchar_t SZ_VOL_FM_DEFAULT[] = L"0";
static const int     VOL_MAX_FM = 99;
static const wchar_t SZ_VOL_MAX_FM[] = L"99";
//SSG
static const wchar_t KEY_VOL_SSG[] = L"SSG";
static const wchar_t VOL_SSG_DEFAULT = 0;
static const wchar_t SZ_VOL_SSG_DEFAULT[] = L"0";
static const int     VOL_MAX_SSG = 15;
static const wchar_t SZ_VOL_MAX_SSG[] = L"15";
//Rhythm
static const wchar_t KEY_VOL_RHYTHM[] = L"Rhythm";
static const wchar_t VOL_RHYTHM_DEFAULT = 0;
static const wchar_t SZ_VOL_RHYTHM_DEFAULT[] = L"0";
static const int     VOL_MAX_RHYTHM = 63;
static const wchar_t SZ_VOL_MAX_RHYTHM[] = L"63";
//ADPCM
static const wchar_t KEY_VOL_ADPCM[] = L"ADPCM";
static const wchar_t VOL_ADPCM_DEFAULT = 0;
static const wchar_t SZ_VOL_ADPCM_DEFAULT[] = L"0";
static const int     VOL_MAX_ADPCM = 99;
static const wchar_t SZ_VOL_MAX_ADPCM[] = L"99";
//PPZ
static const wchar_t KEY_VOL_PPZ[] = L"PPZ";
static const wchar_t VOL_PPZ_DEFAULT = 0;
static const wchar_t SZ_VOL_PPZ_DEFAULT[] = L"0";
static const int     VOL_MAX_PPZ = 0;
static const wchar_t SZ_VOL_MAX_PPZ[] = L"0";

//PartMask
//FM
static const wchar_t KEY_PARTMASK_FM[] = L"PartMaskFM";
static const wchar_t PARTMASK_FM_DEFAULT[] = L"000000";
static const int PARTMASK_FM_DIGITS = 6;
//FM3Ex
static const wchar_t KEY_PARTMASK_FM3EX[] = L"PartMaskFM3Ex";
static const wchar_t PARTMASK_FM3EX_DEFAULT[] = L"000";
static const int PARTMASK_FM3EX_DIGITS = 3;
//SSG
static const wchar_t KEY_PARTMASK_SSG[] = L"PartMaskSSG";
static const wchar_t PARTMASK_SSG_DEFAULT[] = L"000";
static const int PARTMASK_SSG_DIGITS = 3;
//Rhythm
static const wchar_t KEY_PARTMASK_RHYTHM[] = L"PartMaskRhythm";
static const wchar_t PARTMASK_RHYTHM_DEFAULT[] = L"000000";
static const int PARTMASK_RHYTHM_DIGITS = 6;
//ADPCM
static const wchar_t KEY_PARTMASK_ADPCM[] = L"PartMaskADPCM";
static const wchar_t PARTMASK_ADPCM_DEFAULT[] = L"0";
static const int PARTMASK_ADPCM_DIGITS = 1;
//PPZ(WinFMP.dll では設定不可)
static const wchar_t KEY_PARTMASK_PPZ[] = L"PartMaskPPZ";
static const wchar_t PARTMASK_PPZ_DEFAULT[] = L"00000000";
static const int PARTMASK_PPZ_DIGITS = 8;

static const wchar_t KEY_REVERSE[] = L"Reverse";
static const wchar_t KEY_RESET[] = L"Reset";

static const PM_DIGITS pm_digits[] = {
    {KEY_PARTMASK_FM, PARTMASK_FM_DIGITS},
    {KEY_PARTMASK_FM3EX, PARTMASK_FM3EX_DIGITS},
    {KEY_PARTMASK_SSG, PARTMASK_SSG_DIGITS},
    {KEY_PARTMASK_RHYTHM, PARTMASK_RHYTHM_DIGITS},
    {KEY_PARTMASK_ADPCM, PARTMASK_ADPCM_DIGITS},
    {KEY_PARTMASK_PPZ, PARTMASK_PPZ_DIGITS},
    {NULL, NULL}
};

//////////////////////////////////////////////////////////////////////////////
BOOL __fastcall kbfmp_EnumConfig(IKpiConfig* pConfig, IKpiConfigEnumerator* pEnumerator)
{
    kbfmpmd_EnumConfig(pConfig, pEnumerator, false);//false: FMP として列挙
    static const KPI_CFG_SECTION sec[] = {//static にしない
        {SEC_VOLUME, L"Volume",
            L"FM, SSG, Rhythm, ADPCM, PPZ の各パートの音量調整に関する設定です。\r\n"
            L"kbfmp.kpi と kbpmd.kpi で設定を区別しています。"},
        {SEC_PARTMASK, L"PartMask",
            L"各パートのパートマスクに関する設定です。\r\n"
            L"kbfmp.kpi と kbpmd.kpi で設定を区別しています。"},
        {NULL, NULL, NULL}
    };
    const KPI_CFG_KEY key[] = {
        //   type, section, key, keydesc, default, min, max, list, listdesc, help
    //   type, section, key, keydesc, default, min, max, list, listdesc, help
        { KPI_CFG_TYPE_INT, SEC_VOLUME, KEY_VOL_FM, L"FM",
            SZ_VOL_FM_DEFAULT, SZ_VOL_MIN, SZ_VOL_MAX_FM, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"FM パートの音量を指定した値だけ下げます。\r\n"
            L"音量は 0 が最大、99 が最小となります。\r\n"
            L"" },
        { KPI_CFG_TYPE_INT, SEC_VOLUME, KEY_VOL_SSG, L"SSG",
            SZ_VOL_SSG_DEFAULT, SZ_VOL_MIN, SZ_VOL_MAX_SSG, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"SSG パートの音量を指定した値だけ下げます。\r\n"
            L"音量は 0 が最大、15 が最小となります。\r\n"
            L"" },
        { KPI_CFG_TYPE_INT, SEC_VOLUME, KEY_VOL_RHYTHM, L"Rhythm",
            SZ_VOL_RHYTHM_DEFAULT, SZ_VOL_MIN, SZ_VOL_MAX_RHYTHM, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"OPNA リズム音源の音量を指定した値だけ下げます。\r\n"
            L"音量は 0 が最大、63 が最小となります。\r\n"
            L"" },
        { KPI_CFG_TYPE_INT, SEC_VOLUME, KEY_VOL_ADPCM, L"ADPCM",
            SZ_VOL_ADPCM_DEFAULT, SZ_VOL_MIN, SZ_VOL_MAX_ADPCM, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"ADPCM の音量を指定した値だけ下げます。\r\n"
            L"音量は 0 が最大、99 が最小となります。\r\n"
            L"" },
        { KPI_CFG_TYPE_INT, SEC_VOLUME, KEY_VOL_PPZ, L"PPZ",
            SZ_VOL_PPZ_DEFAULT, SZ_VOL_MIN, SZ_VOL_MAX_PPZ, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"現在のところ無効です。(WinFMP.dll SDK より)\r\n"
            L"PPZ8 パートの音量は SSG で設定できます。\r\n"
            L"" },
        { KPI_CFG_TYPE_STR, SEC_PARTMASK, KEY_PARTMASK_FM, L"FM",
            PARTMASK_FM_DEFAULT, NULL, L"16", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"FM1〜FM6\r\n"
            L"6桁の文字列でマスクをかけるチャンネルを 1 にします。\r\n"
            L"(例： 101000)\r\n"
            L"" },
        { KPI_CFG_TYPE_STR, SEC_PARTMASK, KEY_PARTMASK_FM3EX, L"FM3Ex",
            PARTMASK_FM3EX_DEFAULT, NULL, L"16", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"FMEx1〜FMEx3\r\n"
            L"3桁の文字列でマスクをかけるチャンネルを 1 にします。\r\n"
            L"(例： 010)\r\n"
            L"" },
        { KPI_CFG_TYPE_STR, SEC_PARTMASK, KEY_PARTMASK_SSG, L"SSG",
            PARTMASK_SSG_DEFAULT, NULL, L"16", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"SSG1〜SSG3\r\n"
            L"3桁の文字列でマスクをかけるチャンネルを 1 にします。\r\n"
            L"(例： 010)\r\n"
            L"" },
        { KPI_CFG_TYPE_STR, SEC_PARTMASK, KEY_PARTMASK_RHYTHM, L"Rhythm",
            PARTMASK_RHYTHM_DEFAULT, NULL, L"16", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"6桁の文字列でマスクをかけるリズムパートを指定します。\r\n"
            L"左の桁から順に\r\n"
            L"\r\n"
            L"バスドラム、スネアドラム、シンバル、ハイハット、タム、リムショット\r\n"
            L"\r\n"
            L"となります。\r\n"
            L"例えば 011010 の場合はスネアドラム、シンバル、タムにマスクをかけます。\r\n"
            L"" },
        { KPI_CFG_TYPE_STR, SEC_PARTMASK, KEY_PARTMASK_ADPCM, L"ADPCM",
            PARTMASK_ADPCM_DEFAULT, NULL, L"16", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"ADPCM にマスクをかける場合は 1 にします。\r\n"
            L"" },
        { KPI_CFG_TYPE_STR, SEC_PARTMASK, KEY_PARTMASK_PPZ, L"PPZ",
            PARTMASK_PPZ_DEFAULT, NULL, L"16", //default/min/max
            NULL, //list
            NULL, //listdesc
            L"PPZ1〜PPZ8\r\n"
            L"8桁の文字列でマスクをかけるチャンネルを 1 にします。\r\n"
            L"(例： 010110000)\r\n"
            L"\r\n"
            L"WinFMP.dll では設定出来ないようです。設定は無視されます。\r\n"
            L"" },

        { KPI_CFG_TYPE_BIN, SEC_PARTMASK, KEY_REVERSE, L"Reverse",
            NULL, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"パートマスクの設定を反転します。\r\n"
            L"" },

        { KPI_CFG_TYPE_BIN, SEC_PARTMASK, KEY_RESET, L"Reset",
            NULL, NULL, NULL, //default/min/max
            NULL, //list
            NULL, //listdesc
            L"パートマスクの設定をリセットします。\r\n"
            L"" },

        {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
    };
    int i;
    for (i = 0; sec[i].cszSection; i++) {
        pEnumerator->EnumSection(&sec[i]);
    }
    for (i = 0; key[i].cszSection; i++) {
        pEnumerator->EnumKey(&key[i]);
    }
    //CheckTemporary(pConfig);
    return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
DWORD __fastcall kbfmp_ApplyConfig(IKpiConfig* pConfig,
    const wchar_t* cszSection, //セクション名
    const wchar_t* cszKey, //キー名
    INT64  nValue, //変更後の値(BOOL/INT の場合)
    double dValue, //変更後の値(FLOAT の場合)
    const wchar_t* cszValue)//変更後の値(文字列)
{
    if (_wcsicmp(cszSection, SEC_VOLUME) == 0) {
        pConfig->SetStr(cszSection, cszKey, cszValue);
        return KPI_CFGRET_NOTIFY;
    }
    return kbfmpmd_ApplyConfig(pConfig, cszSection, cszKey, nValue, dValue, cszValue, SEC_PARTMASK, pm_digits);
}
//////////////////////////////////////////////////////////////////////////////
/*
struct KbFMP_Settings
{
    int nVolFM;
    int nVolSSG;
    int nVolRhythm;
    int nVolADPCM;
    int nVolPPZ;
    int nPartMask[22];
    int nPartMaskRhythm;
};
*/
void __fastcall kbfmp_GetSettings(IKpiConfig* pConfig, KbFMP_Settings* pSettings)
{
    //Volume
    pSettings->nVolFM = pConfig->GetInt(SEC_VOLUME, KEY_VOL_FM, VOL_FM_DEFAULT);
    if (pSettings->nVolFM < VOL_MIN) {
        pSettings->nVolFM = VOL_MIN;
    }
    else if (pSettings->nVolFM > VOL_MAX_FM) {
        pSettings->nVolFM = VOL_MAX_FM;
    }
    pSettings->nVolSSG = pConfig->GetInt(SEC_VOLUME, KEY_VOL_SSG, VOL_SSG_DEFAULT);
    if (pSettings->nVolSSG < VOL_MIN) {
        pSettings->nVolSSG = VOL_MIN;
    }
    else if (pSettings->nVolSSG > VOL_MAX_SSG) {
        pSettings->nVolSSG = VOL_MAX_SSG;
    }
    pSettings->nVolRhythm = pConfig->GetInt(SEC_VOLUME, KEY_VOL_RHYTHM, VOL_RHYTHM_DEFAULT);
    if (pSettings->nVolRhythm < VOL_MIN) {
        pSettings->nVolRhythm = VOL_MIN;
    }
    else if (pSettings->nVolRhythm > VOL_MAX_RHYTHM) {
        pSettings->nVolRhythm = VOL_MAX_RHYTHM;
    }
    pSettings->nVolADPCM = pConfig->GetInt(SEC_VOLUME, KEY_VOL_ADPCM, VOL_ADPCM_DEFAULT);
    if (pSettings->nVolADPCM < VOL_MIN) {
        pSettings->nVolADPCM = VOL_MIN;
    }
    else if (pSettings->nVolADPCM > VOL_MAX_ADPCM) {
        pSettings->nVolADPCM = VOL_MAX_ADPCM;
    }
    pSettings->nVolPPZ = pConfig->GetInt(SEC_VOLUME, KEY_VOL_PPZ, VOL_PPZ_DEFAULT);
    if (pSettings->nVolPPZ < VOL_MIN) {
        pSettings->nVolPPZ = VOL_MIN;
    }
    else if (pSettings->nVolPPZ > VOL_MAX_PPZ) {
        pSettings->nVolPPZ = VOL_MAX_PPZ;
    }
    //PartMask
/*
    WinFMP.dll, DLLInfop.txt より

    int fmp_maskon(bool rhythm_flag, int ah)
    function fmp_maskon(rhythm_flag : Boolean; ah : Integer) : Integer;
-------------------------------------------------------------------------------
    input
        rhythm_flag リズムパートかどうか？
        ah          マスクをかけるチャンネル
    output          下記の戻り値を参照
-------------------------------------------------------------------------------

    パートマスクをかけます。

    rhythm_flag = true の場合、
        リズム音源のパートを、以下の数値の和で指定します。
         1          バスドラム
         2          スネアドラム
         4          シンバル
         8          ハイハット
        16          タム
        32          リムショット

    rhythm_flag = false の場合、
         0〜 5      FM1 (A)〜FM3(C),FM4(G)〜FM6(I)
         6〜 8      FMEx1〜FMEx3
        18〜20      SSG1(D)〜SSG3(F)
        21          ADPCM(K)
*/
    wchar_t szMask[16];
    int i;
    ZeroMemory(pSettings->bPartMask, sizeof(pSettings->bPartMask));
    //0〜5 FM
    kbfmpmd_GetPartMaskStr(pConfig, SEC_PARTMASK, KEY_PARTMASK_FM, szMask, _countof(szMask), PARTMASK_FM_DIGITS);
    for (i = 0; i < 6; i++) {
        if (!szMask[i]) {
            break;
        }
        pSettings->bPartMask[i] = (szMask[i] == L'1');
    }
    //6〜8 FM3EX
    kbfmpmd_GetPartMaskStr(pConfig, SEC_PARTMASK, KEY_PARTMASK_FM3EX, szMask, _countof(szMask), PARTMASK_FM3EX_DIGITS);
    for (i = 0; i < 3; i++) {
        if (!szMask[i]) {
            break;
        }
        pSettings->bPartMask[i + 6] = (szMask[i] == L'1');
    }
    //9〜17 なし
    for(i = 9; i <= 17; i++)pSettings->bPartMask[i] = false;
    //18〜20 SSG
    kbfmpmd_GetPartMaskStr(pConfig, SEC_PARTMASK, KEY_PARTMASK_SSG, szMask, _countof(szMask), PARTMASK_SSG_DIGITS);
    for (i = 0; i < 3; i++) {
        if (!szMask[i]) {
            break;
        }
        pSettings->bPartMask[i + 18] = (szMask[i] == L'1');
    }
    //21 ADPCM
    kbfmpmd_GetPartMaskStr(pConfig, SEC_PARTMASK, KEY_PARTMASK_ADPCM, szMask, _countof(szMask), PARTMASK_ADPCM_DIGITS);
    pSettings->bPartMask[21] = (szMask[0] == L'1');
    // Rhythm
    pSettings->nPartMaskRhythm = 0;
    kbfmpmd_GetPartMaskStr(pConfig, SEC_PARTMASK, KEY_PARTMASK_RHYTHM, szMask, _countof(szMask), PARTMASK_RHYTHM_DIGITS);
    for (i = 0; i < 6; i++) {
        if (!szMask[i]) {
            break;
        }
        if(szMask[i] == L'1'){
            pSettings->nPartMaskRhythm |= (1 << i);
        }
    }
}
//////////////////////////////////////////////////////////////////////////////
