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


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


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

// global
s_structwin *structwin_head, *structwin_tail;

// local
GtkCList *s_list;

#define STRUCT_COLUM_TITLES 5
gchar *struct_column_titles[] = { "Open", "Base", "Elements", "Fields", "Name" };
#define SWIN_COLUM_TITLES 3
gchar *swin_column_titles[] = { "Base", "Field", "Value" };
#define LWIN_COLUM_TITLES 4
gchar *lwin_column_titles[] = { "Base", "Index", "Field", "Value" };

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


void refresh_structure( GtkWidget *widget, gpointer data ){
	structuresUpdateWindow((s_structwin *)data);
}


void s_destroy_window( GtkWidget *widget, gpointer data ){
	s_structwin *s;

	s = (s_structwin *)gtk_clist_get_row_data(s_list, (int)data);

	gtk_widget_destroy(s->window);
	s->window = NULL;

	gtk_clist_set_text(s_list, (int)data, 0, NULL);
}
#define LEN_STRUCTWIN_TITLE LEN_STRUCT_NAME + 32
void open_structure( GtkWidget *widget, gint row, gint column, GdkEventButton *event, gpointer data ){
	GtkWidget *vbox, *hbox;
	char title[LEN_STRUCTWIN_TITLE], n[LEN_NUMBER];
	s_structwin *s;

	s = (s_structwin *)gtk_clist_get_row_data(s_list, row);

	gtk_clist_unselect_row(s_list, row, column);

	if(s->window) return;

	// create window
	if(s->nr_elts == 1)
		snprintf(title, LEN_STRUCTWIN_TITLE, "BFE - Structure - %s", s->name);
	else snprintf(title, LEN_STRUCTWIN_TITLE, "BFE - List - %s", s->name);
	s->window = new_window(GTK_WINDOW_TOPLEVEL, title, 400, 300);
	gtk_signal_connect(GTK_OBJECT(s->window), "destroy", GTK_SIGNAL_FUNC(s_destroy_window), (gpointer)row);
	vbox = new_window_vbox(s->window);

	// create list
	if(s->nr_elts > 1){
		// list
		s->list = new_list(vbox, LWIN_COLUM_TITLES, lwin_column_titles);
		gtk_clist_set_column_justification(s->list, 0, GTK_JUSTIFY_CENTER);
		gtk_clist_set_column_justification(s->list, 1, GTK_JUSTIFY_CENTER);
		gtk_clist_set_column_justification(s->list, 2, GTK_JUSTIFY_RIGHT);
		gtk_clist_set_column_justification(s->list, 3, GTK_JUSTIFY_LEFT);
	} else {
		// structure
		s->list = new_list(vbox, SWIN_COLUM_TITLES, swin_column_titles);
		gtk_clist_set_column_justification(s->list, 0, GTK_JUSTIFY_CENTER);
		gtk_clist_set_column_justification(s->list, 1, GTK_JUSTIFY_RIGHT);
		gtk_clist_set_column_justification(s->list, 2, GTK_JUSTIFY_LEFT);
	}
	gtk_clist_column_titles_show(s->list);

	hbox = new_hbox(vbox, FALSE);

	if(s->nr_elts > 1){
		new_label(hbox, FALSE, "Start", FALSE);
		s->start_text = new_text_entry(hbox, FALSE, 50);
		gtk_entry_set_text(GTK_ENTRY(s->start_text), "0");
		gtk_entry_set_max_length(GTK_ENTRY(s->start_text), 6);

		new_label(hbox, FALSE, "Total", FALSE);
		s->total_text = new_text_entry(hbox, FALSE, 50);
		snprintf(n, LEN_NUMBER, "%u", s->nr_elts);
		gtk_entry_set_text(GTK_ENTRY(s->total_text), n);
		gtk_entry_set_max_length(GTK_ENTRY(s->total_text), 6);
	} else {
		new_gap(hbox);
	}

	s->refresh_button = new_button(hbox, TRUE, " Refresh ");
	gtk_signal_connect(GTK_OBJECT(s->refresh_button), "clicked", GTK_SIGNAL_FUNC(refresh_structure), (gpointer)s);

	s->close_button = new_button(hbox, FALSE, " Close ");
	gtk_signal_connect(GTK_OBJECT(s->close_button), "clicked", GTK_SIGNAL_FUNC(s_destroy_window), (gpointer)row);

	gtk_clist_set_text(s_list, row, 0, "X");

	structuresUpdateWindow(s);
}


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


void structuresInit( GtkWidget *vbox ){
	s_list = new_list(vbox, STRUCT_COLUM_TITLES, struct_column_titles);
	gtk_clist_column_titles_show(s_list);
	gtk_clist_set_column_justification(s_list, 0, GTK_JUSTIFY_CENTER);
	gtk_clist_set_column_justification(s_list, 1, GTK_JUSTIFY_CENTER);
	gtk_clist_set_column_justification(s_list, 2, GTK_JUSTIFY_RIGHT);
	gtk_clist_set_column_justification(s_list, 3, GTK_JUSTIFY_RIGHT);
	gtk_clist_set_column_justification(s_list, 4, GTK_JUSTIFY_LEFT);

	// setup selection handler
	gtk_signal_connect(GTK_OBJECT(s_list), "select_row", GTK_SIGNAL_FUNC(open_structure), NULL);

	// structwin_head and structwin_tail are set in prefs.c
}


