#include <windows.h>

#include "kbtim_SettingTimidity.h"
#include "kbini.h"
#include "kbstr.h"

#define HAVE_MKSTEMP
#include "timidity.h"
//*****************************************************************************/
// Setting

enum {
    RESAMPLE_CSPLINE,  //0 = C スプライン補間
    RESAMPLE_LAGRANGE, //1 = ラグランジュ補間
    RESAMPLE_GAUSS,    //2 = ガウス補間
    RESAMPLE_NEWTON,   //3 = ニュートン補間
    RESAMPLE_LINEAR,   //4 = 線形補間(デフォルト)
    RESAMPLE_NONE      //5 = なし
};

enum {//playmidi.h
	MODULE_TIMIDITY_DEFAULT = 0x0,
	/* GS modules */
	MODULE_SC55 = 0x1,
	MODULE_SC88 = 0x2,
	MODULE_SC88PRO = 0x3,
	MODULE_SC8850 = 0x4,
	/* XG modules */
	MODULE_MU50 = 0x10,
	MODULE_MU80 = 0x11,
	MODULE_MU90 = 0x12,
	MODULE_MU100 = 0x13,
	/* GM modules */
	MODULE_SBLIVE = 0x20,
	MODULE_SBAUDIGY = 0x21,
	/* Special modules */
	MODULE_TIMIDITY_SPECIAL1 = 0x70,
	MODULE_TIMIDITY_DEBUG = 0x7f,
};
struct MIDI_MODULE
{
    const char *cszModuleName;
    int nModule;
}g_Modules[] = {
    {"TiMidity++ Default",   MODULE_TIMIDITY_DEFAULT},
    {"SC-55",                MODULE_SC55},
    {"SC-88",                MODULE_SC88},
    {"SC-88Pro",             MODULE_SC88PRO},
    {"SC-8850",              MODULE_SC8850},
    {"MU-50",                MODULE_MU50},
    {"MU-80",                MODULE_MU80},
    {"MU-90",                MODULE_MU90},
    {"MU-100",               MODULE_MU100},
    {"Sound Blaster Live!",  MODULE_SBLIVE},
    {"Sound Blaster Audigy", MODULE_SBAUDIGY},
    {"TiMidity++ Special 1", MODULE_TIMIDITY_SPECIAL1},
    {"TiMidity++ Debug",     MODULE_TIMIDITY_DEBUG},
    {NULL, 0}
};
enum{
    type_Bool,
    type_Int,
    type_Str,
};
static const char SEC_PLAYER[] = "PLAYER";
static const char SEC_TIMIDITY[] = "TIMIDITY";
static const char SEC_KBTIM[] = "KBTIM"; //kbtim.kpi 固有の設定はこのセクションに保存する

