#define NCOL 4
#define NROW 16
#define COLSPC 20

// Select a file navigating in the SD card
// Globals:
//   cur_dir_cluster: The cluster of the current directory 
//                    (use "FATvar.root_cluster" for \)
// Arguments:
//   ppext:   Array os string pointers with allowed File extensions for
//            filtering. Terminate the array with a NULL pointer.
//            NULL: show all files.
//   pmsg:    Text string to be shown before the file names

u32 cur_dir_cluster;

u8 *file_select(u8 *ppext[], u8 *pmsg)
{
	s32 i,j,ix,sel,off,act,l;
	static u8 filename[13];
	u8 *p;
	u16 *ps;
	u32 *pi;

	// strlen(pmsg)
	p=pmsg; l=0; while(*p++) l++;

redofsel:
	//_printf("\033[2J");
	sel=off=act=0;
	while(1) {
		_printf("\033[2J\033[1;%dH\033[01;32m%s\033[m",(80-l)/2,pmsg);
		p=FATbuf;
		FATvar.filecluster=cur_dir_cluster;
		FATvar.filesector=0;
		FATvar.filelen=-1;
		for(ix=0;;) {
			i=rdfile(FATbuf); if(!i) return 0;

			for(p=FATbuf;p<&FATbuf[512];p=&p[32]) {
				ps=(u16 *)p;
				pi=(u32 *)p;
				if (!p[0]) goto enddir;		// End of directory
				if (p[0]==0xe5) continue;	// Deleted file: skip
				if (p[11]==0x0F) continue;	// Long filename entry: skip
				// Filtering
				if (!(p[11]&0x10)) {	// Not subdir
					if (ppext) {
						for (i=0;ppext[i];i++) {
							if (p[8]==ppext[i][0] && p[9]==ppext[i][1] && p[10]==ppext[i][2]) break;
						}
						if (!ppext[i]) continue;
					}
				}
				// Prepare a NAME.EXT for return
				for (i=j=0;i<8;i++) {
					if (p[i]!=' ') filename[j++]=p[i];
				}
				if (p[i]!=' ') {
					filename[j++]='.';
					for (;i<11;i++) {
						if (p[i]!=' ') filename[j++]=p[i];
					}
				}
				filename[j]=0;
				// Matching: return data or change directory
				if ((ix==sel) && act) {	
					i=ps[26/2];				// first cluster (16-bits)
					if (FATvar.fat_type!=FAT16) i+=ps[20/2]<<16; // FAT32: cluster bits MSB
					if(!i) i=FATvar.root_cluster;	// ".." dir has a value of 0 for root parent
					FATvar.filecluster=i;	// "open" file
					FATvar.filesector=0;
					FATvar.filelen=pi[28/4];
					if (p[11]&0x10) {cur_dir_cluster=i; goto redofsel;}	// Change directory
					return filename;
				}
				// Show files
				if ((ix>=off) && (ix<off+(NCOL*NROW))) { 
					i=ix-off;
					_printf("\033[%d;%dH",i%NROW+3,(i/NROW)*COLSPC+1);
					if(ix==sel) _printf("\033[01;36m\033[7m");
					if (p[11]&0x10) putchar('/');
					_printf("%s",filename);
					if(ix==sel) _printf("\033[m");
				}
				ix++;
			}
		}
enddir:	i=_getch();
		if (i==27) {
			if((i=_getch())=='[') i=_getch()-'A';
		}
		switch(i) {
			case 0:	// Up
				if (--sel<0) sel=0;
				break;
			case 1:	// Down
				if(++sel>=ix) sel=ix-1;
				break;
			case 2:	// Right
				sel+=NROW;
				if(sel>=ix) sel=ix-1;
				break;
			case 3:	// Left
				sel-=NROW;
				if (sel<0) sel=0;
				break;
			case '\n':
				act=1;
				break;
		}
		off=(sel>=(NCOL*NROW))?((sel-(NCOL-1)*NROW)/NROW)*NROW:0;
	}
}

///////////////////// XMODEM ///////////////////////

// Read one X-modem packet (128 data bytes)
unsigned int rx_xmodem128(unsigned char *pbuf)
{
	unsigned int i,d,ck;
	
	//getch() with timeout. Keep sending NACKs until start
	i=4<<20;	// loop count: ~4e6
	while(1){
		if (U0LSR&1) break;
		if (--i==0) {i=4<<20; U0THR=21;}
	}
	d=U0RBR;

	if (d==4) {putchar(6); return 0;}	// EOT
	if (d==1) {
		d=U0getch();
		i=U0getch()^0xff;
		if (i!=d) {putchar(21); return 0;}
		for (i=ck=0;i<128;i++) {
			d=U0getch();
			ck+=d;
			*pbuf++=d;
		}
		d=U0getch();
		ck&=0xff;
		if (ck!=d) {putchar(21); return 1;}
		return 128;
	}
	return 0;
}

/////////////////// Buffered getch ///////////////////
unsigned int fbix, fbnb;
int fgetch()
{
	if (fbix==fbnb) {
		fbnb=rdfile(FATbuf);
		fbix=0;
		if(!fbnb) return -1;
	}
	return FATbuf[fbix++];
}
void fbopen()
{
	fbix=fbnb=0;
}

unsigned int xini,xbix,xbnb;
int xgetch()
{
	if (xbix==xbnb) {
		if(!xini) xini=1; 
		else putchar(6);	// ACK previous buffer
		xbnb=rx_xmodem128(FATbuf);
		if (!xbnb) return -1;
		xbix=0;
	}
	return FATbuf[xbix++];
}

void xbopen()
{
	xbix=xbnb=xini=0;
}

void xbclose()
{
	putchar(6);	// ACK previous buffer
	while(rx_xmodem128(FATbuf)); // wait for EOT
}

int muxgetch(int ch)
{
	return (ch)?xgetch():fgetch();
}

////////////////////// ASCII to Integer //////////////////////
unsigned int atohex(unsigned char *buf, int nbytes)
{
	u32 d,v,i;
	i=nbytes*2;
	d=0;
	while(*buf==' ' || *buf=='\t') buf++; 
	for (;i;i--) {
		v=*buf++;
		if (!v || v=='\n') break;
		if (v>='a' && v<='f') v-=32;
		if (v<'0' || (v>'9' && v<'A') || v>'F') break;
		d<<=4;
		v-='0'; if (v>9) v-=7;
		d+=v;
	}
	return d;
}

unsigned int _atoi(unsigned char *buf)
{
	u32 d,v;
	d=0;
	while(*buf==' ' || *buf=='\t') buf++; 
	for (;;) {
		v=*buf++;
		if (!v || v=='\n') break;
		if (v<'0' || v>'9') break;
		d*=10;;
		v-='0';
		d+=v;
	}
	return d;
}

