#include "huc.h"

/* Menu titles */
#define BRAM_SCREEN_MENU_TITLE "BRAMBackup Tool - J.Snowdon (2014)"
#define BRAM_SCREEN_BRAM_TITLE "BRAM Info"
#define BRAM_SCREEN_SYS_TITLE "System Info"

/* Screen numbers - these should be contiguous */
#define BRAM_SCREEN_MENU 	0
#define BRAM_SCREEN_BRAM 	1
#define BRAM_SCREEN_SYS 	2

/* These should always match the upper and lower screen numbers */
#define SCREEN_NUM_MIN 		0
#define SCREEN_NUM_MAX 	2

/* Screen lines and columns */
#define SCREEN_HEIGHT 		32
#define SCREEN_WIDTH 		64

/* Maximum number of text lines */
#define SCREEN_MAX_LINES 	27

/* Number of files shown in the BRAM browser */
#define SCREEN_MAX_BRAM_FILES_SHOWN 	7

/* Width of the BRAM browser */
#define BRAM_FILE_MENU_WIDTH 	27

/* max size of a save file and therefore size of buffer to hold it */
#define MAX_BRAM_FILE_SIZE 	2000
#define MAX_BRAM_XFER_SIZE		62

/* enable/disable debug mode - display value of variable etc */
#define DEBUG

put_string_long(charbuf) char charbuf;{
	/* display a string that is too long to fit on one line - wrap to the next */
}

put_joy(joy_input) int joy_input; {
	/* print joystick inputs */
	
	switch(joy_input) {
		case JOY_UP:
			put_string("JOY_UP  ", 54, 0);		
			break;
		case JOY_DOWN:
			put_string("JOY_DOWN", 54, 0);
			break;
		case JOY_LEFT:
			put_string("JOY_LEFT", 54, 0);
			break;
		case JOY_RGHT:
			put_string("JOY_RGHT", 54, 0);
			break;
	}
	
}

init_screen(){
	set_screen_size(SCR_SIZE_64x64);
	set_xres(512, XRES_SHARP);
	set_color_rgb(1, 7, 7, 7);
	set_font_color(1, 0);
	set_font_pal(0);
	load_default_font();
}

show_bottom_menu(cs) char cs;{
	/* Show the bottom menu with the current active screen highlighted */
		
	switch(cs) {
		case BRAM_SCREEN_BRAM:
			put_string("- Menu * BRAM - System", 2, SCREEN_MAX_LINES);
			break;
		case BRAM_SCREEN_SYS:
			put_string("- Menu - BRAM * System", 2, SCREEN_MAX_LINES);
			break;
		case BRAM_SCREEN_MENU:
			put_string("* Menu - BRAM - System", 2, SCREEN_MAX_LINES);
			break;
	}
	put_string("[L] / [R] Screen", 44, SCREEN_MAX_LINES);
	
	#ifdef DEBUG
	put_number(cs, 1, 61, SCREEN_MAX_LINES);
	put_string("/", 62, SCREEN_MAX_LINES);
	put_number(SCREEN_NUM_MAX, 1, 63, SCREEN_MAX_LINES);
	#endif
}

wait_for_screen_choice(msn, joy_input) char msn; int joy_input;{
	/* Wait for Left/Right pad input to change the screen */
	
	char cs;
	cs = msn;
		
	if (joy_input == 0) {
		vsync();
		joy_input = joytrg(0);
		#ifdef DEBUG
		put_joy(joy_input);
		#endif
	}
	while (cs == msn) {
		cs = handle_left_right(msn, joy_input);
		if (cs != msn) {
			return cs;
		}
		vsync();
		joy_input = joytrg(0);
		#ifdef DEBUG
		put_joy(joy_input);
		#endif
	}
}

handle_left_right(cs, joy_input) char cs; int joy_input; {
	/* Only allow left or right if they don't exceed the min and max values */
	
	if (joy_input & JOY_RGHT) {
		/* Show the next screen to the right */
		if (cs < SCREEN_NUM_MAX) {
			/* next screen number */
			cs++;	
		} else {
			/* loop to first screen */
			cs = SCREEN_NUM_MIN;
		}
	}
	
	if (joy_input & JOY_LEFT) {
		/* Show the next screen to the left */
		if (cs > SCREEN_NUM_MIN) {
			/* previous screen number */
			cs--;
		} else {
			/* loop to last screen */
			cs = SCREEN_NUM_MAX;
		}
	}
	
	#ifdef DEBUG
	put_number(cs, 1, 63, 0);
	#endif
	
	return cs;
}

