/*Arculator 2.1 by Sarah Walker
  Podule configuration subsystem*/
#include <wx/wxprec.h>

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include <wx/xrc/xmlres.h>
#include <wx/filedlg.h>

#include <map>

#include "wx-podule-config.h"

extern "C"
{
#include "arc.h"
#include "config.h"
#include "podules.h"

void *podule_config_get_current(void *window_p, int id);
void podule_config_set_current(void *window_p, int id, void *val);
}
#define IDC_CONFIG_BASE 1000

class wxPointer: public wxObject
{
public:
	wxPointer(const void *p) : p(p) { }

	const void *p;
};

class PoduleConfigDialog: public wxDialog
{
public:
	PoduleConfigDialog(wxWindow *parent, const podule_header_t *podule, const podule_config_t *config_, bool running, int slot_nr, const char *prefix = NULL);

	std::map<int, int> id_map;
	std::map<int, int> type_map;

	const podule_header_t *podule;

	bool running;
	int slot_nr;

private:
	void OnCommand(wxCommandEvent &event);
	void OnButton(wxCommandEvent &event);
	void OnCombo(wxCommandEvent &event);
	void OnText(wxCommandEvent &event);
	const char *get_item_name(const podule_config_item_t *item, const char *prefix);

	char section_name[20];
	const char *prefix;
	char temp_name_s[256];
};

const char *PoduleConfigDialog::get_item_name(const podule_config_item_t *item, const char *prefix)
{
	if (item->flags & CONFIG_FLAGS_NAME_PREFIXED)
	{
		snprintf(temp_name_s, sizeof(temp_name_s), "%s%s", prefix, item->name);
		return temp_name_s;
	}
	else
		return item->name;
}

