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


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


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

// global
s_breakpoint breakpoints[MAX_BREAKPOINTS];

// local
GtkWidget *bp_add_button, *bp_remove_button, *bp_removeall_button, *bp_address_text;
GtkCList *bp_list;
int selected_bp;
uint nr_breakpoints;
s_bgroup *bp_type;

#define BP_COLUMN_TITLES 3
gchar *bp_column_titles[] = { "Last Stop", "Type", "Breakpoint" };

char *bp_titles[] = { " Physical ", " Linear ", " Virtual " };

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


void read_breakpoints( ){
	char scan, c;
	uint n;

	for(n = 0; n < MAX_BREAKPOINTS; n++) breakpoints[n].enabled = 0;
	nr_breakpoints = 0;

	fprintf(writepipe, "info break\n");

	// remove the "Num Type Disp Enb Address"
	c = fgetc(readpipe);
	for(; !isdigit(c) && (c != '<') && (c != EOF); c = fgetc(readpipe));
	if(c == EOF) return;
	if(ungetc(c, readpipe) == EOF) return;

	for(n = 0; n < MAX_BREAKPOINTS; n++){
		c = fgetc(readpipe);
		for(; isspace(c) && (c != EOF); c = fgetc(readpipe));
		if(c == EOF) return;
		if((ungetc(c, readpipe) == EOF) || !isdigit(c)) break;

		if(fscanf(readpipe, "%u %c%*s ", &breakpoints[n].number, &scan) != 2) break;
		switch(scan){
		case 'p':
			breakpoints[n].type = BP_PHYSICAL;
			break;
		case 'l':
			breakpoints[n].type = BP_LINEAR;
			break;
		case 'v':
			breakpoints[n].type = BP_VIRTUAL;
			break;
		default:
			prompt_read();
			return;
		}
		if(fscanf(readpipe, "%*s %c ", &scan) != 1) break;
		breakpoints[n].enabled = (scan == 'y');
		if(fscanf(readpipe, "%x", & breakpoints[n].segment) != 1) break;
		if(fscanf(readpipe, ":%x", &breakpoints[n].offset) < 1){
			breakpoints[n].offset  = breakpoints[n].segment;
			breakpoints[n].segment = 0;
		}

		nr_breakpoints++;
	}

	prompt_read();
	return;
}



void add_breakpoint( GtkWidget *widget, gpointer data ){
	const char *address;

	address = gtk_entry_get_text(GTK_ENTRY(bp_address_text));

	switch(bp_type->current){
	case BP_PHYSICAL:
		fprintf(writepipe, "pb %s\n", address);
		break;
	case BP_LINEAR:
		fprintf(writepipe, "lb %s\n", address);
		break;
	case BP_VIRTUAL:
		fprintf(writepipe, "vb %s\n", address);
		break;
	}
	prompt_read();
	if((int)data) breakpointsUpdate(BP_UPDATE);
	gtk_entry_set_text(GTK_ENTRY(bp_address_text), "");
}


void remove_breakpoint( GtkWidget *widget, gpointer data ){
	fprintf(writepipe, "d %u\n", breakpoints[selected_bp].number);
	prompt_read();
	breakpointsUpdate(BP_UPDATE);
}


void removeall_breakpoints( GtkWidget *widget, gpointer data ){
	uint x;

	for(x = 0; x < MAX_BREAKPOINTS; x++){
		if(breakpoints[0].enabled){
			fprintf(writepipe, "d %u\n", breakpoints[0].number);
			prompt_read();
			breakpointsUpdate(BP_UPDATE);
		} else break;
	}
}


void bp_selected( GtkWidget *widget, gint row, gint column, GdkEventButton *event, gpointer data ){
	selected_bp = row;
	gtk_widget_set_sensitive(bp_remove_button, TRUE);
}


void bp_unselected( GtkWidget *widget, gint row, gint column, GdkEventButton *event, gpointer data ){
	selected_bp = -1;
	gtk_widget_set_sensitive(bp_remove_button, FALSE);
}


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


