#include "stdafx.h"
#include <windows.h>
#include <wchar.h>
#include "kbnsfplug_config.h"
#include "kbnsfplug_decoder.h"
#include "NSFDialogs.h"

//#define ENABLE_EASY_DLG //簡易設定(良く分からないので無効にする)

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

static const wchar_t KEY_RESET_CONFIG[] = L"ResetConfig";
static const wchar_t KEY_RESET_POS[] = L"ResetPos";

#ifdef ENABLE_EASY_DLG
static const wchar_t KEY_EASY[] = L"Easy";
#endif
static const wchar_t KEY_CONFIG[] = L"Config";
static const wchar_t KEY_MIXER[] = L"Mixer";
static const wchar_t KEY_MASK[] = L"Mask";
static const wchar_t KEY_PAN[] = L"Pan";
static const wchar_t KEY_TRACK[] = L"ShowTrack";
static const wchar_t KEY_MEMORY[] = L"ShowMemory";
static const wchar_t KEY_INFO[] = L"ShowInfo";

static const wchar_t KEY_INIDATA[] = L"IniData";

static const bool TRACK_DEFAULT = false;
static const wchar_t SZ_TRACK_DEFAULT[] = L"0";
static const bool MEMORY_DEFAULT = false;
static const wchar_t SZ_MEMORY_DEFAULT[] = L"0";
static const bool INFO_DEFAULT = false;
static const wchar_t SZ_INFO_DEFAULT[] = L"0";

static const wchar_t KEY_CONFIG_POS_X[] = L"ConfigPosX";
static const wchar_t KEY_CONFIG_POS_Y[] = L"ConfigPosY";
static const wchar_t KEY_MIXER_POS_X[] = L"MixerPosX";
static const wchar_t KEY_MIXER_POS_Y[] = L"MixerPosY";
static const wchar_t KEY_MASK_POS_X[] = L"MaskPosX";
static const wchar_t KEY_MASK_POS_Y[] = L"MaskPosY";
static const wchar_t KEY_PAN_POS_X[] = L"PanPosX";
static const wchar_t KEY_PAN_POS_Y[] = L"PanPosY";
static const wchar_t KEY_TRACK_POS_X[] = L"TrackPosX";
static const wchar_t KEY_TRACK_POS_Y[] = L"TrackPosY";
static const wchar_t KEY_TRACK_POS_W[] = L"TrackPosW";
static const wchar_t KEY_TRACK_POS_H[] = L"TrackPosH";
static const wchar_t KEY_MEMORY_POS_X[] = L"MemoryPosX";
static const wchar_t KEY_MEMORY_POS_Y[] = L"MemoryPosY";
static const wchar_t KEY_MEMORY_POS_W[] = L"MemoryPosW";
static const wchar_t KEY_MEMORY_POS_H[] = L"MemoryPosH";
static const wchar_t KEY_INFO_POS_X[] = L"InfoPosX";
static const wchar_t KEY_INFO_POS_Y[] = L"InfoPosY";
static const wchar_t KEY_INFO_POS_W[] = L"InfoPosW";
static const wchar_t KEY_INFO_POS_H[] = L"InfoPosH";


static const int POS_DEFAULT = -32768;