void PoduleConfigDialog::OnCommand(wxCommandEvent &event)
{
	wxPointer *userData = dynamic_cast<wxPointer *>(event.GetEventUserData());
	const podule_config_t *config = (const podule_config_t *)userData->p;

	switch (event.GetId())
	{
		case wxID_OK:
		{
		int id = IDC_CONFIG_BASE;
		const podule_config_item_t *item = config->items;
		int c;
		int changed = 0;

		while (item->type != -1)
		{
			podule_config_selection_t *selection = item->selection;
			int val_int;
			const char *val_str;

			if (!item->name)
			{
				switch (item->type)
				{
					case CONFIG_BINARY:
					case CONFIG_BUTTON:
					id++;
					break;

					case CONFIG_SELECTION:
					case CONFIG_SELECTION_STRING:
					case CONFIG_STRING:
					id += 2;
					break;
				}

				item++;
				continue;
			}

			switch (item->type)
			{
				case CONFIG_BINARY:
				{
				wxCheckBox *cbox = (wxCheckBox *)this->FindWindow(id);
				val_int = config_get_int(CFG_MACHINE, section_name, get_item_name(item, prefix), item->default_int);

				if (val_int != cbox->GetValue())
					changed = 1;

				id++;
				}
				break;

				case CONFIG_SELECTION:
				{
				wxComboBox *cbox = (wxComboBox *)this->FindWindow(id + 1);
				val_int = config_get_int(CFG_MACHINE, section_name, get_item_name(item, prefix), item->default_int);

				c = cbox->GetCurrentSelection();

				for (; c > 0; c--)
					selection++;

				if (val_int != selection->value)
					changed = 1;

				id += 2;
				}
				break;

				case CONFIG_SELECTION_STRING:
				{
				wxComboBox *cbox = (wxComboBox *)this->FindWindow(id + 1);
				val_str = config_get_string(CFG_MACHINE, section_name, get_item_name(item, prefix), item->default_string);

				c = cbox->GetCurrentSelection();

				for (; c > 0; c--)
					selection++;

				if (strcmp(val_str, selection->value_string))
					changed = 1;

				id += 2;
				}
				break;

				case CONFIG_STRING:
				{
				char temp_s[256];
				wxTextCtrl *text = (wxTextCtrl *)this->FindWindow(id + 1);
				strncpy(temp_s, (const char *)text->GetValue().mb_str(), sizeof(temp_s)-1);
				temp_s[sizeof(temp_s)-1] = 0;

				val_str = config_get_string(CFG_MACHINE, section_name, get_item_name(item, prefix), item->default_string);

				if ((!val_str && temp_s[0]) || (val_str && strcmp(val_str, temp_s)))
					changed = 1;

				id += 2;
				}
				break;

				case CONFIG_BUTTON:
				id++;
				break;
			}
			item++;
		}

		if (config->close)
			changed |= config->close(this);

		if (!changed)
		{
			EndModal(0);
			return;
		}

		if (running)
		{
			if (wxMessageBox("This will reset Arculator!\nOkay to continue?", "Arculator", wxYES_NO | wxCENTRE | wxSTAY_ON_TOP, this) != wxYES)
			{
				EndModal(0);
				return;
			}
		}

		id = IDC_CONFIG_BASE;
		item = config->items;

		while (item->type != -1)
		{
			podule_config_selection_t *selection = item->selection;

			if (!item->name)
			{
				switch (item->type)
				{
					case CONFIG_BINARY:
					case CONFIG_BUTTON:
					id++;
					break;

					case CONFIG_SELECTION:
					case CONFIG_SELECTION_STRING:
					case CONFIG_STRING:
					id += 2;
					break;
				}

				item++;
				continue;
			}

			switch (item->type)
			{
				case CONFIG_BINARY:
				{
				wxCheckBox *cbox = (wxCheckBox *)this->FindWindow(id);
				config_set_int(CFG_MACHINE, section_name, get_item_name(item, prefix), cbox->GetValue());

				id++;
				}
				break;

				case CONFIG_SELECTION:
				{
				wxComboBox *cbox = (wxComboBox *)this->FindWindow(id + 1);
				c = cbox->GetCurrentSelection();

				for (; c > 0; c--)
					selection++;
				config_set_int(CFG_MACHINE, section_name, get_item_name(item, prefix), selection->value);

				id += 2;
				}
				break;

				case CONFIG_SELECTION_STRING:
				{
				wxComboBox *cbox = (wxComboBox *)this->FindWindow(id + 1);
				c = cbox->GetCurrentSelection();

				for (; c > 0; c--)
					selection++;
				config_set_string(CFG_MACHINE, section_name, get_item_name(item, prefix), selection->value_string);

				id += 2;
				}
				break;

				case CONFIG_STRING:
				{
				char temp_s[256];
				wxTextCtrl *text = (wxTextCtrl *)this->FindWindow(id + 1);
				strncpy(temp_s, (const char *)text->GetValue().mb_str(), sizeof(temp_s)-1);
				temp_s[sizeof(temp_s)-1] = 0;

				config_set_string(CFG_MACHINE, section_name, get_item_name(item, prefix), temp_s);

				id += 2;
				}
				break;

				case CONFIG_BUTTON:
				id++;
				break;
			}
			item++;
		}

		saveconfig();
		if (running)
			arc_reset();

		EndModal(1);
		break;
		}

		case wxID_CANCEL:
		EndModal(0);
		break;
	}
}

void PoduleConfigDialog::OnText(wxCommandEvent &event)
{
	wxObject *obj = dynamic_cast<wxObject *>(event.GetEventObject());
	wxPointer *userData = dynamic_cast<wxPointer *>(event.GetEventUserData());

	if (userData && obj)
	{
		const podule_config_item_t *item = (const podule_config_item_t *)userData->p;

		if (item->function)
		{
			static bool in_callback = false;
			int update = 0;

			if (!in_callback)
			{
				in_callback = true;

				switch (item->type)
				{
					case CONFIG_STRING:
					update = item->function(this, item, (void *)(dynamic_cast<wxTextCtrl *>(obj)->GetValue().char_str()));
					break;
				}

				in_callback = false;
			}

			if (update)
				Update();
		}
	}
}

