/*
 * SampleGenerator.cpp
 * -------------------
 * Purpose: Generate samples from math formulas using muParser
 * Notes  : (currently none)
 * Authors: OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */


#include "stdafx.h"

#if MPT_DISABLED_CODE

#include "SampleGenerator.h"
#include "modsmp_ctrl.h"

int CSampleGenerator::sample_frequency = 44100;
int CSampleGenerator::sample_length = CSampleGenerator::sample_frequency;
mu::string_type CSampleGenerator::expression = _T("sin(xp * _pi)");
smpgen_clip_methods CSampleGenerator::sample_clipping = smpgen_normalize;

mu::value_type *CSampleGenerator::sample_buffer = nullptr;
size_t CSampleGenerator::samples_written = 0;


CSampleGenerator::CSampleGenerator()
{

	// Setup function callbacks
	muParser.DefineFun(_T("clip"), &ClipCallback, false);
	muParser.DefineFun(_T("pwm"), &PWMCallback, false);
	muParser.DefineFun(_T("rnd"), &RndCallback, false);
	muParser.DefineFun(_T("smp"), &SampleDataCallback, false);
	muParser.DefineFun(_T("tri"), &TriangleCallback, false);

	// Setup binary operator callbacks
	muParser.DefineOprt(_T("mod"), &ModuloCallback, 0);

	//muParser.DefineConst("pi", (mu::value_type)PARSER_CONST_PI);

}


// Open the smpgen dialog
bool CSampleGenerator::ShowDialog()
{
	bool isDone = false, result = false;
	while(!isDone)
	{
		CSmpGenDialog dlg(sample_frequency, sample_length, sample_clipping, expression);
		dlg.DoModal();

		// pressed "OK" button?
		if(dlg.CanApply())
		{
			sample_frequency = dlg.GetFrequency();
			sample_length = dlg.GetLength();
			sample_clipping = dlg.GetClipping();
			expression = dlg.GetExpression();
			isDone = CanRenderSample();
			if(isDone) isDone = TestExpression();	// show dialog again if the formula can't be parsed.
			result = true;
		} else
		{
			isDone = true; // just quit.
			result = false;
		}
	}
	return result;
}


// Check if the currently select expression can be parsed by muParser.
bool CSampleGenerator::TestExpression()
{
	// reset helper variables
	samples_written = 0;
	sample_buffer = nullptr;

	muParser.SetExpr(expression);
	mu::value_type x = 0;
	muParser.DefineVar(_T("x"), &x);
	muParser.DefineVar(_T("xp"), &x);
	muParser.DefineVar(_T("len"), &x);
	muParser.DefineVar(_T("lens"), &x);
	muParser.DefineVar(_T("freq"), &x);

	try
	{
		muParser.Eval();
	}
	catch (mu::Parser::exception_type &e)
	{
		ShowError(&e);
		return false;
	}
	return true;
}


// Check if sample parameters are valid.
bool CSampleGenerator::CanRenderSample() const
{
	if(sample_frequency < SMPGEN_MINFREQ || sample_frequency > SMPGEN_MAXFREQ || sample_length < SMPGEN_MINLENGTH || sample_length > SMPGEN_MAXLENGTH) return false;
	return true;
}