bm_get_name_by_number(file_number, namebuf) char file_number; char* namebuf;{
	/* Loop through backup ram until we find file number and write the name into namebuf # */
	
	int ptr;
	char line_cnt;
	
	line_cnt = 0;
	ptr = BRAM_STARTPTR;
	while (ptr = bm_getptr(ptr, namebuf)) {
		line_cnt++;
		if (line_cnt == file_number) {
			return;	
		}
	}
	return;
}

bm_get_file_count() {
	/* return the number of files in bram */
	
	char namebuf[13];
	char file_cnt;
	int ptr;
	
	file_cnt = 0;
	ptr = BRAM_STARTPTR;
	namebuf[12] = 0;
	while (ptr = bm_getptr(ptr, namebuf)){
		file_cnt++;
	}
	return file_cnt;
}

show_bram(){
	/* main screen for browsing bram files */
	
	char line_cnt, i;	
	int j, joy_input;
	char l;
	char file_cnt, file_sel, file_sel_cnt, file_status_line, file_sel_line, file_start_line_cnt, file_name_line, file_size_line;
	char msn;
	char return_from_screen;
	
	cls();
	
	msn = 1;	
	line_cnt = 0;
	file_cnt = 0;
	file_sel = 0;
	joy_input = 0;
	return_from_screen = 0;
	l = 0;
	put_string(BRAM_SCREEN_BRAM_TITLE, 2, l);
	show_bottom_menu(msn);
	/* Is BRAM available? */
	if (bm_check()){
		
		/* Yes */
		file_cnt = bm_get_file_count();
		l++;
		l++;
		put_string("B
			RAM detected!", 2, l);
		put_string("[Details", 32, l);
		put_string("]", 63, l);
		file_status_line = l;
		
		l++;
		put_number(bm_size(), 1, 2, l);
		put_string(" kbytes total", 3, l);
		put_string("Backup File #   :", 32, l);
		file_sel_line = l;
		
		l++;
		put_number(bm_free(), 4, 2, l);
		put_string(" bytes free", 6, l);
		put_string("Backup File Name:", 32, l);
		file_name_line = l;
		
		l++;
		put_number(file_cnt, 2, 2, l);
		put_string(" files", 4, l);
		put_string("Backup File Size:", 32, l);
		file_size_line = l;
		
		if (file_cnt > 0){
			/* There are some files in BRAM - let the user browse them */
			put_string("[U] / [D] Scroll - [I] / [II] Select", 2, SCREEN_MAX_LINES - 1);
			l++;
			l++;
			put_string("[Name", 2, l);
			put_string("Size]", 17, l);
			line_cnt = l;
			line_cnt++;
			
			/* do we have more than the SCREEN_MAX_BRAM_FILES_SHOWN amount of files? */
			file_start_line_cnt = line_cnt;
			repaint_bram_file_menu(1, file_cnt, file_start_line_cnt, 0);
			show_selected_bram(file_sel, file_cnt, file_sel_line, file_start_line_cnt, file_name_line, file_size_line);
			return_from_screen = 0;
			/* Now wait for user input */
			while (return_from_screen == 0) {
				vsync();
				j = joytrg(0);
				#ifdef DEBUG
				put_joy(joy_input);
				#endif
				if (j & JOY_LEFT) {
					/* exit and go back to menu to the previous screen */
					joy_input = j;
					return_from_screen = 1;	
				} else if (j & JOY_RGHT) {
					/* exit and go back to the menu to the next screen */
					joy_input = j;
					return_from_screen = 1;
				} else { 
					/* move the selection down the list by one */
					if ((j & JOY_DOWN) | (j & JOY_UP)) {
						if (j & JOY_DOWN) {
							if (file_sel < file_cnt) {
								file_sel++;		
							}
						}
						if (j & JOY_UP) {
							if (file_sel > 0) {
								file_sel--;
							}
						}
						/* repaint menu and selected file */
						/* only repaint the menu if we need to scroll it */
						repaint_bram_file_menu(file_sel, file_cnt, file_start_line_cnt, 1);
						show_selected_bram(file_sel, file_cnt, file_sel_line, file_start_line_cnt, file_name_line, file_size_line);
					}
					/* wait for another joystick input */
					return_from_screen = 0;
					
				}
			}
		} else {
			/* No files in BRAM */
			l++;
			l++;
			put_string("No files in BRAM", 3, l);
		}
	} else {
		/* No BRAM present */
		l++;
		put_string("BRAM not available", 3, l);
	}
	return wait_for_screen_choice(msn, joy_input);
}

repaint_bram_file_menu(file_sel, file_cnt, start_line, repaint_mode) char file_sel, file_cnt, start_line, repaint_mode; {
	/* redraws the list of bram files, from the X to Yth files */
	
	char namebuf[13];
	char i, ii, cnt, start;
	int nextptr;
	
	cnt = 1;
	namebuf[12] = 0;
	nextptr = BRAM_STARTPTR;
	
	
	/* What file number are we starting from */
	if (file_cnt > SCREEN_MAX_BRAM_FILES_SHOWN) {
		/* no, we'll have to scroll them instead, start from this file number */
		if (file_sel != 0) {
			start = file_sel;
		} else {
			start = 1;
		}
	} else {
		/* yes, just show them all, start from the first */
		start = 1;
	}
	
	#ifdef DEBUG
	put_string("X", 58, 26);
	put_number(start, 2, 59, 26);
	put_string("Y", 61, 26);
	put_number(SCREEN_MAX_BRAM_FILES_SHOWN, 2, 62, 26);
	#endif
	
	/* Erase the list */
	if (file_cnt > SCREEN_MAX_BRAM_FILES_SHOWN) { 
		for (i = 0; i <= SCREEN_MAX_BRAM_FILES_SHOWN + 1; i++) {
			/* clear the line */
			for (ii = 0; ii < BRAM_FILE_MENU_WIDTH ; ii++) {
				put_string(" ", ii, start_line + i);
			}
		}
	}
	
	i = 1;
	/* loop over all bram files */
	while (nextptr = bm_getptr(nextptr, namebuf)) {
		
		/* if the count for this loop is in the range we want to show */ 
		if ((i >= start) & (cnt <= SCREEN_MAX_BRAM_FILES_SHOWN)) {
			
			if ((file_cnt > SCREEN_MAX_BRAM_FILES_SHOWN) | (repaint_mode == 0 )) { 
				/* print file data */
				put_string(&namebuf[2], 3, start_line + cnt - 1);
				put_number(bm_sizeof(namebuf), 4, 17, start_line + cnt - 1);
			}
			
			/* highlight the current selected line if it matches the current file_sel */
			if (repaint_mode) {
				if (i == file_sel) {
					put_string("*", 2, start_line + cnt - 1);
				} else {
					put_string(" ", 2, start_line + cnt - 1);
				}
			}
			cnt++ ;
		}
		i++;
	}
	
	/* If there are more files than we can show, then tell the user whether
	they can scroll up or down to see the rest */
	if (file_cnt > SCREEN_MAX_BRAM_FILES_SHOWN) {
		if (start > 1) {
			put_string("[U] More", 3, start_line + SCREEN_MAX_BRAM_FILES_SHOWN + 1);
		} else {
			put_string("[D] More", 3, start_line + SCREEN_MAX_BRAM_FILES_SHOWN + 1);
		}
	}
	return;
}

show_selected_bram(file_sel, file_cnt, file_sel_line, file_start_line_cnt, file_name_line, file_size_line) 
char file_sel, file_cnt, file_sel_line;
char file_start_line_cnt, file_name_line, file_size_line; {
	/* display the selected BRAM file */
	
	int file_size, bytes_transferred;
	char hex_start_line, hex_chars_per_line;
	char l, i;
	char buf_size;
	char file_buf[MAX_BRAM_XFER_SIZE];
	char buf_sel[13];
	char available_lines;
	
	hex_start_line = (file_start_line_cnt + SCREEN_MAX_BRAM_FILES_SHOWN + 3);
	hex_chars_per_line = MAX_BRAM_XFER_SIZE;
	buf_sel[12] = 0;
	bytes_transferred = 0;
	
	/* the number of lines we have to display the bram data */
	available_lines = SCREEN_MAX_LINES - hex_start_line - 3;
	
	if (file_sel != 0) {
		/* lookup and display info on this bram file */
		bm_get_name_by_number(file_sel, buf_sel);			
		if (buf_sel[0] != -1) {
			
			/* display info in top right hand corner */
			file_size = bm_sizeof(buf_sel);
			put_number(file_sel, 4, 60, file_sel_line);
			put_string(&buf_sel[2], 50, file_name_line);
			put_number(file_size, 4, 60, file_size_line);
			
			
			/* clear the bram hex memory buffer */
			for (i = 0; i < MAX_BRAM_XFER_SIZE ; i++) {
				file_buf[i] = -1;
			}
			
			/* clear the bram hex display area */
			l = hex_start_line + 1;
			while (available_lines > 0) {
				for (i = 0; i < hex_chars_per_line ; i ++) {
					put_string(" ", i+2, l);
				}
				available_lines--;
				l++;
			}
			available_lines = SCREEN_MAX_LINES - hex_start_line - 3;
		
			put_string("[Hex Viewer]", 2, hex_start_line);
			/* clear the 'not long enough' line */
			put_string("                                 ", 2, SCREEN_MAX_LINES - 2);
			
			l = hex_start_line + 1;
			while ((bytes_transferred < file_size) & (available_lines > 0)) {
			
				/* load X bytes of the file into memory */
				bm_read(file_buf, buf_sel, bytes_transferred, SCREEN_WIDTH - 2);
				bytes_transferred +=  MAX_BRAM_XFER_SIZE;
				
				/* print the raw bram data */
				for (i = 0; i < hex_chars_per_line ; i ++) {
					put_hex(file_buf[i], 1, i+2, l);
				}
				l++;
				available_lines--;
			}
			
			/* if not all hex data can be seen on-screen, then let us know */
			if (bytes_transferred < file_size) {
				put_string("NOTE: Not all data can be shown", 2, l);	
			} 
		}
	} else {
		/* blank the info area */
		put_number(0000, 4, 60, file_sel_line);
		put_string("            ", 50, file_name_line);
		put_number(0000, 4, 60, file_size_line);
		
		/* clear the bram hex area */
		l = hex_start_line + 1;
		while (available_lines > 0) {
			for (i = 0; i < hex_chars_per_line ; i ++) {
				put_string(" ", i+2, l);
			}
			available_lines--;
			l++;
		}
		/* clear the 'not long enough' line */
		put_string("                                 ", 2, SCREEN_MAX_LINES - 3);
	}
	return ;
}

show_syscfg(){

	char msn;
	char l;

	l = 0;
	msn = 2;
	
	cls();	
	put_string(BRAM_SCREEN_SYS_TITLE, 2, l);
	show_bottom_menu(msn);
	
	return wait_for_screen_choice(msn, 0);
}

bramtool_handle_input(cs) char cs;{
	/* Display whichever screen is currently selected */
	
	int j;
	
	/* Show BRAM utility screen */
	while (cs == BRAM_SCREEN_BRAM){
		cs = show_bram();
		if (cs != BRAM_SCREEN_BRAM) {
			return cs;
		}
	}
	
	/* Show system info screen */
	while (cs == BRAM_SCREEN_SYS){
		cs = show_syscfg();
		if (cs != BRAM_SCREEN_SYS) {
			return cs;
		}
	}
	
	/* Show main menu/front page screen */
	while (cs == BRAM_SCREEN_MENU){

		put_string(BRAM_SCREEN_MENU_TITLE, 2, 0);
		show_bottom_menu(cs);
		
		/* Read joystick input */
		vsync();
		j = joytrg(0);
		#ifdef DEBUG
		put_joy(j);
		#endif
		cs = handle_left_right(cs, j);
		if (cs != BRAM_SCREEN_MENU) {
			return cs;
		}
	}
}

main(){
	/* Sets screen mode and calls the menu handler each time the screen number changes */
	
	char cs;
	cs = 0;
	
	init_screen();
	for(;;){
		cls();
		cs = bramtool_handle_input(cs);	
	}
}