void structuresUpdate( ){
	char base[LEN_ADDRESS], elts[LEN_NUMBER], fields[LEN_NUMBER], *row[STRUCT_COLUM_TITLES];
	s_structwin *s;
	uint r;

	row[0] = NULL;
	row[1] = base;
	row[2] = elts;
	row[3] = fields;
	// row[4] is set below

	gtk_clist_freeze(s_list);
	gtk_clist_clear(s_list);

	r = 0;
	for(s = structwin_head; s != NULL; s = s->next){
		snprintf(base, LEN_ADDRESS, "%.8X", s->base);
		snprintf(elts, LEN_NUMBER, "%u", s->nr_elts);
		snprintf(fields, LEN_NUMBER, "%u", s->nr_fields);
		row[4] = s->name;
		gtk_clist_append(s_list, row);
		gtk_clist_set_row_data(s_list, r, (gpointer)s);
		r++;
	}
	gtk_clist_thaw(s_list);
}


#define LEN_VALUE 20
void structuresUpdateWindow( s_structwin *s ){
	char base[LEN_ADDRESS], n[LEN_NUMBER], value[LEN_VALUE];
	char *row0[LWIN_COLUM_TITLES], *row1[LWIN_COLUM_TITLES];
	uint x, size, offset, start, total;
	uint32 val, val1;
	s_field *f;

	if(bochs_offline) return;
	if(s->window == NULL) return;

	if(s->nr_elts > 1){
		if(sscanf(gtk_entry_get_text(GTK_ENTRY(s->start_text)), "%u", &start) != 1)
			start = 0;
		if(sscanf(gtk_entry_get_text(GTK_ENTRY(s->total_text)), "%u", &total) != 1)
			total = s->nr_fields;

		if(total < 1 || total > s->nr_elts){
			total = s->nr_elts;
			snprintf(n, LEN_NUMBER, "%u", total);
			gtk_entry_set_text(GTK_ENTRY(s->total_text), n);
		}
		if(start < 0 || start >= s->nr_elts){
			start = 0;
			gtk_entry_set_text(GTK_ENTRY(s->start_text), "0");
		}
		if(start + total > s->nr_elts){
			total = s->nr_elts - start;
			snprintf(n, LEN_NUMBER, "%u", total);
			gtk_entry_set_text(GTK_ENTRY(s->total_text), n);
		}
	} else {
		start = 0;
		total = 1;
	}

	for(f = s->fields_head, size = 0; f != NULL; f = f->next) size += f->size;

	row0[0] = base;
	if(s->nr_elts == 1) row0[1] = NULL;
	else row0[1] = n;
	row0[2] = NULL;
	row0[3] = NULL;

	row1[0] = NULL;
	if(s->nr_elts == 1){
		// row1[1] is set below
		row1[2] = value;
		row1[3] = NULL;
	} else {
		row1[1] = NULL;
		// row1[2] is set below
		row1[3] = value;
	}

	gtk_clist_freeze(s->list);
	gtk_clist_clear(s->list);

	for(x = start; x < start + total; x++){
		offset = 0;

		snprintf(base, LEN_ADDRESS, "%.8X", s->base + (x * size));
		if(s->nr_elts > 1) snprintf(n, LEN_NUMBER, "(%u)", x);
		gtk_clist_append(s->list, row0);

		for(f = s->fields_head; f != NULL; f = f->next){
		   if(f->name[0] == '\\' && f->name[1] == '\\'){
			if(s->nr_elts == 1) row1[1] = NULL;
			else row1[2] = NULL;
			value[0] = '\0';
			gtk_clist_append(s->list, row1);
		   } else {
			if(s->nr_elts == 1) row1[1] = f->name;
			else row1[2] = f->name;
			switch(f->size){
			case 1:
				fprintf(writepipe, "x /1bx 0x%X\n", s->base + (x * size) + offset);
				fscanf(readpipe, " %*s %*s 0x%X ", &val);
				snprintf(value, LEN_VALUE, "= %.2X", val);
				break;
			case 2:
				fprintf(writepipe, "x /1hx 0x%X\n", s->base + (x * size) + offset);
				fscanf(readpipe, " %*s %*s 0x%X ", &val);
				snprintf(value, LEN_VALUE, "= %.4X", val);
				break;
			case 4:
				fprintf(writepipe, "x /1wx 0x%X\n", s->base + (x * size) + offset);
				fscanf(readpipe, " %*s %*s 0x%X ", &val);
				snprintf(value, LEN_VALUE, "= %.8X", val);
				break;
			case 8:
				fprintf(writepipe, "x /2wx 0x%X\n", s->base + (x * size) + offset);
				fscanf(readpipe, " %*s %*s 0x%X 0x%X", &val, &val1);
				snprintf(value, LEN_VALUE, "= %.8X%.8X", val1, val);
				break;
			default:
				g_print("BFE: encountered a structure value with a non power of 2 size, or a size larger than 8 bytes.\n");
				snprintf(value, LEN_VALUE, "<invalid size>");
			}

			prompt_read();
			offset += f->size;
			gtk_clist_append(s->list, row1);
		   }
		}
	}
	gtk_clist_thaw(s->list);
}


void structuresEnablePage( gboolean v ){
	s_structwin *s;

	gtk_widget_set_sensitive(GTK_WIDGET(s_list), v);
	for(s = structwin_head; s != NULL; s = s->next)
		if(s->window) gtk_widget_set_sensitive(s->refresh_button, v);
}
