//	bfe2 - preferences
//	Copyright (c) 1999-2003 Brand Huntsman and Lee Salzman
//


#include "common.h"
#include "functions.h"


//////////////////////////////////////////////////////////////////////////

// global
uint h_maxlen, stack_above, stack_below, tracing, steplock_time, nextlock_time;
uint reload_breakpoints, reload_watchpoints;

// local
GtkWidget *hislen_text, *stackabove_text, *stackbelow_text, *steplock_text, *nextlock_text;
GtkWidget *trace_button;

s_bgroup *page_tab;
char *tab_titles[] = { " Bottom ", " Top ", " Left ", " Right " };

//////////////////////////////////////////////////////////////////////////


uint find_char( char *buffer, uint *i, char m ){
	char c;

	do {
		c = buffer[*i];
		if(buffer[*i] == m) return(1);
		(*i)++;
	} while(c != '\0');
	return(0);
}


uint get_structure( char *buffer, uint *i, uint type ){
	s_structwin *s;
	s_field *field;
	char *word;

	if((s = (s_structwin *)malloc(sizeof(s_structwin))) == NULL)
		return(0);

	if(get_word(buffer, i, &word) == 0) return(0);
	if(strlen(word) >= LEN_STRUCT_NAME) return(0);
	strcpy(s->name, word);						// STRUCT: name
	if(get_word(buffer, i, &word) == 0) return(0);
	if(sscanf(word, "%x", &s->base) != 1) return(0);		// STRUCT: base

	if(type){
		s->nr_elts = 1;						// STRUCT: nr_elts
	} else {
		if(get_word(buffer, i, &word) == 0) return(0);
		if(sscanf(word, "%u", &s->nr_elts) != 1) return(0);	// LIST: nr_elts
	}

	s->nr_fields = 0;
	s->fields_head = s->fields_tail = NULL;
	for(;;){
		if(get_word(buffer, i, &word) == 0) return(0);
		if(strlen(word) >= LEN_FIELD_NAME) return(0);
		if(word[0] == '}' && word[1] == '\0') break;

		if((field = (s_field *)malloc(sizeof(s_field))) == NULL)
			return(0);

		strcpy(field->name, word);				// FIELD: name
		if(get_word(buffer, i, &word) == 0) return(0);
		if(sscanf(word, "%u", &field->size) != 1) return(0);	// FIELD: size

		// add field to list
		field->next = NULL;
		if(s->fields_head == NULL){
			s->fields_head = field;
		} else {
			field->prev = s->fields_tail;
			s->fields_tail->next = field;
		}
		s->fields_tail = field;

		s->nr_fields++;
	}

	s->window = NULL;

	// add structure to list
	s->next = NULL;
	if(structwin_head == NULL){
		structwin_head = s;
	} else {
		s->prev = structwin_tail;
		structwin_tail->next = s;
	}
	structwin_tail = s;

	return(1);
}


void load_prefs( ){
	FILE *file;
	struct stat stat_buffer;
	char *buffer, *word;
	uint i, tabs;

	if((file = fopen(RC_PATH, "r")) == NULL){
		g_print("BFE: %s file not found, using defaults\n", RC_PATH);
		return;
	}
	stat(RC_PATH, &stat_buffer);
	if((buffer = (char *)malloc(stat_buffer.st_size + 1)) == NULL){
		g_print("BFE: Couldn't process %s file, using defaults\n", RC_PATH);
		return;
	}
	i = 0;
	while(!feof(file)){
		buffer[i] = getc(file);
		i++;
	}
	buffer[i-1] = '\0';
	fclose(file);

	i = 0;
	for(;;){
		// find beginning of a block
		if(!find_char(buffer, &i, '{')) return;
		i++;
		if(get_word(buffer, &i, &word) == 0) return;

		if(string_compare(word, "history")){
			if(get_word(buffer, &i, &word) == 0) return;
			if(sscanf(word, "%u", &h_maxlen) != 1) return;		// HISTORY: max length
		} else if(string_compare(word, "stack")){
			if(get_word(buffer, &i, &word) == 0) return;
			if(sscanf(word, "%u", &stack_above) != 1) return;	// STACK: above

			if(get_word(buffer, &i, &word) == 0) return;
			if(sscanf(word, "%u", &stack_below) != 1) return;	// STACK: below
		} else if(string_compare(word, "tabs")){
			if(get_word(buffer, &i, &word) == 0) return;
			if(sscanf(word, "%u", &tabs) != 1) return;		// TABS: position
			set_bgroup(page_tab, tabs);
		} else if(string_compare(word, "reload")){
			if(get_word(buffer, &i, &word) == 0) return;
			if(sscanf(word, "%u", &reload_breakpoints) != 1) return;	// RELOAD: breakpoints

			if(get_word(buffer, &i, &word) == 0) return;
			if(sscanf(word, "%u", &reload_watchpoints) != 1) return;	// RELOAD: watchpoints
		} else if(string_compare(word, "struct")){
			if(get_structure(buffer, &i, 1) == 0) return;
		} else if(string_compare(word, "list")){
			if(get_structure(buffer, &i, 0) == 0) return;
		} else {
			g_print("BFE: Unknown block type '%s' in %s\n", word, RC_PATH);
			return;
		}
	}
}