// Actual render loop.
bool CSampleGenerator::RenderSample(CSoundFile *pSndFile, SAMPLEINDEX nSample)
{
	if(!CanRenderSample() || !TestExpression() || (pSndFile == nullptr) || (nSample < 1) || (nSample > pSndFile->m_nSamples)) return false;

	// allocate a new buffer
	sample_buffer = (mu::value_type *)malloc(sample_length * sizeof(mu::value_type));
	if(sample_buffer == nullptr) return false;
	memset(sample_buffer, 0, sample_length * sizeof(mu::value_type));

	mu::value_type x = 0, xp = 0;
	mu::value_type v_len = sample_length, v_freq = sample_frequency, v_lens = v_len / v_freq;
	muParser.DefineVar(_T("x"), &x);
	muParser.DefineVar(_T("xp"), &xp);
	muParser.DefineVar(_T("len"), &v_len);
	muParser.DefineVar(_T("lens"), &v_lens);
	muParser.DefineVar(_T("freq"), &v_freq);

	bool success = true;
	mu::value_type minmax = 0;

	for(size_t i = 0; i < (size_t)sample_length; i++)
	{
		samples_written = i;
		x = (mu::value_type)i;
		xp = x * 100 / sample_length;

		try
		{
			sample_buffer[i] = muParser.Eval();
		}
		catch (mu::Parser::exception_type &e)
		{
			// let's just ignore div by zero errors (note: this error code is currently unused (muParser 1.30))
			if(e.GetCode() != mu::ecDIV_BY_ZERO)
			{
				ShowError(&e);
				success = false;
				break;
			}
			sample_buffer[i] = 0;
		}
		// new maximum value?
		if(std::abs(sample_buffer[i]) > minmax) minmax = std::abs(sample_buffer[i]);

	}

	if(success)
	{
		MODSAMPLE *pModSample = &pSndFile->Samples[nSample];

		BEGIN_CRITICAL();

		// first, save some memory... (leads to crashes)
		//CSoundFile::FreeSample(pModSample->pSample);
		//pModSample->pSample = nullptr;

		if(minmax == 0) minmax = 1;	// avoid division by 0

		// convert sample to 16-bit (or whateve rhas been specified)
		int16 *pSample = (sampling_type *)CSoundFile::AllocateSample((sample_length + 4) * SMPGEN_MIXBYTES);
		for(size_t i = 0; i < (size_t)sample_length; i++)
		{
			switch(sample_clipping)
			{
			case smpgen_clip: sample_buffer[i] = CLAMP(sample_buffer[i], -1, 1); break;	// option 1: clip
			case smpgen_normalize: sample_buffer[i] /= minmax; break;	// option 3: normalize
			}

			pSample[i] = (sampling_type)(sample_buffer[i] * sample_maxvalue);
		}

		// set new sample proprerties
		pModSample->nC5Speed = sample_frequency;
		CSoundFile::FrequencyToTranspose(pModSample);
		pModSample->uFlags |= CHN_16BIT;	// has to be adjusted if SMPGEN_MIXBYTES changes!
		pModSample->uFlags &= ~(CHN_STEREO|CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
		pModSample->nLoopStart = 0;
		pModSample->nLoopEnd = sample_length;
		pModSample->nSustainStart = pModSample->nSustainEnd = 0;
		if(sample_length / sample_frequency < 5)	// arbitrary limit for automatic sample loop (5 seconds)
			pModSample->uFlags |= CHN_LOOP;
		else
			pModSample->uFlags &= ~(CHN_LOOP|CHN_PINGPONGLOOP);

		ctrlSmp::ReplaceSample(*pModSample, (LPSTR)pSample, sample_length, pSndFile);

		END_CRITICAL();
	}

	free(sample_buffer);
	sample_buffer = nullptr;

	return success;
}


// Callback function to access sample data
mu::value_type CSampleGenerator::SampleDataCallback(mu::value_type v)
{
	if(sample_buffer == nullptr) return 0;
	v = CLAMP(v, 0, samples_written);
	size_t pos = static_cast<size_t>(v);
	return sample_buffer[pos];
}


void CSampleGenerator::ShowError(mu::Parser::exception_type *e)
{
	std::string errmsg;
	errmsg = "The expression\n    " + e->GetExpr() + "\ncontains an error ";
	if(!e->GetToken().empty())
		errmsg += "in the token\n    " + e->GetToken() + "\n";
	errmsg += "at position " + Stringify(e->GetPos()) + ".\nThe error message was: " + e->GetMsg();
	::MessageBox(0, errmsg.c_str(), _T("muParser Sample Generator"), 0);
}


//////////////////////////////////////////////////////////////////////////
// Sample Generator Dialog implementation

#define MAX_SAMPLEGEN_EXPRESSIONS 61

BEGIN_MESSAGE_MAP(CSmpGenDialog, CDialog)
	ON_EN_CHANGE(IDC_EDIT_SAMPLE_LENGTH,		&CSmpGenDialog::OnSampleLengthChanged)
	ON_EN_CHANGE(IDC_EDIT_SAMPLE_LENGTH_SEC,	&CSmpGenDialog::OnSampleSecondsChanged)
	ON_EN_CHANGE(IDC_EDIT_SAMPLE_FREQ,			&CSmpGenDialog::OnSampleFreqChanged)
	ON_EN_CHANGE(IDC_EDIT_FORMULA,				&CSmpGenDialog::OnExpressionChanged)
	ON_COMMAND(IDC_BUTTON_SHOW_EXPRESSIONS,		&CSmpGenDialog::OnShowExpressions)
	ON_COMMAND(IDC_BUTTON_SAMPLEGEN_PRESETS,	&CSmpGenDialog::OnShowPresets)
	ON_COMMAND_RANGE(ID_SAMPLE_GENERATOR_MENU, ID_SAMPLE_GENERATOR_MENU + MAX_SAMPLEGEN_EXPRESSIONS - 1, &CSmpGenDialog::OnInsertExpression)
	ON_COMMAND_RANGE(ID_SAMPLE_GENERATOR_PRESET_MENU, ID_SAMPLE_GENERATOR_PRESET_MENU + MAX_SAMPLEGEN_PRESETS + 1, &CSmpGenDialog::OnSelectPreset)
END_MESSAGE_MAP()


// List of all possible expression for expression menu
const samplegen_expression menu_descriptions[MAX_SAMPLEGEN_EXPRESSIONS] =
{
	{"Variables", ""},
	{"Current position (sampling point)", "x"},
	{"Current position (percentage)", "xp"},
	{"Sample length", "len"},
	{"Sample length (seconds)", "lens"},
	{"Sampling frequency", "freq"},
	{"Constants", ""},
	{"Pi", "_pi"},
	{"e", "_e"},
	{"Trigonometric functions", ""},
	{"Sine", "sin(x)"},
	{"Cosine", "cos(x)"},
	{"Tangens", "tan(x)"},
	{"Arcus Sine", "asin(x)"},
	{"Arcus Cosine", "acos(x)"},
	{"Arcus Tangens", "atan(x)"},
	{"Hyperbolic Sine", "sinh(x)"},
	{"Hyperbolic Cosine", "cosh(x)"},
	{"Hyperbolic Tangens", "tanh(x)"},
	{"Hyperbolic Arcus Sine", "asinh(x)"},
	{"Hyperbolic Arcus Cosine", "acosh(x)"},
	{"Hyperbolic Arcus Tangens", "atanh(x)"},
	{"Log, Exp, Root", ""},
	{"Logarithm (base 2)", "log2(x)"},
	{"Logarithm (base 10)", "log(x)"},
	{"Natural Logarithm (base e)", "ln(x)"},
	{"e^x", "exp(x)"},
	{"Square Root", "sqrt(x)"},
	{"Sign and rounding", ""},
	{"Sign", "sign(x)"},
	{"Absolute value", "abs(x)"},
	{"Round to nearest integer", "rint(x)"},
	{"Sets", ""},
	{"Minimum", "min(x, y, ...)"},
	{"Maximum", "max(x, y, ...)"},
	{"Sum", "sum(x, y, ...)"},
	{"Mean value", "avg(x, y, ...)"},
	{"Misc functions", ""},
	{"Pulse generator", "pwm(position, duty%, width)"},
	{"Triangle", "tri(position, width)"},
	{"Random value between 0 and x", "rnd(x)"},
	{"Access previous sampling point", "smp(position)"},
	{"Clip between values", "clip(value, minclip, maxclip)"},
	{"If...Then...Else", "if(condition, statement1, statement2)"},
	{"Operators", ""},
	{"Assignment", "x = y"},
	{"Logical And", "x abd y"},
	{"Logical Or", "x or y"},
	{"Logical Xor", "x xor y"},
	{"Less or equal", "x <= y"},
	{"Greater or equal", "x >= y"},
	{"Not equal", "x != y"},
	{"Equal", "x == y"},
	{"Greater than", "x > y"},
	{"Less than", "x < y"},
	{"Addition", "x + y"},
	{"Subtraction", "x - y"},
	{"Multiplication", "x * y"},
	{"Division", "x / y"},
	{"x^y", "x ^ y"},
	{"Modulo", "x mod y"},
};


BOOL CSmpGenDialog::OnInitDialog()
{
	CDialog::OnInitDialog();
	RecalcParameters(false, true);
	SetDlgItemText(IDC_EDIT_FORMULA, expression.c_str());

	int check = IDC_RADIO_SMPCLIP1;
	switch(sample_clipping)
	{
	case smpgen_clip: check = IDC_RADIO_SMPCLIP1; break;
	case smpgen_overflow: check = IDC_RADIO_SMPCLIP2; break;
	case smpgen_normalize: check = IDC_RADIO_SMPCLIP3; break;
	}
	CheckRadioButton(IDC_RADIO_SMPCLIP1, IDC_RADIO_SMPCLIP3, check);

	if(presets.GetNumPresets() == 0)
	{
		CreateDefaultPresets();
	}

	// Create font for "dropdown" button (Marlett system font)
	hButtonFont = CreateFont(14, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, TEXT("Marlett"));
	::SendMessage(GetDlgItem(IDC_BUTTON_SHOW_EXPRESSIONS)->m_hWnd, WM_SETFONT, (WPARAM)hButtonFont, MAKELPARAM(TRUE, 0));

	return TRUE;
}


void CSmpGenDialog::OnOK()
{
	CDialog::OnOK();
	apply = true;

	int check = GetCheckedRadioButton(IDC_RADIO_SMPCLIP1, IDC_RADIO_SMPCLIP3);
	switch(check)
	{
	case IDC_RADIO_SMPCLIP1: sample_clipping = smpgen_clip; break;
	case IDC_RADIO_SMPCLIP2: sample_clipping = smpgen_overflow; break;
	case IDC_RADIO_SMPCLIP3: sample_clipping = smpgen_normalize; break;
	}

	DeleteObject(hButtonFont);
}


void CSmpGenDialog::OnCancel()
{
	CDialog::OnCancel();
	apply = false;
}


// User changed formula
void CSmpGenDialog::OnExpressionChanged()
{
	CString result;
	GetDlgItemText(IDC_EDIT_FORMULA, result);
	expression = result;
}


// User changed sample length field
void CSmpGenDialog::OnSampleLengthChanged()
{
	int temp_length = GetDlgItemInt(IDC_EDIT_SAMPLE_LENGTH);
	if(temp_length >= SMPGEN_MINLENGTH && temp_length <= SMPGEN_MAXLENGTH)
	{
		sample_length = temp_length;
		RecalcParameters(false);
	}
}


// User changed sample length (seconds) field
void CSmpGenDialog::OnSampleSecondsChanged()
{
	CString str;
	GetDlgItemText(IDC_EDIT_SAMPLE_LENGTH_SEC, str);
	double temp_seconds = atof(str);
	if(temp_seconds > 0)
	{
		sample_seconds = temp_seconds;
		RecalcParameters(true);
	}
}


// User changed sample frequency field
void CSmpGenDialog::OnSampleFreqChanged()
{
	int temp_freq = GetDlgItemInt(IDC_EDIT_SAMPLE_FREQ);
	if(temp_freq >= SMPGEN_MINFREQ && temp_freq <= SMPGEN_MAXFREQ)
	{
		sample_frequency = temp_freq;
		RecalcParameters(false);
	}
}


// Show all expressions that can be input
void CSmpGenDialog::OnShowExpressions()
{
	HMENU hMenu = ::CreatePopupMenu(), hSubMenu = NULL;
	if(!hMenu) return;

	for(int i = 0; i < MAX_SAMPLEGEN_EXPRESSIONS; i++)
	{
		if(menu_descriptions[i].expression == "")
		{
			// add sub menu
			if(hSubMenu != NULL) ::DestroyMenu(hSubMenu);
			hSubMenu = ::CreatePopupMenu();

			AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, menu_descriptions[i].description.c_str());
		} else
		{
			// add sub menu entry (formula)
			AppendMenu(hSubMenu, MF_STRING, ID_SAMPLE_GENERATOR_MENU + i, menu_descriptions[i].description.c_str());
		}
	}

	// place popup menu below button
	RECT button;
	GetDlgItem(IDC_BUTTON_SHOW_EXPRESSIONS)->GetWindowRect(&button);
	::TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, button.left, button.bottom, 0, m_hWnd, NULL);
	::DestroyMenu(hMenu);
	::DestroyMenu(hSubMenu);
}


