#include <windows.h>

extern "C"{

#include "config.h"
#include "interface.h"

#include "timidity.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "readmidi.h"
#include "reverb.h"
#include "output.h"
#include "controls.h"
#include "recache.h"
#include "tables.h"
#include "resample.h"

extern int opt_default_mid;
extern int effect_lr_mode;
extern int effect_lr_delay_msec;
extern char def_instr_name[];
extern int opt_control_ratio;
extern char *opt_aq_max_buff;
extern char *opt_aq_fill_buff;
extern int opt_evil_mode;
extern int opt_buffer_fragments;
extern int32 opt_output_rate;
extern volatile int data_block_bits;
extern volatile int data_block_num;
}

#include "kbtim_setting.h"
#include "kbstr.h"

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

KbTimSetting::KbTimSetting(void)
{
    m_szIniFile[0] = 0;
    m_ftIni.dwHighDateTime = m_ftIni.dwLowDateTime = 0;
    m_ftCfg.dwHighDateTime = m_ftCfg.dwLowDateTime = 0;
    m_hNotification = INVALID_HANDLE_VALUE;
}
KbTimSetting::~KbTimSetting(void)
{
    Close();
}
BOOL __fastcall KbTimSetting::LoadIniFile(const char *cszIniFile)
{
    kbStrLCpy(m_szIniFile, cszIniFile, sizeof(m_szIniFile));
    m_Setting.LoadSettings(cszIniFile);
    //INI/CFG のファイル更新日時を取得
    GetIniCfgFileTime(&m_ftIni, &m_ftCfg);

    //CFG ファイルが置かれているフォルダの更新監視
    char szCfgDir[MAX_PATH*2];//CFG が置かれているフォルダ
    kbExtractFilePath(szCfgDir, GetCfgFileName(), sizeof(szCfgDir));
    kbRemovePathDelimiter(szCfgDir);
    if(szCfgDir[0]){
        m_hNotification = 
            FindFirstChangeNotification(szCfgDir,
                                        TRUE,
                                        FILE_NOTIFY_CHANGE_FILE_NAME |
                                        FILE_NOTIFY_CHANGE_DIR_NAME  |
                                        FILE_NOTIFY_CHANGE_LAST_WRITE);
    }
    else{
        m_hNotification = INVALID_HANDLE_VALUE;
    }
    return TRUE;
}
void __fastcall KbTimSetting::Close(void)
{
    m_szIniFile[0] = 0;
    m_Setting.ConfigFile[0] = 0;
    m_ftIni.dwHighDateTime = m_ftIni.dwLowDateTime = 0;
    m_ftCfg.dwHighDateTime = m_ftCfg.dwLowDateTime = 0;
    if(m_hNotification != INVALID_HANDLE_VALUE){
        FindCloseChangeNotification(m_hNotification);
        m_hNotification = INVALID_HANDLE_VALUE;
    }
}
void __fastcall KbTimSetting::Apply(void)
{
    int i;
    amplification = m_Setting.amplification;
    antialiasing_allowed = m_Setting.b.antialiasing_allowed;
    //opt_buffer_fragments = -1;
    //default_drumchannels = default_drumchannels;
    //default_drumchannel_mask = default_drumchannel_mask;
    opt_modulation_wheel = m_Setting.b.opt_modulation_wheel;
    opt_portamento = m_Setting.b.opt_portamento;
    opt_nrpn_vibrato = m_Setting.b.opt_nrpn_vibrato;
    opt_channel_pressure = m_Setting.b.opt_channel_pressure;
    //opt_trace_text_meta_event = //SetFlag(st->opt_trace_text_meta_event);
    opt_overlap_voice_allow = m_Setting.b.opt_overlap_voice_allow;
    opt_default_mid = m_Setting.opt_default_mid;
    //default_tonebank = ;
    //special_tonebank = ;
    effect_lr_mode = m_Setting.effect_lr_mode;
    effect_lr_delay_msec = m_Setting.effect_lr_delay_msec;
    opt_reverb_control = m_Setting.opt_reverb_control;
    opt_chorus_control = m_Setting.opt_chorus_control;
	opt_surround_chorus = m_Setting.opt_chorus_control;
    noise_sharp_type = m_Setting.noise_sharp_type;
    //opt_evil_mode = ;
	opt_tva_attack = m_Setting.b.opt_tva_attack;
	opt_tva_decay = m_Setting.b.opt_tva_decay;
	opt_tva_release = m_Setting.b.opt_tva_release;
	opt_delay_control = m_Setting.opt_delay_control;
    opt_default_module = m_Setting.opt_default_module;
	opt_lpf_def = m_Setting.opt_lpf_def;
	opt_drum_effect = m_Setting.b.opt_drum_effect;
	opt_modulation_envelope = m_Setting.b.opt_modulation_envelope;
	opt_pan_delay = m_Setting.b.opt_pan_delay;
	opt_eq_control = m_Setting.b.opt_eq_control;
	opt_insertion_effect = m_Setting.b.opt_insertion_effect;
    //adjust_panning_immediately = ;
    //fast_decay = ;
    //for(i = 0; i < MAX_CHANNELS; i++)
	//default_program[i] = st->default_program[i];
    for(i = 0; i < MAX_CHANNELS; i++)
	default_program[i] = SPECIAL_PROGRAM;
    max_voices = voices = m_Setting.voices;
	auto_reduce_polyphony = 0;
    //quietchannels = st->quietchannels;
    //temper_type_mute = st->temper_type_mute;

    modify_release = m_Setting.modify_release;
    //allocate_cache_size = ;
	//key_adjust = ;
	//opt_force_keysig = ;
	//opt_pure_intonation = ;
	//opt_init_keysig = ;
    free_instruments_afterwards = m_Setting.b.free_instruments_afterwards;
    opt_realtime_playing = m_Setting.b.opt_realtime_playing;

    control_ratio = opt_control_ratio = m_Setting.control_ratio;
	//opt_control_ratio == 0 の場合の control_ratio の値の設定は
    //kbtim_initialize で行う
    
    opt_drum_power = m_Setting.opt_drum_power;
    opt_amp_compensation = m_Setting.b.opt_amp_compensation;
    //data_block_bits = ;
    //data_block_num = ;

    //Resampling 補間
    set_current_resampler(m_Setting.resample_type);
}
void __fastcall KbTimSetting::GetIniCfgFileTime(FILETIME* pftIni, FILETIME* pftCfg)
{//INI/CFG ファイルの更新日時を取得
    HANDLE hFile;
    const char *cszIni = GetIniFileName();
    const char *cszCfg = GetCfgFileName();
    const char *filenames[] = {cszIni, cszCfg, NULL};
    FILETIME   *filetimes[] = {pftIni, pftCfg, NULL};
    int i = 0;
    while(filenames[i]){
        hFile = CreateFile(filenames[i],
                           GENERIC_READ,
                           FILE_SHARE_READ|FILE_SHARE_WRITE,
                           NULL, 
                           OPEN_EXISTING,
                           FILE_ATTRIBUTE_NORMAL,
                           NULL);
        filetimes[i]->dwHighDateTime = filetimes[i]->dwLowDateTime = 0;
        if(hFile != INVALID_HANDLE_VALUE){
            GetFileTime(hFile, NULL, NULL, filetimes[i]);
            CloseHandle(hFile);
        }
        i++;
    }
}
BOOL __fastcall KbTimSetting::IsUpdated(void)
{//
    FILETIME ftIni, ftCfg;
    GetIniCfgFileTime(&ftIni, &ftCfg);
    if(CompareFileTime(&ftIni, &m_ftIni) != 0 ||
       CompareFileTime(&ftCfg, &m_ftCfg) != 0){
        //INI or CFG のファイル更新日時が変わっている
        //OutputDebugString("IniFile or CfgFile is updated\n");
        return TRUE;
    }
    else if((ftIni.dwHighDateTime == 0 && ftIni.dwLowDateTime == 0) ||
            (ftCfg.dwHighDateTime == 0 && ftCfg.dwLowDateTime == 0)){
        //INI or CFG ファイルが存在しない
        //OutputDebugString("IniFile or CfgFile does not exist\n");
        return TRUE;
    }
    else if(m_hNotification != INVALID_HANDLE_VALUE &&
            WaitForSingleObject(m_hNotification, 0) == WAIT_OBJECT_0){
        //CFG が置かれているフォルダが更新されている
        //OutputDebugString("CfgFolder is updated\n");
        return TRUE;
    }
    else{
        return FALSE;
    }
}