void PoduleConfigDialog::OnCombo(wxCommandEvent &event)
{
	wxObject *obj = dynamic_cast<wxObject *>(event.GetEventObject());
	wxPointer *userData = dynamic_cast<wxPointer *>(event.GetEventUserData());

	if (userData && obj)
	{
		const podule_config_item_t *item = (const podule_config_item_t *)userData->p;

		if (item->function)
		{
			static bool in_callback = false;
			int update = 0;

			if (!in_callback)
			{
				in_callback = true;

				switch (item->type)
				{
					case CONFIG_SELECTION:
					update = item->function(this, item, (void *)(intptr_t)(dynamic_cast<wxComboBox *>(obj)->GetCurrentSelection()));
					break;

					case CONFIG_SELECTION_STRING:
					update = item->function(this, item, (void *)(dynamic_cast<wxComboBox *>(obj)->GetValue().char_str()));
					break;
				}

				in_callback = false;
			}

			if (update)
				Update();
		}
	}
}

void PoduleConfigDialog::OnButton(wxCommandEvent &event)
{
	wxObject *obj = dynamic_cast<wxObject *>(event.GetEventObject());
	wxPointer *userData = dynamic_cast<wxPointer *>(event.GetEventUserData());

	if (userData && obj)
	{
		const podule_config_item_t *item = (const podule_config_item_t *)userData->p;

		if (item->function)
			item->function(this, item, NULL);
	}
}