// Show all expression presets
void CSmpGenDialog::OnShowPresets()
{
	HMENU hMenu = ::CreatePopupMenu();
	if(!hMenu) return;

	bool prestsExist = false;
	for(size_t i = 0; i < presets.GetNumPresets(); i++)
	{
		if(presets.GetPreset(i)->expression != "")
		{
			AppendMenu(hMenu, MF_STRING, ID_SAMPLE_GENERATOR_PRESET_MENU + i, presets.GetPreset(i)->description.c_str());
			prestsExist = true;
		}
	}
	
	if(prestsExist) AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);

	AppendMenu(hMenu, MF_STRING, ID_SAMPLE_GENERATOR_PRESET_MENU + MAX_SAMPLEGEN_PRESETS, _TEXT("Manage..."));

	CString result;
	GetDlgItemText(IDC_EDIT_FORMULA, result);
	if((!result.IsEmpty()) && (presets.GetNumPresets() < MAX_SAMPLEGEN_PRESETS))
	{
		AppendMenu(hMenu, MF_STRING, ID_SAMPLE_GENERATOR_PRESET_MENU + MAX_SAMPLEGEN_PRESETS + 1, _TEXT("Add current..."));
	}

	// place popup menu below button
	RECT button;
	GetDlgItem(IDC_BUTTON_SAMPLEGEN_PRESETS)->GetWindowRect(&button);
	::TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, button.left, button.bottom, 0, m_hWnd, NULL);
	::DestroyMenu(hMenu);
}



