
#include "configuration.h"
#include "video.h"
#include "gui.h"
#include "detect.h"

#include <stdlib.h>
#include <string>

// extern VideoDev *videodev;
extern GtkBuilder *_builder_;			// work around for threads
extern PosCtl posctl;


Configuration::Configuration() {
	debugpath = NULL;
	readdumppath = NULL;
	destpath = "./";
	show_debugwin = 0;
};


Configuration::~Configuration() {
};


std::string Configuration::GetDefaultFileName() {
	std::string fn = "";
	char *hd = getenv ((char*)"HOME");

	if (hd != NULL) {
		fn = hd;
		fn = fn + "/" + CONFIGURATION_DEFAULTFILE;
	}

	return fn;
};


void Configuration::SaveDefault() {
	std::string fn = GetDefaultFileName();
	if (fn.length() < 1) {
		printf ("%s:%d %s could not read HOME environment variable\n", __FILE__, __LINE__, __FUNCTION__);
		return;
	}
	SaveConfig (fn);
};


void Configuration::LoadDefault() {
	std::string fn = GetDefaultFileName();
	if (fn.length() < 1) {
		printf ("%s:%d %s could not read HOME environment variable\n", __FILE__, __LINE__, __FUNCTION__);
		return;
	}
	LoadConfig (fn);
};


/*
 * collect all the settings, and push them into the JSONparse object
 * save the json string.
 */
void Configuration::SaveConfig(std::string filename) {
	JSONParse jp;
	string vstr;
	int i;
	GtkWidget *cb;
	GtkWidget *cbe;

	std::string s = setlocale(LC_ALL, NULL);
	setlocale (LC_ALL, "C");

	//
	// Add resolution, format and video device to config json element
	cb = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videores"));
	cbe = gtk_bin_get_child(GTK_BIN(cb));
	jp.AddObject("video_resolution", (string) gtk_entry_get_text(GTK_ENTRY(cbe)));
	cb = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videofmt"));
	cbe = gtk_bin_get_child(GTK_BIN(cb));
	jp.AddObject("video_format", (string) gtk_entry_get_text(GTK_ENTRY(cbe)));
	cb = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videodev"));
	cbe = gtk_bin_get_child(GTK_BIN(cb));
	jp.AddObject("device", (string) gtk_entry_get_text(GTK_ENTRY(cbe)));

	//
	// save posctl settings
	double amin, amax, akp, aki, akd;
	JSONParse jaxis;

	posctl.LockMutex();
	for (i = 0; i < 2; i++) {
		posctl.GetAxisParam(i,  &amin,  &amax,  &akp,  &aki,  &akd);
		jaxis.Clear();
		jaxis.AddObject("min", amin);
		jaxis.AddObject("max", amax);
		jaxis.AddObject("kp", akp);
		jaxis.AddObject("ki", aki);
		jaxis.AddObject("kd", akd);
		printf ("%s:%d save config axis %d Out Range: %f - %f kP:%g kI:%g kD:%g\n",
				__FILE__, __LINE__, i, amin, amax, akp, aki, akd);
		jp.AddObject("posctl_axis" + to_string(i), jaxis);
	}
	jp.AddObject("posctl_filter", posctl.GetFilter());
	jp.AddObject("posctl_device", posctl.GetDevice());
	posctl.UnLockMutex();

	//
	// histogram settings and debayer mode
	jp.AddObject("histogram_log", histogram_log);
	jp.AddObject("debayer_mode", debayer_mode);

	//
	// save destination path
	jp.AddObject("destination_path", destpath);

	//
	// save button config
	for (i = 0; i < BTN_PRESET_MAX; i++) {
		JSONElement je;
		je.type = JSON_T_ARRAY;
		je.name = "presetbtn"+ std::to_string(i);
		list<VideoDevCtrl>::iterator vciter;
		JSONParse jpbtn;
		jpbtn.Clear();

		// convert VideoDevCtrl to JSON
		for (vciter = presetbtn[i].begin(); vciter != presetbtn[i].end(); vciter++) {
			jpbtn.Clear();
			jpbtn.AddObject("name", vciter->name);
			jpbtn.AddObject("id", (signed int) vciter->id);
			jpbtn.AddObject("max", vciter->max);
			jpbtn.AddObject("min", vciter->min);
			jpbtn.AddObject("value", vciter->value);

			if (je.value.length()>0) je.value += ","  + jpbtn.ToString();
			else je.value = jpbtn.ToString();
		}
		jp.AddObject(je);
	}

	//
	// save windows position
	printf ("%s:%d Xwayland is not able to retrieve the current position.\n", __FILE__, __LINE__);
	for (i = 0; i < 6; i++) {
		int x, y, w, h, show;
		string name;

		switch (i) {
		case 0:
			name = "window-main";
			break;
		case 1:
			name = "window-input";
			break;
		case 2:
			name = "window-output";
			break;
		case 3:
			name = "window-detect";
			break;
		case 4:
			name = "window-histogram";
			break;
		case 5:
			name = "window-posctl";
			break;
		}

		GtkWidget *wnd = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), name.c_str()));
		if (wnd) {
			g_object_get (wnd, "visible", &show, NULL);
			gtk_widget_show (wnd);
			gtk_window_get_position (GTK_WINDOW(wnd), &x, &y);
			gtk_window_get_size (GTK_WINDOW(wnd), &w, &h);

			jp.AddObject(name+"_x", x);
			jp.AddObject(name+"_y", y);
			jp.AddObject(name+"_w", w);
			jp.AddObject(name+"_h", h);
			jp.AddObject(name+"_show", show);
		}
	}

	//
	// save config
	if (jp.SaveToFile(filename)) {
		printf ("%s:%d %s could not save to file [%s] Error: %s\n", __FILE__, __LINE__, __FUNCTION__,
				filename.c_str(), strerror(errno));
	}

	setlocale (LC_ALL, s.c_str());
};