PoduleConfigDialog::PoduleConfigDialog(wxWindow *parent, const podule_header_t *podule, const podule_config_t *config, bool running, int slot_nr, const char *prefix) :
	wxDialog(parent, -1, "Configure podule"),
	podule(podule),
	running(running),
	slot_nr(slot_nr),
	prefix(prefix)
{
	const podule_config_item_t *item;
	char s[257];
	int id;
	int c;

	snprintf(section_name, 20, "%s.%i", podule->short_name, slot_nr);

	wxFlexGridSizer *root = new wxFlexGridSizer(0, 1, 0, 0);
	root->SetFlexibleDirection(wxBOTH);
	root->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
	SetSizer(root);

	wxFlexGridSizer *sizer = new wxFlexGridSizer(0, 2, 0, 0);
	sizer->SetFlexibleDirection(wxBOTH);
	sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
	sizer->AddGrowableCol(1);
	root->Add(sizer, 1, wxEXPAND, 5);

	id = IDC_CONFIG_BASE;
	item = config->items;
	while (item->type != -1)
	{
		switch (item->type)
		{
			case CONFIG_STRING:
//                        sizer->Add(0, 0, 1, wxEXPAND, 5);
			sizer->Add(new wxStaticText(this, id++, item->description), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
			sizer->Add(new wxTextCtrl(this, id++, wxEmptyString, wxDefaultPosition, wxSize(250, -1)), 0, wxALL, 5);
			break;

			case CONFIG_BINARY:
			sizer->Add(0, 0, 1, wxEXPAND, 5);
			sizer->Add(new wxCheckBox(this, id++, item->description), 0, wxALL, 5);
			break;

			case CONFIG_SELECTION:
			case CONFIG_SELECTION_STRING:
			{
			sprintf(s, "%s:", item->description);
			sizer->Add(new wxStaticText(this, id++, s), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
			wxBoxSizer* comboSizer = new wxBoxSizer(wxHORIZONTAL);
			sizer->Add(comboSizer, 1, wxEXPAND, 5);
			wxComboBox* cb = new wxComboBox(this, id++);
			cb->SetEditable(false);
			comboSizer->Add(cb, 1, wxALL, 5);
			}
			break;

			case CONFIG_BUTTON:
			sizer->Add(0, 0, 1, wxEXPAND, 5);
			sizer->Add(new wxButton(this, id++, item->description), 0, wxALL, 5);
			break;
		}

		item++;
	}

	wxBoxSizer* okCancelSizer = new wxBoxSizer(wxHORIZONTAL);
	root->Add(okCancelSizer, 1, wxEXPAND, 5);

	okCancelSizer->Add(0, 0, 1, wxEXPAND, 5);
	okCancelSizer->Add(new wxButton(this, wxID_OK), 0, wxALL, 5);
	okCancelSizer->Add(new wxButton(this, wxID_CANCEL), 0, wxALL, 5);

	Bind(wxEVT_BUTTON, &PoduleConfigDialog::OnCommand, this, wxID_OK, wxID_OK, new wxPointer(config));
	Bind(wxEVT_BUTTON, &PoduleConfigDialog::OnCommand, this, wxID_CANCEL, wxID_CANCEL, new wxPointer(config));

	id = IDC_CONFIG_BASE;
	item = config->items;
	while (item->type != -1)
	{
		podule_config_selection_t *selection = item->selection;
		int val_int;
		const char *val_str;

		type_map.insert(std::pair<int, int>(item->id, item->type));
		switch (item->type)
		{
			case CONFIG_STRING:
			{
			wxTextCtrl *text = (wxTextCtrl *)this->FindWindow(id+1);
			if (item->name)
				val_str = config_get_string(CFG_MACHINE, section_name, get_item_name(item, prefix), item->default_string);
			else
				val_str = item->default_string;

			text->SetValue(val_str);
			if (item->flags & CONFIG_FLAGS_DISABLED)
				text->Enable(false);
			else
				Bind(wxEVT_TEXT, &PoduleConfigDialog::OnText, this, id+1, id+1, new wxPointer(item));

			id_map.insert(std::pair<int, int>(item->id, id+1));
			id += 2;
			}
			break;

			case CONFIG_BINARY:
			{
			wxCheckBox *cbox = (wxCheckBox *)this->FindWindow(id);
			val_int = config_get_int(CFG_MACHINE, section_name, get_item_name(item, prefix), item->default_int);

			cbox->SetValue(val_int);
			if (item->flags & CONFIG_FLAGS_DISABLED)
				cbox->Enable(false);

			id_map.insert(std::pair<int, int>(item->id, id));
			id++;
			}
			break;

			case CONFIG_SELECTION:
			{
			wxComboBox *cbox = (wxComboBox *)this->FindWindow(id+1);

			val_int = config_get_int(CFG_MACHINE, section_name, get_item_name(item, prefix), item->default_int);

			c = 0;
			while (selection->description[0])
			{
				cbox->Append(selection->description);
				if (val_int == selection->value)
					cbox->Select(c);
				selection++;
				c++;
			}
			if (item->flags & CONFIG_FLAGS_DISABLED)
				cbox->Enable(false);
			Bind(wxEVT_COMBOBOX, &PoduleConfigDialog::OnCombo, this, id+1, id+1, new wxPointer(item));

			id_map.insert(std::pair<int, int>(item->id, id+1));
			id += 2;
			}
			break;

			case CONFIG_SELECTION_STRING:
			{
			wxComboBox *cbox = (wxComboBox *)this->FindWindow(id+1);

			val_str = config_get_string(CFG_MACHINE, section_name, get_item_name(item, prefix), item->default_string);

			c = 0;
			while (selection->description[0])
			{
				cbox->Append(selection->description);
				if (!strcmp(val_str, selection->value_string))
					cbox->Select(c);
				selection++;
				c++;
			}
			if (item->flags & CONFIG_FLAGS_DISABLED)
				cbox->Enable(false);
			Bind(wxEVT_COMBOBOX, &PoduleConfigDialog::OnCombo, this, id+1, id+1, new wxPointer(item));

			id_map.insert(std::pair<int, int>(item->id, id+1));
			id += 2;
			}
			break;

			case CONFIG_BUTTON:
			Bind(wxEVT_BUTTON, &PoduleConfigDialog::OnButton, this, id, id, new wxPointer(item));
			id++;
			break;
		}
		item++;
	}

	Fit();
}

static wxWindow *find_wxWindow(void *window_p, int id, int *type)
{
	PoduleConfigDialog *dlg = static_cast<PoduleConfigDialog *>(window_p);
	int wx_id = dlg->id_map[id];

	*type = dlg->type_map[id];

	return dlg->FindWindow(wx_id);
}

static char temp_s[256];

void *podule_config_get_current(void *window_p, int id)
{
	int type;
	wxWindow *window = find_wxWindow(window_p, id, &type);

	switch (type)
	{
		case CONFIG_STRING:
		{
			wxTextCtrl *text = dynamic_cast<wxTextCtrl *>(window);
			strcpy(temp_s, (const char *)text->GetValue().mb_str());
			return temp_s;
		}

		case CONFIG_SELECTION:
		{
			wxComboBox *cbox = dynamic_cast<wxComboBox *>(window);
			return (void *)(intptr_t)cbox->GetSelection();
		}

		case CONFIG_SELECTION_STRING:
		{
			wxComboBox *cbox = dynamic_cast<wxComboBox *>(window);
			strcpy(temp_s, (const char *)cbox->GetValue().mb_str());
			return temp_s;
		}
	}

	return NULL;
}

void podule_config_set_current(void *window_p, int id, void *val)
{
	int type;
	wxWindow *window = find_wxWindow(window_p, id, &type);

	switch (type)
	{
		case CONFIG_STRING:
		{
			wxTextCtrl *text = dynamic_cast<wxTextCtrl *>(window);
			text->SetValue((char *)val);
			break;
		}

		case CONFIG_SELECTION:
		{
			wxComboBox *cbox = dynamic_cast<wxComboBox *>(window);
			cbox->SetSelection((int)(uintptr_t)val);
			break;
		}

		case CONFIG_SELECTION_STRING:
		{
			wxComboBox *cbox = dynamic_cast<wxComboBox *>(window);
			cbox->SetValue((char *)val);
			break;
		}
	}
}

int podule_config_file_selector(void *window_p, const char *title, const char *default_path, const char *default_fn, const char *default_ext, const char *wildcard, char *dest, int dest_len, int flags)
{
	PoduleConfigDialog *dlg = static_cast<PoduleConfigDialog *>(window_p);
	int wx_flags = 0;

	if (flags & CONFIG_FILESEL_SAVE)
		wx_flags = wxFD_SAVE | wxFD_OVERWRITE_PROMPT;
	else
		wx_flags = wxFD_OPEN | wxFD_FILE_MUST_EXIST;

	wxString fn = wxFileSelector(title, default_path, default_fn, default_ext,
				     wildcard, wx_flags, dlg);

	if (!fn.empty())
	{
		strncpy(dest, fn.mb_str(), dest_len);
		return 0;
	}
	else
		return -1; /*Failure*/
}

int podule_config_open(void *window_p, podule_config_t *config, const char *prefix)
{
	PoduleConfigDialog *parent_dlg = static_cast<PoduleConfigDialog *>(window_p);

	PoduleConfigDialog dlg(parent_dlg, parent_dlg->podule, config, parent_dlg->running, parent_dlg->slot_nr, prefix);

	if (config->init)
		config->init(&dlg);

	return dlg.ShowModal();
}


void ShowPoduleConfig(wxWindow *parent, const podule_header_t *podule, podule_config_t *config, bool running, int slot_nr)
{
	PoduleConfigDialog dlg(parent, podule, config, running, slot_nr);

	if (config->init)
		config->init(&dlg);

	dlg.ShowModal();
}
