/* BnuView v1.0
 * Written by Prez@lfx.org
 * 
 * For more information, see README
 *
 */

#include <vga.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <font.h>

#define w 16
#define h 16

#define BPP1	1
#define GB	2
#define NES	3

int fp;
int p1 = 0xFF, p2 = 0xAA, p3 = 0x55, p4 = 0x00;

int bpp1_tile(int loc, int x, int y) {
	int r,c;
	char data[8];

	if (!fp) return(-1);

	lseek(fp, loc, SEEK_SET);
	read(fp, data, 8);


	for ( r = 0 ; r < 8 ; r++ ) {
		for ( c = 0 ; c < 8 ; c++ ) {
			if ( data[r] & 1<<c ) vga_setcolor(2);
			else vga_setcolor(5);
			vga_drawpixel(x+(7-c),y+r);
		}
	}

	return(0);
}

int gb_tile(int loc, int x, int y) {
	int r,c;
	char data[16];

	if (!fp) return(-1);

	lseek(fp, loc, SEEK_SET);
	read(fp, data, 16);

	for ( r = 0 ; r < 16 ; r+=2 ) {
		for ( c = 0 ; c < 8 ; c++ ) {
			if ( data[r] & 1<<c && data[r+1] & 1<<c ) vga_setcolor(2);
			else if ( data[r] & 1<<c && ! (data[r+1] & 1<<c) ) vga_setcolor(3);
			else if ( ! (data[r] & 1<<c) && data[r+1] & 1<<c ) vga_setcolor(4);
			else vga_setcolor(5);

			vga_drawpixel(x+(7-c),y+(r/2));
		}
	}

	return(0);
}

int nes_tile(int loc, int x, int y) {
	int r,c;
	char data[16];

	if (!fp) return(-1);

	lseek(fp, loc, SEEK_SET);
	read(fp, data, 16);

	for ( r = 0 ; r < 8 ; r++ ) {
		for ( c = 0 ; c < 8 ; c++ ) {
			if ( data[r] & 1<<c && data[r+8] & 1<<c ) vga_setcolor(2);
			else if ( data[r] & 1<<c && ! (data[r+8] & 1<<c) ) vga_setcolor(3);
			else if ( ! (data[r] & 1<<c) && data[r+8] & 1<<c ) vga_setcolor(4);
			else vga_setcolor(5);

			vga_drawpixel(x+(7-c),y+r);
		}
	}

	return(0);
}

int draw_file(int loc, int mode, int bpp) {
	int r,c;

	for ( r = 0 ; r < h ; r++ ) {
		for ( c = 0 ; c < w ; c++ ) {
		  if ( mode == BPP1 ) bpp1_tile(loc+((c+(r*w))*(8*bpp)), c*8, r*8);
		  else if ( mode == GB ) gb_tile(loc+((c+(r*w))*(8*bpp)), c*8, r*8);
		  else if ( mode == NES ) nes_tile(loc+((c+(r*w))*(8*bpp)), c*8, r*8);
		}
	}
}

int swap_pal() {
	int temp;

	temp = p1;
	p1 = p4;
	p4 = temp;

	temp = p2;
	p2 = p3;
	p3 = temp;

	vga_setpalette(2,p1,p1,p1);
	vga_setpalette(3,p2,p2,p2);
	vga_setpalette(4,p3,p3,p3);
	vga_setpalette(5,p4,p4,p4);

	return(0);
}

int draw_box(int x, int y, int W, int H) {
	int r,c;

	for ( r = 0 ; r < H ; r++ ) {
		for ( c = 0 ; c < W ; c++ ) {
			vga_setcolor(0);
			vga_drawpixel(x+c,y+r);
		}
	}

	return(0);
}

int main(int argc, char ** argv) {
	int loc = 0;
	int input;
	int mode = BPP1;
	int bpp = 1;
	int eof;
	char addr[20];
	char eofaddr[16];

	if ( argc < 2 ) exit(printf("Usage: %s <file>\n", argv[0]));

	fp = open(argv[1],O_RDONLY);
	if ( !fp ) exit(printf("Error loading file..\n"));

	eof = lseek(fp, 0, SEEK_END);

	vga_init();
	vga_setmode(G320x240x256);

	vga_setpalette(0,0,0,0);
	vga_setpalette(1,255,255,255);
	vga_setpalette(2,p1,p1,p1);
	vga_setpalette(3,p2,p2,p2);
	vga_setpalette(4,p3,p3,p3);
	vga_setpalette(5,p4,p4,p4);
	

	vga_setcolor(1);
	draw_string("BnuView v1.0 Written by Prez@lfx.org",0,240-16);

	vga_setcolor(1);
	draw_string("Controls:\nw  Line up\nz  Line down\ns  Byte up\na  Byte down\ne  Page up\nx  Page down", (8*17), 0);

	draw_string("1  BPP1\n2  Gameboy\n3  NES\n\nr  Swap palette\nq  Quit", (8*30), 16);

	sprintf(eofaddr,"End: 0x%08X", eof);
	draw_string(eofaddr,0,(8*16)+16);

	while (1) {
		draw_file(loc, mode, bpp);
		sprintf(addr,"Offset: 0x%08X", loc);
		draw_box(0,(8*16),(5*18),16);
		vga_setcolor(1);
		draw_string(addr,0,(8*16));

		input = vga_getch();
		if ( input == 'w' ) loc-=((bpp*8)*w);
		else if ( input == 'z' ) loc+=((bpp*8)*w);
		else if ( input == 'a' ) loc--;
		else if ( input == 's' ) loc++;
		else if ( input == 'e' ) loc-=((bpp*8)*w)*h;
		else if ( input == 'x' ) loc+=((bpp*8)*w)*h;

		else if ( input == '1' ) mode = BPP1, bpp = 1;
		else if ( input == '2' ) mode = GB, bpp = 2;
		else if ( input == '3' ) mode = NES, bpp = 2;

		else if ( input == 'r' ) swap_pal();
		else if ( input == 'q' ) {
		vga_setmode(TEXT);
		exit();
		}

		if ( loc > eof - ((bpp*8)*16*16) ) loc = eof - ((bpp*8)*16*16);
		if ( loc < 0 ) loc = 0;

	}

	/*we should never get here.. but incase we do..*/
	vga_setmode(TEXT);
	return(0);
}		