struct KbTimSetupKeyName
{
    const char *cszSectionName;//セクション名
    const char *cszKeyName;    //キー名
    void *pValue;              //値(アドレス値):
    const void *pDefaultValue; //デフォルト値(type_Str のときは文字列の先頭アドレス、それ以外のときは int 型の値(アドレスではない)
    int nType;                 //type_Int(int)、type_Bool(bool), type_Str(char*)
    int nSize;                 //type_Int: sizeof(int), type_Bool: sizeof(bool), type_Str: バッファサイズ
};
#define KEY_NAME_STR(SECTION, KEY) \
                    {SECTION, #KEY, &KEY, (void*)g_##KEY##_default, type_Str, sizeof(KEY)},
#define KEY_NAME_INT(SECTION, KEY) \
                    {SECTION, #KEY, &KEY, (void*)g_##KEY##_default, type_Int, sizeof(KEY)},
#define KEY_NAME_BOOL(SECTION, KEY) \
                    {SECTION, #KEY, &b.##KEY, (void*)g_##KEY##_default, type_Bool, sizeof(b.##KEY)},

//KEY_NAME_STR(section, key)
// => {section, "key", &key, (void*)g_key_default, type_String, sizeof(key)},
//KEY_NAME_INT(section, key)
// => {section, "key", &key, (void*)g_key_default, type_Int, sizeof(key)},
//KEY_NAME_BOOL(section, key)
// => {section, "key", &b.key, (void*)g_key_default, type_Bool, sizeof(b.key)},

//ini ファイルでの指定が無視される設定用
//無視していた設定が将来有効になったときは _IGNORE を除去する
#define KEY_NAME_STR_IGNORE(SECTION, KEY) 
#define KEY_NAME_INT_IGNORE(SECTION, KEY) 
#define KEY_NAME_BOOL_IGNORE(SECTION, KEY) 

KbSettingTimidity::KbSettingTimidity(void)
{
    szTemplateName[0] = 0;
    ConfigFile[0] = 0;
    //opt_reverb_control = g_opt_reverb_control_default;
    SetReverb(g_opt_reverb_control_default);
    //opt_chorus_control = g_opt_chorus_control_default;
    //opt_surround_chorus = g_opt_surround_chorus_default;
    SetChorus(g_opt_chorus_control_default, g_opt_surround_chorus_default);
    opt_delay_control = g_opt_delay_control_default;
    opt_lpf_def = g_opt_lpf_def_default;
    noise_sharp_type = g_noise_sharp_type_default;
    voices = g_voices_default;
    amplification = g_amplification_default;
    control_ratio = g_control_ratio_default;
    //effect_lr_mode = g_effect_lr_mode_default;
    //effect_lr_delay_msec = g_effect_lr_delay_msec_default;
    SetLRDelay(g_effect_lr_mode_default, g_effect_lr_delay_msec_default);
    opt_default_module = g_opt_default_module_default;
    opt_default_mid = g_opt_default_mid_default;
    opt_drum_power = g_opt_drum_power_default;
    modify_release = g_modify_release_default;
    b.opt_modulation_wheel = g_opt_modulation_wheel_default;
    b.opt_portamento = g_opt_portamento_default;
    b.opt_channel_pressure = g_opt_channel_pressure_default;
    b.opt_drum_effect = g_opt_drum_effect_default;
    b.opt_nrpn_vibrato = g_opt_nrpn_vibrato_default;
    b.opt_tva_attack = g_opt_tva_attack_default;
    b.opt_tva_decay = g_opt_tva_decay_default;
    b.opt_tva_release = g_opt_tva_release_default;
    b.opt_eq_control = g_opt_eq_control_default;
    b.opt_insertion_effect = g_opt_insertion_effect_default;
    //b.bReverb = (opt_reverb_control < 0); //SetReverb で設定済み
    //b.bChorus = (opt_chorus_control < 0); //SetChorus で設定済み
    b.opt_overlap_voice_allow = g_opt_overlap_voice_allow_default;
    b.opt_modulation_envelope = g_opt_modulation_envelope_default;
    b.opt_pan_delay = g_opt_pan_delay_default;
    //b.bLRDelay = (g_effect_lr_mode_default != -1);//SetLRDelay で設定済み
    b.antialiasing_allowed = g_antialiasing_allowed_default;
    b.opt_realtime_playing = g_opt_realtime_playing_default;
    b.free_instruments_afterwards = g_free_instruments_afterwards_default;
    b.opt_amp_compensation = g_opt_amp_compensation_default;
    resample_type = g_resample_type_default;
    bNew = FALSE;
}
void __fastcall KbSettingTimidity::DoCheckParameter(void)
{//設定の範囲チェック
 //範囲外になってる場合は範囲内に収まる最も近い値かデフォルト値にする
    //opt_reverb_control
    SetReverb(opt_reverb_control);
    //opt_chorus_control/opt_surround_chorus
    SetChorus(opt_chorus_control, opt_surround_chorus);
    //opt_delay_control
    opt_delay_control = opt_delay_control != 0;
    //opt_lpf_def
    if(opt_lpf_def < 0){
        opt_lpf_def = 0;
    }
    else if(opt_lpf_def > 2){
        opt_lpf_def = 2;
    }
    //noise_sharp_type
    if(noise_sharp_type < 0){
        noise_sharp_type = 0;
    }
    else if(noise_sharp_type > 6){
        noise_sharp_type = 6;
    }
    //voices
    if(voices < 0){
        voices = 0;
    }
    else if(voices > 1024){
        voices = 1024;
    }
    //amplification
    if(amplification < 0){
        amplification = 0;
    }
    //else if(amplification > MAX_AMPLIFICATION){//MAX_AMPLIFICATION=800(音割れがひどすぎる)
    //    amplification = MAX_AMPLIFICATION;
    //}
    else if(amplification > 200){
        amplification = 200;
    }
    //control_ratio
    if(control_ratio < 0){
        control_ratio = 0;
    }
    else if(control_ratio > MAX_CONTROL_RATIO){
        control_ratio = MAX_CONTROL_RATIO;
    }
    //effect_lr_mode, effect_lr_delay_msec
    SetLRDelay(effect_lr_mode, effect_lr_delay_msec);
    //opt_default_module
    switch(opt_default_module){
        case MODULE_TIMIDITY_DEFAULT:
        case MODULE_SC55:
        case MODULE_SC88:
        case MODULE_SC88PRO:
        case MODULE_SC8850:
        case MODULE_MU50:
        case MODULE_MU80:
        case MODULE_MU90:
        case MODULE_MU100:
        case MODULE_SBLIVE:
        case MODULE_SBAUDIGY:
        case MODULE_TIMIDITY_SPECIAL1:
        case MODULE_TIMIDITY_DEBUG:
            break;
        default:
            opt_default_module = g_opt_default_module_default;
            break;
    }
    //opt_default_mid
    switch(opt_default_mid){
        case 0x7e: case 0x41: case 0x43:
            break;
        default:
            opt_default_mid = 0x7e;
            break;
    }
    //modify_release
    if(modify_release < 0){
        modify_release = 0;
    }
    else if(modify_release > 1000){
        modify_release = 1000;
    }
    //opt_drum_power
    if(opt_drum_power < 0){
        opt_drum_power = 0;
    }
    else if(opt_drum_power > 200){
        opt_drum_power = 200;
    }
    //resample_type
    if(resample_type < 0 || resample_type > 5){
        resample_type = g_resample_type_default;
    }
}
void __fastcall KbSettingTimidity::GetReverbLevel(int *pType, int *pLevel)
{
    if(opt_reverb_control >= 0){
        *pType = opt_reverb_control;
        *pLevel = 1;
    }
    else{
        *pType =  1 + (-opt_reverb_control)/128;
        *pLevel = (-opt_reverb_control)%128;
    }
}
void __fastcall KbSettingTimidity::GetChorusLevel(int *pType, int *pLevel)
{
    if(opt_surround_chorus){
        *pType = 2;
    }
    else{
        *pType = opt_chorus_control != 0;
    }
    if(opt_chorus_control < 0){
        *pLevel = -opt_chorus_control;
    }
    else{
        *pLevel = 1;
    }
}
void __fastcall KbSettingTimidity::SetReverb(int nReverb)
{
    if(nReverb < -511){
        nReverb = -511;
    }
    else if(nReverb > 4){
        nReverb = 4;
    }
    opt_reverb_control = nReverb;
    b.bReverb = (opt_reverb_control < 0);
}
void __fastcall KbSettingTimidity::SetReverbLevel(int nType, int nLevel)
{
    int nReverb;
    if(nLevel > 0){
        nReverb = (1-nType)*128 - nLevel;
    }
    else{
        nReverb = nType;
    }
    SetReverb(nReverb);
}
void __fastcall KbSettingTimidity::SetChorus(int nChorus, int nSurroundChorus)
{
    if(nChorus > 1){
        nChorus = 1;
    }
    else if(nChorus < -127){
        nChorus = -127;
    }
    opt_chorus_control = nChorus;
    opt_surround_chorus = nSurroundChorus != 0;
    b.bChorus = (opt_chorus_control < 0);
}
void __fastcall KbSettingTimidity::SetChorusLevel(int nType, int nLevel)
{
    if(nLevel < 0){
        nLevel = 0;
    }
    else if(nLevel > 127){
        nLevel = 127;
    }
    if(nType == 0){//コーラスなし
        opt_chorus_control = 0;
    }
    else{
        if(nLevel > 127){
            nLevel = 127;
        }
        else if(nLevel < 0){
            nLevel = 0;
        }
        if(nLevel > 0){
            opt_chorus_control = -nLevel;
        }
        else{
            opt_chorus_control = 1;
        }
    }
    opt_surround_chorus = (nType == 2);
    b.bChorus = (opt_chorus_control < 0);
}
void __fastcall KbSettingTimidity::SetLRDelay(int nMode, int nMS)
{
    if(nMode < 0){
        nMode = -1;
    }
    else if(nMode > 2){
        nMode = 2;
    }
    if(nMS < 0){
        nMS = 0;
    }
    else if(nMS > 500){
        nMS = 500;
    }
    effect_lr_mode = nMode;
    effect_lr_delay_msec = nMS;
    b.bLRDelay = (nMode >= 0);
}
void __fastcall KbSettingTimidity::LoadOrSaveSettings(const char *cszIniFile, BOOL bLoad)
{
    KbIniFile IniFile;
    IniFile.Open(cszIniFile);
    KbTimSetupKeyName KeyNames[] = {
        KEY_NAME_STR(SEC_PLAYER, ConfigFile)
        KEY_NAME_INT(SEC_TIMIDITY, amplification)
        KEY_NAME_BOOL(SEC_TIMIDITY, antialiasing_allowed)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, opt_buffer_fragments)
        KEY_NAME_INT(SEC_TIMIDITY, control_ratio)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, default_drumchannels)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, default_drumchannel_mask)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_modulation_wheel)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_portamento)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_nrpn_vibrato)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_channel_pressure)
        KEY_NAME_BOOL_IGNORE(SEC_TIMIDITY, opt_trace_text_meta_event)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_overlap_voice_allow)
        KEY_NAME_INT(SEC_TIMIDITY, opt_default_mid)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, default_tonebank)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, special_tonebank)
        KEY_NAME_INT(SEC_TIMIDITY, effect_lr_mode)
        KEY_NAME_INT(SEC_TIMIDITY, effect_lr_delay_msec)
        KEY_NAME_INT(SEC_TIMIDITY, opt_reverb_control)
        KEY_NAME_INT(SEC_TIMIDITY, opt_chorus_control)
        KEY_NAME_INT(SEC_TIMIDITY, opt_surround_chorus)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_tva_attack)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_tva_decay)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_tva_release)
        KEY_NAME_INT(SEC_TIMIDITY, opt_delay_control)
        KEY_NAME_INT(SEC_TIMIDITY, opt_default_module)
        KEY_NAME_INT(SEC_TIMIDITY, opt_lpf_def)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_drum_effect)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_modulation_envelope)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_pan_delay)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_eq_control)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_insertion_effect)
        KEY_NAME_INT(SEC_TIMIDITY, noise_sharp_type)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, opt_evil_mode)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, adjust_panning_immediately)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, fast_decay)
        //KEY_NAME_INT_IGNORE(SEC_TIMIDITY, default_programN)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, opt_ctl)
        KEY_NAME_INT(SEC_TIMIDITY, opt_drum_power)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_amp_compensation)
        KEY_NAME_BOOL(SEC_TIMIDITY, opt_realtime_playing)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, opt_playmode)
        KEY_NAME_STR_IGNORE(SEC_TIMIDITY, OutputName)
        KEY_NAME_STR_IGNORE(SEC_TIMIDITY, OutputDirName)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, auto_output_mode)
        KEY_NAME_INT(SEC_TIMIDITY, voices)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, auto_reduce_polyphony)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, quietchannels)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, temper_type_mute)
        KEY_NAME_STR_IGNORE(SEC_TIMIDITY, opt_qsize)
        KEY_NAME_INT(SEC_TIMIDITY, modify_release)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, allocate_cache_size)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, key_adjust)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, opt_force_keysig)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, opt_pure_intonation)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, opt_init_keysig)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, output_rate)
        KEY_NAME_STR_IGNORE(SEC_TIMIDITY, output_text_code)
        KEY_NAME_BOOL(SEC_TIMIDITY, free_instruments_afterwards)
        KEY_NAME_STR_IGNORE(SEC_TIMIDITY, out_wrd)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, data_block_bits)
        KEY_NAME_INT_IGNORE(SEC_TIMIDITY, data_block_num)
        //kbtim.kpi 固有
        KEY_NAME_INT(SEC_KBTIM, resample_type)
        {NULL, NULL, NULL, NULL, 0, 0}
    };
    if(bLoad){//Load
        int i = 0;
        while(KeyNames[i].cszSectionName){
            if(KeyNames[i].nType == type_Str){
                IniFile.ReadStr(KeyNames[i].cszSectionName,
                                  KeyNames[i].cszKeyName,
                                  (const char*)KeyNames[i].pDefaultValue,
                                  (char*)KeyNames[i].pValue,
                                  KeyNames[i].nSize);
            }
            else if(KeyNames[i].nType == type_Bool ||
                    KeyNames[i].nType == type_Int){
                int nValue = IniFile.ReadInt(KeyNames[i].cszSectionName,
                                               KeyNames[i].cszKeyName,
                                               (int)KeyNames[i].pDefaultValue);
                if(KeyNames[i].nType == type_Bool){
                    *((bool*)KeyNames[i].pValue) = (nValue != 0);
                }
                else{
                    *((int*)KeyNames[i].pValue) = nValue;
                }
            }
            i++;
        }
        //設定の範囲チェック
        DoCheckParameter();
    }
    else{//Save
        //設定の範囲チェック
        DoCheckParameter();
        int i = 0;
        while(KeyNames[i].cszSectionName){
            if(KeyNames[i].nType == type_Str){
                const char *pszValue = (const char*)KeyNames[i].pValue;
                IniFile.WriteStr(KeyNames[i].cszSectionName,
                                 KeyNames[i].cszKeyName,
                                 pszValue);
            }
            else if(KeyNames[i].nType == type_Bool ||
                    KeyNames[i].nType == type_Int){
                void *pValue = KeyNames[i].pValue;
                int nValue;
                if(KeyNames[i].nType == type_Bool){
                    nValue = *((bool*)pValue);
                }
                else{
                    nValue = *((int*)pValue);
                }
                IniFile.WriteInt(KeyNames[i].cszSectionName,
                                 KeyNames[i].cszKeyName,
                                 nValue);
            }
            i++;
        }
        IniFile.Flush();
        bNew = FALSE;
    }
}

const char * __fastcall KbSettingTimidity::GetModuleNameFromIndex(int nIndex)
{
    if(0 <= nIndex && nIndex < sizeof(g_Modules) / sizeof(g_Modules[0]) - 1){
        return g_Modules[nIndex].cszModuleName;
    }
    return NULL;
}
int __fastcall KbSettingTimidity::GetModuleFromIndex(int nIndex)
{
    if(0 <= nIndex && nIndex < sizeof(g_Modules) / sizeof(g_Modules[0]) - 1){
        return g_Modules[nIndex].nModule;
    }
    return g_opt_default_module_default;
}
int __fastcall KbSettingTimidity::GetIndexFromModule(int nModule)
{
    int i;
    for(i = 0; g_Modules[i].cszModuleName; i++){
        if(g_Modules[i].nModule == nModule){
            return i;
        }
    }
    return -1;
}