// Insert expression from context menu
void CSmpGenDialog::OnInsertExpression(UINT nId)
{
	if((nId < ID_SAMPLE_GENERATOR_MENU) || (nId >= ID_SAMPLE_GENERATOR_MENU + MAX_SAMPLEGEN_EXPRESSIONS)) return;

	expression += " " +  menu_descriptions[nId - ID_SAMPLE_GENERATOR_MENU].expression;

	SetDlgItemText(IDC_EDIT_FORMULA, expression.c_str());
}


// Select a preset (or manage them, or add one)
void CSmpGenDialog::OnSelectPreset(UINT nId)
{
	if((nId < ID_SAMPLE_GENERATOR_PRESET_MENU) || (nId >= ID_SAMPLE_GENERATOR_PRESET_MENU + MAX_SAMPLEGEN_PRESETS + 2)) return;

	if(nId - ID_SAMPLE_GENERATOR_PRESET_MENU >= MAX_SAMPLEGEN_PRESETS)
	{
		// add...
		if((nId - ID_SAMPLE_GENERATOR_PRESET_MENU == MAX_SAMPLEGEN_PRESETS + 1))
		{
			samplegen_expression newPreset;
			newPreset.description = newPreset.expression = expression;
			presets.AddPreset(newPreset);
			// call preset manager now.
		}

		// manage...
		CSmpGenPresetDlg dlg(&presets);
		dlg.DoModal();
	} else
	{
		expression = presets.GetPreset(nId - ID_SAMPLE_GENERATOR_PRESET_MENU)->expression;
		SetDlgItemText(IDC_EDIT_FORMULA, expression.c_str());
	}

}