static void OpenDialog(IKpiConfig *pConfig, const wchar_t *cszKeyName)
{
    static const struct{
        const wchar_t *key;
        int id;
    }key_ids[] = {
#ifdef ENABLE_EASY_DLG
        {KEY_EASY,   NSFplug_UI::DLG_EASY},
#endif
        {KEY_CONFIG, NSFplug_UI::DLG_CONFIG},
        {KEY_MIXER,  NSFplug_UI::DLG_MIXER},
        {KEY_MASK,   NSFplug_UI::DLG_MASK},
        //{KEY_TRACK,  NSFplug_UI::DLG_TRACK},  //ここでは表示しない(再生中のみ表示)
        //{KEY_MEMORY, NSFplug_UI::DLG_MEMORY}, //ここでは表示しない(再生中のみ表示)
        //{KEY_INFO, NSFplug_UI::DLG_INFO}, //ここでは表示しない(再生中のみ表示)
        {KEY_PAN,    -1},
        {NULL, -2}
    };
    int id = -2;
    for(int i = 0; key_ids[i].key; i++){
        if(wcscmp(cszKeyName, key_ids[i].key) == 0){
            id = key_ids[i].id;
            break;
        }
    }
    if(id >= -1){
        KbNsfplugDecoder::OpenDialog(pConfig, id);
    }
}
/////////////////////////////////////////////////////////////////////////////
BOOL __fastcall kbnsfplug_EnumConfig(IKpiConfigEnumerator *pEnumerator)
{
    static const KPI_CFG_SECTION sec[] = {
        {SEC_GENERAL, NULL, L"kbnsfplug.kpi に関する一般的な設定です。\r\n"},
        {NULL, NULL, NULL}
    };
    static const KPI_CFG_KEY key[] = {
        {KPI_CFG_TYPE_BIN, SEC_GENERAL, KEY_RESET_CONFIG, L"設定初期化", 
            NULL, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"本プラグインに関する設定を初期値に戻します。\r\n"
            L"設定画面の位置とサイズは戻しません。\r\n"
            L""},//help
        {KPI_CFG_TYPE_BIN, SEC_GENERAL, KEY_RESET_POS, L"画面位置初期化", 
            NULL, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"設定画面の位置とサイズを初期値に戻します。\r\n"
            L"設定画面が行方不明になった場合は実行してみて下さい。\r\n"
            L""},//help
#ifdef ENABLE_EASY_DLG
        {KPI_CFG_TYPE_BIN, SEC_GENERAL, KEY_EASY, L"簡易設定", 
            NULL, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"簡易設定を開きます。\r\n"
            L"本設定画面上の設定を初期値に戻すには「設定を初期化」を実行して下さい。\r\n"
            L""},//help
#endif
        {KPI_CFG_TYPE_BIN, SEC_GENERAL, KEY_CONFIG, L"詳細設定", 
            NULL, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"詳細設定を開きます。変更しても反映されない設定もあります。\r\n"
            L"再生周波数は曲データを開き直すと反映されます。(本体からの要求が優先)\r\n"
            L"本設定画面上の設定を初期値に戻すには「設定を初期化」を実行して下さい。\r\n"
            L""},//help
        {KPI_CFG_TYPE_BIN, SEC_GENERAL, KEY_MIXER, L"音量バランス", 
            NULL, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"音量バランス設定を開きます。\r\n"
            L"変更は即座に反映されます。\r\n"
            L"反映までにかかる時間は本体の「再生バッファサイズ」と「先読みバッファのサイズ」\r\n"
            L"の値に依存します。(値が小さいほど早く反映されます)\r\n"
            L"「操作」の「リセット」で初期値に戻すことが出来ます。\r\n"
            L""},//help
        {KPI_CFG_TYPE_BIN, SEC_GENERAL, KEY_MASK, L"チャンネルマスク", 
            NULL, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"チャンネルマスク設定を開きます。\r\n"
            L"変更は即座に反映されます。\r\n"
            L"反映までにかかる時間は本体の「再生バッファサイズ」と「先読みバッファのサイズ」\r\n"
            L"の値に依存します。(値が小さいほど早く反映されます)\r\n"
            L"「初期化」ボタンで初期値に戻すことが出来ます。\r\n"
            L""},//help
        {KPI_CFG_TYPE_BIN, SEC_GENERAL, KEY_PAN, L"チャンネルミキサー", 
            NULL, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"チャンネルミキサー設定を開きます。\r\n"
            L"変更は即座に反映されます。\r\n"
            L"反映までにかかる時間は本体の「再生バッファサイズ」と「先読みバッファのサイズ」\r\n"
            L"の値に依存します。(値が小さいほど早く反映されます)\r\n"
            L"「操作」の「リセットボリューム」と「モノラルパン」で初期値に戻すことが出来ます。\r\n"
            L""},//help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_TRACK, L"演奏情報", 
            SZ_TRACK_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"再生中に演奏情報を表示します。\r\n"
            L"\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_BOOL, SEC_GENERAL, KEY_MEMORY, L"メモリ表示", 
            SZ_MEMORY_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"再生中にメモリを表示します。\r\n"
            L"本体の「再生バッファサイズ」と「先読みバッファのサイズ」の分だけ先行して表示されます。\r\n"
            L"(値が小さいほど表示のずれが小さくなります)\r\n"
            L""},//help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_INFO, L"プロパティ", 
            SZ_INFO_DEFAULT, NULL, NULL, //default/min/max
            NULL, NULL, //list/listdesc
            L"再生中の曲データの情報を表示します。\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 kbnsfplug_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_RESET_CONFIG) == 0){//設定の初期化
        int ret = MessageBoxW(NULL, L"本プラグインに関する設定を初期値に戻します。\n"
                                    L"宜しいですか？", L"確認", MB_YESNO|MB_DEFBUTTON2);
        if(ret == IDYES){
            KbNsfplugDecoder::ResetConfig();
            return KPI_CFGRET_RELOAD_KPI;//プラグインの再ロードが必要
        }
        return KPI_CFGRET_OK;
    }
    if(wcscmp(cszKey, KEY_RESET_POS) == 0){//位置の初期化
        int ret = MessageBoxW(NULL, L"設定画面の位置とサイズを初期値に戻します。\n"
                                    L"宜しいですか？", L"確認", MB_YESNO|MB_DEFBUTTON2);
        if(ret == IDYES){
            KbNsfplugDecoder::ResetPos();
            return KPI_CFGRET_RELOAD_KPI;//プラグインの再ロードが必要
        }
        return KPI_CFGRET_OK;
    }
    else if(wcscmp(cszKey, KEY_TRACK) == 0 ||
            wcscmp(cszKey, KEY_MEMORY) == 0 ||
            wcscmp(cszKey, KEY_INFO) == 0){
         pConfig->SetStr(cszSection, cszKey, cszValue);
         return KPI_CFGRET_NOTIFY;
    }
    OpenDialog(pConfig, cszKey);
    return KPI_CFGRET_OK;
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbnsfplug_GetSettings(IKpiConfig *pConfig, kbnsfplug_Settings *pSettings)
{
    pSettings->bTrack = pConfig->GetInt(SEC_GENERAL, KEY_TRACK, TRACK_DEFAULT) != 0;
    pSettings->bMemory = pConfig->GetInt(SEC_GENERAL, KEY_MEMORY, MEMORY_DEFAULT) != 0;
    pSettings->bInfo = pConfig->GetInt(SEC_GENERAL, KEY_INFO, INFO_DEFAULT) != 0;
   
    struct{
        int *pos;
        const wchar_t *key;
    }s[] = {
        {&pSettings->nConfigPosX, KEY_CONFIG_POS_X},
        {&pSettings->nConfigPosY, KEY_CONFIG_POS_Y},
        {&pSettings->nMixerPosX,  KEY_MIXER_POS_X},
        {&pSettings->nMixerPosY,  KEY_MIXER_POS_Y},
        {&pSettings->nMaskPosX,   KEY_MASK_POS_X},
        {&pSettings->nMaskPosY,   KEY_MASK_POS_Y},
        {&pSettings->nPanPosX,    KEY_PAN_POS_X},
        {&pSettings->nPanPosY,    KEY_PAN_POS_Y},
        {&pSettings->nTrackPosX,  KEY_TRACK_POS_X},
        {&pSettings->nTrackPosY,  KEY_TRACK_POS_Y},
        {&pSettings->nTrackPosW,  KEY_TRACK_POS_W},
        {&pSettings->nTrackPosH,  KEY_TRACK_POS_H},
        {&pSettings->nMemoryPosX, KEY_MEMORY_POS_X},
        {&pSettings->nMemoryPosY, KEY_MEMORY_POS_Y},
        {&pSettings->nMemoryPosW, KEY_MEMORY_POS_W},
        {&pSettings->nMemoryPosH, KEY_MEMORY_POS_H},
        {&pSettings->nInfoPosX,   KEY_INFO_POS_X},
        {&pSettings->nInfoPosY,   KEY_INFO_POS_Y},
        {&pSettings->nInfoPosW,   KEY_INFO_POS_W},
        {&pSettings->nInfoPosH,   KEY_INFO_POS_H},
        {NULL, NULL},
    };
    for(int i = 0; s[i].pos; i++){
        *s[i].pos = pConfig->GetInt(SEC_GENERAL, s[i].key, POS_DEFAULT);
    }
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbnsfplug_GetIniData(IKpiConfig *pConfig, char **ppszIniData)
{
    const DWORD buf_size = 16*1024;//16KB あれば十分の筈
    BYTE *buf = (BYTE*)malloc(buf_size+1);
    DWORD dwSize = pConfig->GetBin(SEC_GENERAL, KEY_INIDATA, buf, buf_size);
    if(dwSize > buf_size){//サイズが足りない
        free(buf);
        *ppszIniData = NULL;
    }
    else{
        buf[dwSize] = 0;//少なくとも最後が 0 で終わるようにする
        *ppszIniData = (char*)buf;
    }
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbnsfplug_SaveIniData(IKpiConfig *pConfig, const char *cszIniData)
{
    pConfig->SetBin(SEC_GENERAL, KEY_INIDATA, (const BYTE*)cszIniData, (strlen(cszIniData)+1)*sizeof(cszIniData[0]));
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbnsfplug_SaveDialogPos(IKpiConfig *pConfig, NSFDialogManager *pManager)
{//ダイアログの位置とサイズを保存
    if(!pManager)return;
    WINDOWPLACEMENT wp;
    wp.length = sizeof(wp);
    struct{
        CWnd *wnd;
        const wchar_t *key_x;
        const wchar_t *key_y;
        const wchar_t *key_w;
        const wchar_t *key_h;
    }s[] = {
        {pManager->config, KEY_CONFIG_POS_X, KEY_CONFIG_POS_Y, NULL, NULL},
        {pManager->mixer, KEY_MIXER_POS_X, KEY_MIXER_POS_Y, NULL, NULL},
        {pManager->mask, KEY_MASK_POS_X, KEY_MASK_POS_Y, NULL, NULL},
        {pManager->panner, KEY_PAN_POS_X, KEY_PAN_POS_Y, NULL, NULL},
        {pManager->track, KEY_TRACK_POS_X, KEY_TRACK_POS_Y, KEY_TRACK_POS_W, KEY_TRACK_POS_H},
        {pManager->memory, KEY_MEMORY_POS_X, KEY_MEMORY_POS_Y, KEY_MEMORY_POS_W, KEY_MEMORY_POS_H},
        {pManager->info, KEY_INFO_POS_X, KEY_INFO_POS_Y, KEY_INFO_POS_W, KEY_INFO_POS_H},
        {NULL, NULL, NULL, NULL, NULL}
    };
    for(int i = 0; s[i].key_x; i++){//s[i].wnd は NULL の場合がある
        if(!s[i].wnd)continue;
        s[i].wnd->GetWindowPlacement(&wp);
        pConfig->SetInt(SEC_GENERAL, s[i].key_x, wp.rcNormalPosition.left);
        pConfig->SetInt(SEC_GENERAL, s[i].key_y, wp.rcNormalPosition.top);
        if(s[i].key_w && s[i].key_h){
            int w = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
            int h = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
            pConfig->SetInt(SEC_GENERAL, s[i].key_w, w);
            pConfig->SetInt(SEC_GENERAL, s[i].key_h, h);
        }
    }
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbnsfplug_ResetDialogPos(IKpiConfig *pConfig)
{//サイズと位置を初期値に戻す
    static const wchar_t *keys[] = {
        KEY_CONFIG_POS_X, KEY_CONFIG_POS_Y, 
        KEY_MIXER_POS_X,  KEY_MIXER_POS_Y, 
        KEY_MASK_POS_X,   KEY_MASK_POS_Y, 
        KEY_PAN_POS_X,    KEY_PAN_POS_Y, 
        KEY_TRACK_POS_X,  KEY_TRACK_POS_Y, KEY_TRACK_POS_W, KEY_TRACK_POS_H, 
        KEY_MEMORY_POS_X, KEY_MEMORY_POS_Y, KEY_MEMORY_POS_W, KEY_MEMORY_POS_H, 
        KEY_INFO_POS_X,   KEY_INFO_POS_Y, KEY_INFO_POS_W, KEY_INFO_POS_H, 
        NULL};
    for(int i = 0; keys[i]; i++){
        pConfig->SetInt(SEC_GENERAL, keys[i], POS_DEFAULT);
    }
}
/////////////////////////////////////////////////////////////////////////////