void breakpointsInit( GtkWidget *vbox ){
	GtkWidget *hbox, *vbox2;

	hbox = new_hbox(vbox, TRUE);

	bp_list = new_list(hbox, BP_COLUMN_TITLES, bp_column_titles);
	gtk_clist_column_titles_show(bp_list);
	gtk_clist_set_column_justification(bp_list, 0, GTK_JUSTIFY_CENTER);
	gtk_clist_set_column_justification(bp_list, 1, GTK_JUSTIFY_CENTER);
	gtk_clist_set_column_justification(bp_list, 2, GTK_JUSTIFY_LEFT);

	// setup selection handler
	gtk_signal_connect(GTK_OBJECT(bp_list), "select_row", GTK_SIGNAL_FUNC(bp_selected), NULL);
	gtk_signal_connect(GTK_OBJECT(bp_list), "unselect_row", GTK_SIGNAL_FUNC(bp_unselected), NULL);

	vbox2 = new_vbox(hbox, FALSE);
	hbox = new_hbox(vbox2, FALSE);

	// remove button
	bp_remove_button = new_button(hbox, TRUE, " Remove ");
	gtk_signal_connect(GTK_OBJECT(bp_remove_button), "clicked", GTK_SIGNAL_FUNC(remove_breakpoint), NULL);

	// remove all button
	bp_removeall_button = new_button(hbox, TRUE, " Remove All ");
	gtk_signal_connect(GTK_OBJECT(bp_removeall_button), "clicked", GTK_SIGNAL_FUNC(removeall_breakpoints), NULL);

	new_separator(vbox2, FALSE, HORIZONTAL);

	hbox = new_hbox(vbox2, FALSE);

	bp_type = new_button_group(hbox, TRUE, HORIZONTAL, 3, bp_titles);

	// address text field
	bp_address_text = new_text_entry(vbox2, FALSE, 145);
	gtk_signal_connect(GTK_OBJECT(bp_address_text), "activate", GTK_SIGNAL_FUNC(add_breakpoint), (gpointer)1);

	// add button
	bp_add_button = new_button(vbox2, FALSE, " Add ");
	gtk_signal_connect(GTK_OBJECT(bp_add_button), "clicked", GTK_SIGNAL_FUNC(add_breakpoint), (gpointer)1);

	selected_bp = -1;
	nr_breakpoints = 0;
}


void breakpointsUpdate( e_bp_mode mode ){
	const char *p_type = "Physical", *l_type = "Linear", *v_type = "Virtual";
	char address[LEN_ADDRESS], *row[BP_COLUMN_TITLES];
	uint x;

	if(bochs_offline) return;

	if(mode == BP_UPDATE) read_breakpoints();

	gtk_clist_freeze(bp_list);
	gtk_clist_clear(bp_list);
	selected_bp = -1;
	gtk_widget_set_sensitive(bp_remove_button, FALSE);
	if(nr_breakpoints) gtk_widget_set_sensitive(bp_removeall_button, TRUE);
	else gtk_widget_set_sensitive(bp_removeall_button, FALSE);

	row[0] = NULL;
	// row[1] is set below
	row[2] = address;

	for(x = 0; (x < MAX_BREAKPOINTS) && breakpoints[x].enabled; x++){
		switch(breakpoints[x].type){
		case BP_PHYSICAL:
			row[1] = (char *)p_type;
			snprintf(address, LEN_ADDRESS, "%.8X", breakpoints[x].offset);
			break;
		case BP_LINEAR:
			row[1] = (char *)l_type;
			snprintf(address, LEN_ADDRESS, "%.8X", breakpoints[x].offset);
			break;
		case BP_VIRTUAL:
			row[1] = (char *)v_type;
			snprintf(address, LEN_ADDRESS, "%.4X:%.8X",
				breakpoints[x].segment, breakpoints[x].offset);
			break;
		}
		gtk_clist_append(bp_list, row);
	}
	gtk_clist_thaw(bp_list);
}


void breakpointsReload( ){
	char address[LEN_ADDRESS];
	uint x, old_bp_type;

	if(reload_breakpoints){
		old_bp_type = bp_type->current;

		for(x = 0; (x < MAX_BREAKPOINTS) && breakpoints[x].enabled; x++){
			switch(breakpoints[x].type){
			case BP_PHYSICAL:
			case BP_LINEAR:
				snprintf(address, LEN_ADDRESS, "0x%.8X", breakpoints[x].offset);
				break;
			case BP_VIRTUAL:
				snprintf(address, LEN_ADDRESS, "0x%.4X:0x%.8X",
					breakpoints[x].segment, breakpoints[x].offset);
				break;
			}
			bp_type->current = breakpoints[x].type;
			gtk_entry_set_text(GTK_ENTRY(bp_address_text), address);
			add_breakpoint(bp_address_text, (gpointer)0);
		}

		bp_type->current = old_bp_type;
	}

	breakpointsUpdate(BP_UPDATE);
}


void breakpointsEnablePage( gboolean v ){
	gtk_widget_set_sensitive(GTK_WIDGET(bp_list), v);
	gtk_widget_set_sensitive(bp_add_button, v);
	if(selected_bp != -1) gtk_widget_set_sensitive(bp_remove_button, v);
	else gtk_widget_set_sensitive(bp_remove_button, FALSE);
	if(nr_breakpoints) gtk_widget_set_sensitive(bp_removeall_button, v);
	else gtk_widget_set_sensitive(bp_removeall_button, FALSE);
	gtk_widget_set_sensitive(bp_address_text, v);
}


void breakpointsSelect( uint bp ){
	gtk_clist_set_text(bp_list, bp, 0, "X");
	gtk_clist_select_row(bp_list, bp, 0);
}