// Update input fields, depending on what has been chagned
void CSmpGenDialog::RecalcParameters(bool secondsChanged, bool forceRefresh)
{
	static bool isLocked = false;
	if(isLocked) return;
	isLocked = true;	// avoid deadlock

	if(secondsChanged)
	{
		// seconds changed => recalc length
		sample_length = (int)(sample_seconds * sample_frequency);
		if(sample_length < SMPGEN_MINLENGTH || sample_length > SMPGEN_MAXLENGTH) sample_length = SMPGEN_MAXLENGTH;
	} else
	{
		// length/freq changed => recalc seconds
		sample_seconds = ((double)sample_length) / ((double)sample_frequency);
	}

	if(secondsChanged || forceRefresh) SetDlgItemInt(IDC_EDIT_SAMPLE_LENGTH, sample_length);
	if(secondsChanged || forceRefresh) SetDlgItemInt(IDC_EDIT_SAMPLE_FREQ, sample_frequency);
	CString str;
	str.Format("%.4f", sample_seconds);
	if(!secondsChanged || forceRefresh) SetDlgItemText(IDC_EDIT_SAMPLE_LENGTH_SEC, str);

	int smpsize = sample_length * SMPGEN_MIXBYTES;
	if(smpsize < 1024)
	{
		str.Format("Sample Size: %d Bytes", smpsize);
	} else if((smpsize >> 10) < 1024)
	{
		str.Format("Sample Size: %d KB", smpsize >> 10);
	} else
	{
		str.Format("Sample Size: %d MB", smpsize >> 20);
	}
	SetDlgItemText(IDC_STATIC_SMPSIZE_KB, str);

	isLocked = false;
}