void Configuration::LoadConfig(std::string filename) {
	JSONParse jp;
	string vstr;
	int i;
	JSONElement je;

	std::string s = setlocale(LC_ALL, NULL);
	setlocale (LC_ALL, "C");

	if (jp.LoadFromFile(filename)) {
		printf ("%s:%d %s could not load from file [%s] Error: %s\n", __FILE__, __LINE__, __FUNCTION__,
				filename.c_str(), strerror(errno));
	}
	else {
		printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);

		//
		// set resolution and video stream mode
		//
		if (jp.GetValue("video_resolution", &vstr)) {
			GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videores"));
			GtkWidget *cbdevice = gtk_bin_get_child(GTK_BIN(cbox));
			gtk_entry_set_text(GTK_ENTRY(cbdevice), vstr.c_str());
		}
		if (jp.GetValue("video_format", &vstr)) {
			GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videofmt"));
			GtkWidget *cbdevice = gtk_bin_get_child(GTK_BIN(cbox));
			gtk_entry_set_text(GTK_ENTRY(cbdevice), vstr.c_str());
		}

		//
		// load posctl
		double amin, amax, akp, aki, akd, f;
		JSONParse jaxis;

		posctl.LockMutex();
		for (i = 0; i < 2; i++) {
			if (jp.GetObjectJson("posctl_axis"+to_string(i), &jaxis)) {
				jaxis.GetValueDouble("min", &amin);
				jaxis.GetValueDouble("max", &amax);
				jaxis.GetValueDouble("kp", &akp);
				jaxis.GetValueDouble("ki", &aki);
				jaxis.GetValueDouble("kd", &akd);
			}
			printf ("%s:%d load config axis %d Out Range: %f - %f kP:%f kI:%f kD:%f\n",
					__FILE__, __LINE__, i, amin, amax, akp, aki, akd);
			posctl.SetAxisParam(i, amin, amax, akp, aki, akd);
		}
		if (jp.GetValue("posctl_device", &vstr))
			posctl.SetDevice(vstr);
		if (jp.GetValueDouble("posctl_filter", &f))
			posctl.SetFilter(f);
		jp.AddObject("posctl_filter", posctl.GetFilter());

		posctl.UnLockMutex();

		jp.GetValueInt("histogram_log", &i);
		SetHistogramLog(i);

		jp.GetValueInt("debayer_mode", &i);
		SetDebayerMode(i);

		if (jp.GetValue("destination_path", &destpath)) {
			GtkWidget *entry = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "input_entry_destpath"));
			gtk_entry_set_text(GTK_ENTRY(entry), destpath.c_str());
		}

		//
		// start streaming if a device was selected.
		//
		if (jp.GetValue("device", &vstr)) {
			GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videodev"));
			GtkWidget *cbdevice = gtk_bin_get_child(GTK_BIN(cbox));
			gtk_entry_set_text(GTK_ENTRY(cbdevice), vstr.c_str());
		}

		//
		// load the button config
		//
		for (i = 0; i < BTN_PRESET_MAX; i++) {
			VideoDevCtrl ctrl;
			JSONParse jpbtn, jpctrl;
			list<JSONElement> je;
			list<JSONElement>::iterator iter;
			int ctrli;
			int64_t id;

			//
			// load all controls from JSONParse object
			presetbtn[i].clear();
			ctrli = 0;
			while (jp.GetObjectIdx("presetbtn"+ std::to_string(i), ctrli, &jpctrl)) {
				jpctrl.GetValue("name", &ctrl.name);
				jpctrl.GetValueInt64("id", &id);
				ctrl.id = id;
				jpctrl.GetValueInt("min", &ctrl.min);
				jpctrl.GetValueInt("max", &ctrl.max);
				jpctrl.GetValueInt("value", &ctrl.value);
				presetbtn[i].push_back(ctrl);
				ctrli++;
			}
		}


		//
		// check is the windows are inside the screen
		// not really needed since gnome seem to take care about the windows positions
		// and placement quite well.
		GdkDisplay *dsp = gdk_display_get_default();
		int n_monitor = -1;
		if (dsp) {
			n_monitor = gdk_display_get_n_monitors (dsp);
		}

		if (dsp && n_monitor > 0) {
			GdkMonitor *g_monitor;
			GdkRectangle geometry;
			for (i = 0; i < n_monitor; i++) {
				g_monitor = gdk_display_get_monitor(dsp, i);
				if (g_monitor) {
					gdk_monitor_get_geometry(g_monitor, &geometry);
					printf ("%s:%d Monitor %d Pos:%d,%d  Size:%d x %d\n", __FILE__, __LINE__, i,
							geometry.x, geometry.y, geometry.width, geometry.height);
				}
			}
		}

		//
		// load windows position
		for (i = 0; i < 6; i++) {
			int x, y, w, h, show;
			string name;

			switch (i) {
			case 0:
				name = "window-main";
				break;
			case 1:
				name = "window-input";
				break;
			case 2:
				name = "window-output";
				break;
			case 3:
				name = "window-detect";
				break;
			case 4:
				name = "window-histogram";
				break;
			case 5:
				name = "window-posctl";
				break;
			}

			if (jp.GetValueInt(name+"_x", &x) && jp.GetValueInt(name+"_y", &y) &&
				jp.GetValueInt(name+"_w", &w) && jp.GetValueInt(name+"_h", &h) &&
				jp.GetValueInt(name+"_show", &show)) {

				GtkWidget *wnd = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), name.c_str()));
				if (wnd) {
					printf ("%s:%d window '%s' set geometry %d,%d : %d x %d\n", __FILE__, __LINE__, name.c_str(), x, y, w, h);
					gtk_window_move (GTK_WINDOW(wnd), x, y);
					gtk_window_resize (GTK_WINDOW(wnd), w, h);
					gtk_widget_show(wnd);
					// g_object_set (wnd, "visible", &show, NULL);
				}
			}
		}
	}

	setlocale (LC_ALL, s.c_str());

	// we need to update the gui so floating numbers will displayed with the correct
	// language setting.
	gdk_threads_add_idle(cb_thread_posctl, NULL);
};


/*
 *  presetbtn[btn]
 */
void Configuration::SetPresetButton (int btn, list<VideoDevCtrl> *parameters) {
	if (btn < 0 || btn >= BTN_PRESET_MAX) return;
	presetbtn[btn] = *parameters;
};


list<VideoDevCtrl> Configuration::GetPresetButton (int btn) {
	list<VideoDevCtrl> emptylist;
	if (btn >= 0 && btn < BTN_PRESET_MAX) return presetbtn[btn];
	return emptylist;
};


void Configuration::SetHistogramLog(int trueorfalse) {
	GtkWidget *cfg_logh = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "menu-settings-loghistogram"));
	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(cfg_logh), (trueorfalse == 1));
	histogram_log = trueorfalse;
};



void Configuration::SetDebayerMode(int trueorfalse) {
	GtkWidget *cfg_debayer = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "menu-settings-bilinearrgb"));
	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(cfg_debayer), (trueorfalse == 1));
	debayer_mode = trueorfalse;
};