void toggle_tracing( GtkWidget *widget, gpointer data ){
	if(tracing){
		tracing = 0;
		historyWrite("--- TRACE DISABLED ---");
		fprintf(writepipe, "trace-off\n");
		prompt_read();
	} else {
		tracing = 1;
		historyRepeat();
		historyReset();
		historyWrite("--- TRACE ENABLED ---");
		fprintf(writepipe, "trace-on\n");
		prompt_read();
	}
}


void toggle_bp( GtkWidget *widget, gpointer data ){
	if(reload_breakpoints) reload_breakpoints = 0;
	else reload_breakpoints = 1;
}
void toggle_wp( GtkWidget *widget, gpointer data ){
	if(reload_watchpoints) reload_watchpoints = 0;
	else reload_watchpoints = 1;
}


void update_text( GtkWidget *text, uint value ){
	char n[LEN_NUMBER];

	snprintf(n, LEN_NUMBER, "%u", value);
	gtk_entry_set_text(GTK_ENTRY(text), n);
}
void set_cfg_hislen( GtkWidget *widget, gpointer data ){
	h_maxlen = atoi(gtk_entry_get_text(GTK_ENTRY(hislen_text)));
	if(h_maxlen < 1) h_maxlen = 1;
	historyClip();
	update_text(widget, h_maxlen);
}
void set_cfg_stackabove( GtkWidget *widget, gpointer data ){
	stack_above = atoi(gtk_entry_get_text(GTK_ENTRY(stackabove_text)));
	if(stack_above < 0) stack_above = 0;
	stateUpdateStack();
	update_text(widget, stack_above);
}
void set_cfg_stackbelow( GtkWidget *widget, gpointer data ){
	stack_below = atoi(gtk_entry_get_text(GTK_ENTRY(stackbelow_text)));
	if(stack_below < 0) stack_below = 0;
	stateUpdateStack();
	update_text(widget, stack_below);
}
void set_cfg_steplock( GtkWidget *widget, gpointer data ){
	steplock_time = atoi(gtk_entry_get_text(GTK_ENTRY(steplock_text)));
	if(steplock_time < 1) steplock_time = 1;
	update_text(widget, steplock_time);
}
void set_cfg_nextlock( GtkWidget *widget, gpointer data ){
	nextlock_time = atoi(gtk_entry_get_text(GTK_ENTRY(nextlock_text)));
	if(nextlock_time < 1) nextlock_time = 1;
	update_text(widget, nextlock_time);
}


void toggle_page_tabs( s_bgroup *group ){
	switch(group->current){
	case 0:
		gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM);
		break;
	case 1:
		gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
		break;
	case 2:
		gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
		break;
	case 3:
		gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_RIGHT);
		break;
	}
}


static GtkWidget *new_entry_and_label( GtkWidget *box, char *name, uint maxlen, void (*callback)(), uint width ){
	GtkWidget *hbox, *text;

	hbox = new_hbox(box, FALSE);

	text = new_text_entry(hbox, FALSE, width);
	gtk_entry_set_max_length(GTK_ENTRY(text), maxlen);
	gtk_entry_set_editable(GTK_ENTRY(text), TRUE);
	gtk_signal_connect(GTK_OBJECT(text), "activate", GTK_SIGNAL_FUNC(callback), NULL);
//	gtk_signal_connect(GTK_OBJECT(text), "changed", GTK_SIGNAL_FUNC(callback), NULL);

	new_label(hbox, FALSE, name, FALSE);

	return(text);
}