// Create a set of default formla presets
void CSmpGenDialog::CreateDefaultPresets()
{
	samplegen_expression preset;

	preset.description = "A440";
	preset.expression = "sin(xp * _pi / 50 * 440 * len / freq)";
	presets.AddPreset(preset);

	preset.description = "Brown Noise (kind of)";
	preset.expression = "rnd(1) * 0.1 + smp(x - 1) * 0.9";
	presets.AddPreset(preset);

	preset.description = "Noisy Saw";
	preset.expression = "(x mod 800) / 800 - 0.5 + rnd(0.1)";
	presets.AddPreset(preset);

	preset.description = "PWM Filter";
	preset.expression = "pwm(x, 50 + sin(xp * _pi / 100) * 40, 100) + tri(x, 50)";
	presets.AddPreset(preset);
	
	preset.description = "Fat PWM Pad";
	preset.expression = "pwm(x, xp, 500) + pwm(x, abs(50 - xp), 1000)";
	presets.AddPreset(preset);

	preset.description = "Dual Square";
	preset.expression = "if((x mod 100) < 50, (x mod 200), -x mod 200)";
	presets.AddPreset(preset);

	preset.description = "Noise Hit";
	preset.expression = "exp(-xp) * (rnd(x) - x / 2)";
	presets.AddPreset(preset);

	preset.description = "Laser";
	preset.expression = "sin(xp * _pi * 100 /(xp ^ 2)) * 100 / sqrt(xp)";
	presets.AddPreset(preset);

	preset.description = "Noisy Laser Hit";
	preset.expression = "(sin(sqrt(xp) * 100) + rnd(1) - 0.5) * exp(-xp / 10)";
	presets.AddPreset(preset);

	preset.description = "Twinkle, Twinkle...";
	preset.expression = "sin(xp * _pi * 100 / xp) * 100 / sqrt(xp)";
	presets.AddPreset(preset);

	preset.description = "FM Tom";
	preset.expression = "sin(xp * _pi * 2 + (xp / 5 - 50) ^ 2) * exp(-xp / 10)";
	presets.AddPreset(preset);

	preset.description = "FM Warp";
	preset.expression = "sin(_pi * xp / 2 * (1 + (1 + sin(_pi * xp / 4 * 50)) / 4)) * exp(-(xp / 8) * .6)";
	presets.AddPreset(preset);

	preset.description = "Weird Noise";
	preset.expression = "rnd(1) * 0.1 + smp(x - rnd(xp)) * 0.9";
	presets.AddPreset(preset);

}



//////////////////////////////////////////////////////////////////////////
// Sample Generator Preset Dialog implementation


BEGIN_MESSAGE_MAP(CSmpGenPresetDlg, CDialog)
	ON_COMMAND(IDC_BUTTON_ADD,				&CSmpGenPresetDlg::OnAddPreset)
	ON_COMMAND(IDC_BUTTON_REMOVE,			&CSmpGenPresetDlg::OnRemovePreset)
	ON_EN_CHANGE(IDC_EDIT_PRESET_NAME,		&CSmpGenPresetDlg::OnTextChanged)
	ON_EN_CHANGE(IDC_EDIT_PRESET_EXPR,		&CSmpGenPresetDlg::OnExpressionChanged)
	ON_LBN_SELCHANGE(IDC_LIST_SAMPLEGEN_PRESETS,	&CSmpGenPresetDlg::OnListSelChange)
END_MESSAGE_MAP()


BOOL CSmpGenPresetDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	RefreshList();

	return TRUE;
}


void CSmpGenPresetDlg::OnOK()
{
	// remove empty presets
	for(size_t i = 0; i < presets->GetNumPresets(); i++)
	{
		if(presets->GetPreset(i)->expression.empty())
		{
			presets->RemovePreset(i);
		}
	}
	CDialog::OnOK();
}


void CSmpGenPresetDlg::OnListSelChange()
{
	currentItem = ((CListBox *)GetDlgItem(IDC_LIST_SAMPLEGEN_PRESETS))->GetCurSel() + 1;
	if(currentItem == 0 || currentItem > presets->GetNumPresets()) return;
	samplegen_expression *preset = presets->GetPreset(currentItem - 1);
	if(preset == nullptr) return;
	SetDlgItemText(IDC_EDIT_PRESET_NAME, preset->description.c_str());
	SetDlgItemText(IDC_EDIT_PRESET_EXPR, preset->expression.c_str());
}


void CSmpGenPresetDlg::OnTextChanged()
{
	if(currentItem == 0 || currentItem > presets->GetNumPresets()) return;
	CString result;
	GetDlgItemText(IDC_EDIT_PRESET_NAME, result);

	samplegen_expression *preset = presets->GetPreset(currentItem - 1);
	if(preset == nullptr) return;
	preset->description = result;

	CListBox *clist = (CListBox *)GetDlgItem(IDC_LIST_SAMPLEGEN_PRESETS);
	clist->DeleteString(currentItem - 1);
	clist->InsertString(currentItem - 1, (preset->description).c_str());
	clist->SetCurSel(currentItem - 1);
}


void CSmpGenPresetDlg::OnExpressionChanged()
{
	if(currentItem == 0 || currentItem > presets->GetNumPresets()) return;
	CString result;
	GetDlgItemText(IDC_EDIT_PRESET_EXPR, result);

	samplegen_expression *preset = presets->GetPreset(currentItem - 1);
	if(preset != nullptr) preset->expression = result;

}


void CSmpGenPresetDlg::OnAddPreset()
{
	samplegen_expression newPreset;
	newPreset.description = "New Preset";
	newPreset.expression = "";
	if(presets->AddPreset(newPreset))
	{
		currentItem = presets->GetNumPresets();
		RefreshList();
	}
}


void CSmpGenPresetDlg::OnRemovePreset()
{
	if(currentItem == 0 || currentItem > presets->GetNumPresets()) return;
	if(presets->RemovePreset(currentItem - 1))
		RefreshList();
}


void CSmpGenPresetDlg::RefreshList()
{
	CListBox *clist = (CListBox *)GetDlgItem(IDC_LIST_SAMPLEGEN_PRESETS);
	clist->SetRedraw(FALSE);	//disable lisbox refreshes during fill to avoid flicker
	clist->ResetContent();
	for(size_t i = 0; i < presets->GetNumPresets(); i++)
	{
		samplegen_expression *preset = presets->GetPreset(i);
		if(preset != nullptr)
			clist->AddString((preset->description).c_str());
	}
	clist->SetRedraw(TRUE);		//re-enable lisbox refreshes
	if(currentItem == 0 || currentItem > presets->GetNumPresets())
	{
		currentItem = presets->GetNumPresets();
	}
	if(currentItem != 0) clist->SetCurSel(currentItem - 1);
	OnListSelChange();
}

#endif // MPT_DISABLED_CODE