//////////////////////////////////////////////////////////////////////////


void prefsInit( GtkWidget *vbox ){
	GtkWidget *hbox, *vbox2, *button, *rbp_button, *rwp_button;
	char n[LEN_NUMBER];

	hbox = new_hbox(vbox, FALSE);

	vbox2 = new_vbox(hbox, FALSE);

	hislen_text = new_entry_and_label(vbox2, "Instruction History Length", 5, set_cfg_hislen, 75);
	stackabove_text = new_entry_and_label(vbox2, "Stack values above ESP", 4, set_cfg_stackabove, 75);
	stackbelow_text = new_entry_and_label(vbox2, "Stack values below ESP", 4, set_cfg_stackbelow, 75);
	steplock_text = new_entry_and_label(vbox2, "Step-Lock Time (seconds)", 3, set_cfg_steplock, 75);
	nextlock_text = new_entry_and_label(vbox2, "Next-Lock Time (seconds)", 3, set_cfg_nextlock, 75);

	/////////////////////////////////////////////

	new_separator(hbox, FALSE, VERTICAL);

	/////////////////////////////////////////////

	vbox2 = new_vbox(hbox, FALSE);

	button = new_button(vbox2, FALSE, "Clear History");
	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(historyClear), NULL);

	trace_button = new_check_button(vbox2, FALSE, "Enable instruction tracing", FALSE);
	gtk_signal_connect(GTK_OBJECT(trace_button), "clicked", GTK_SIGNAL_FUNC(toggle_tracing), NULL);

	rbp_button = new_check_button(vbox2, FALSE, "Reload breakpoints when restarting Bochs", TRUE);
	gtk_signal_connect(GTK_OBJECT(rbp_button), "clicked", GTK_SIGNAL_FUNC(toggle_bp), NULL);

	rwp_button = new_check_button(vbox2, FALSE, "Reload watchpoints when restarting Bochs", TRUE);
	gtk_signal_connect(GTK_OBJECT(rwp_button), "clicked", GTK_SIGNAL_FUNC(toggle_wp), NULL);

	hbox = new_hbox(vbox2, FALSE);
	new_label(hbox, FALSE, "Page tabs", FALSE);
	page_tab = new_button_group(hbox, FALSE, HORIZONTAL, 4, tab_titles);
	page_tab->callback = toggle_page_tabs;

	/////////////////////////////////////////////

	// defaults settings
	h_maxlen = DEF_HISLEN;
	stack_above = DEF_STACKABOVE;
	stack_below = DEF_STACKBELOW;
	steplock_time = DEF_STEPLOCK;
	nextlock_time = DEF_NEXTLOCK;
	tracing = 0;
	reload_breakpoints = DEF_RELOAD_BP;
	reload_watchpoints = DEF_RELOAD_WP;
	set_bgroup(page_tab, DEF_PAGE_TAB);
	structwin_head = structwin_tail = NULL;

	load_prefs();

	snprintf(n, LEN_NUMBER, "%u", h_maxlen);
	gtk_entry_set_text(GTK_ENTRY(hislen_text), n);
	snprintf(n, LEN_NUMBER, "%u", stack_above);
	gtk_entry_set_text(GTK_ENTRY(stackabove_text), n);
	snprintf(n, LEN_NUMBER, "%u", stack_below);
	gtk_entry_set_text(GTK_ENTRY(stackbelow_text), n);
	snprintf(n, LEN_NUMBER, "%u", steplock_time);
	gtk_entry_set_text(GTK_ENTRY(steplock_text), n);
	snprintf(n, LEN_NUMBER, "%u", nextlock_time);
	gtk_entry_set_text(GTK_ENTRY(nextlock_text), n);
	if(reload_breakpoints){
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rbp_button), TRUE);
		reload_breakpoints = 1;
	} else {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rbp_button), FALSE);
		reload_breakpoints = 0;
	}
	if(reload_watchpoints){
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rwp_button), TRUE);
		reload_watchpoints = 1;
	} else {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rwp_button), FALSE);
		reload_watchpoints = 0;
	}
}


void prefsEnablePage( gboolean v ){
	gtk_widget_set_sensitive(trace_button, v);
}
