/***************************************************************************

	filer.c

	File browser

***************************************************************************/

#include "ncdz.h"

#define MAX_ENTRY 1024


/***************************************************************************
	Global variables
 ***************************************************************************/

char launchDir[MAX_PATH];
char startupDir[MAX_PATH];
char game_dir[MAX_PATH];
char mp3_dir[MAX_PATH];


/***************************************************************************
	Private structures
 ***************************************************************************/

struct dirent
{
	u32 type;
	int icon;
	int has_title;
	char name[256];
};

static struct dirent files[MAX_ENTRY];
static struct dirent file;
static SceIoDirent dir;


/***************************************************************************
	Private variables
 ***************************************************************************/

static char curr_dir[MAX_PATH];
static int nfiles;
static int neocddir;
static int title_exist;
static u16 title_img[80][144];

static const char *mp3_dirname = "neocd.mp3/";

////////////////////////////////////////////////////////////////////////
// クイックソート
int cmpFile(struct dirent *a, struct dirent *b)
{
	char ca, cb;
	int i, n, ret;

	if(a->type==b->type){
		n=strlen(a->name);
		for(i=0; i<=n; i++){
			ca=a->name[i]; cb=b->name[i];
			if(ca>='a' && ca<='z') ca-=0x20;
			if(cb>='a' && cb<='z') cb-=0x20;

			ret = ca-cb;
			if(ret!=0) return ret;
		}
		return 0;
	}

	if(a->type & FIO_SO_IFDIR)	return -1;
	else					return 1;
}

void sort(struct dirent *a, int left, int right)
{
	struct dirent tmp, pivot;
	int i, p;

	if (left < right) {
		pivot = a[left];
		p = left;
		for (i=left+1; i<=right; i++) {
			if (cmpFile(&a[i],&pivot)<0){
				p=p+1;
				tmp=a[p];
				a[p]=a[i];
				a[i]=tmp;
			}
		}
		a[left] = a[p];
		a[p] = pivot;
		sort(a, left, p-1);
		sort(a, p+1, right);
	}
}


/*--------------------------------------------------------
	Find a file
 -------------------------------------------------------*/

char *find_file(char *pattern, char *path)
{
	int fd, i, found, len1, len2;

	memset(&dir, 0, sizeof(dir));

	fd = sceIoDopen(path);
	found = 0;

	len1 = strlen(pattern);

	while (!found)
	{
		if (sceIoDread(fd, &dir) <= 0) break;

		len2 = strlen(dir.d_name);

		for (i = 0; i < len2; i++)
		{
			if (strnicmp(&dir.d_name[i], pattern, len1) == 0)
			{
				strcpy(file.name, dir.d_name);
				found = 1;
				break;
			}
		}
	}

	sceIoDclose(fd);

	return found ? file.name : NULL;
}


/*--------------------------------------------------------
	Find state file
 -------------------------------------------------------*/

#ifdef SAVE_STATE

void find_state_file(u8 *slot)
{
	int fd, len1, len2;
	char path[MAX_PATH], pattern[16];

	memset(&dir, 0, sizeof(dir));

	sprintf(path, "%sstate", launchDir);
	sprintf(pattern, "%s.sv", game_name);

	len1 = strlen(pattern);
	fd = sceIoDopen(path);

	while (sceIoDread(fd, &dir) > 0)
	{
		if (strnicmp(dir.d_name, pattern, len1) == 0)
		{
			char number = dir.d_name[len1] - '0';

			if (number >= 0 && number <= 9)
				slot[number] = 1;
		}
	}

	sceIoDclose(fd);
}

#endif


/*--------------------------------------------------------
	Check directories
 -------------------------------------------------------*/

static void checkDir(const char *name)
{
	int fd, found;
	char path[MAX_PATH];

	memset(&dir, 0, sizeof(dir));

	fd = sceIoDopen(launchDir);
	found = 0;

	while (!found)
	{
		if (sceIoDread(fd, &dir) <= 0) break;

		if (dir.d_stat.st_attr == FIO_SO_IFDIR)
			if (stricmp(dir.d_name, name) == 0)
				found = 1;
	}

	sceIoDclose(fd);

	if (!found)
	{
		sprintf(path, "%s%s", launchDir, name);
		sceIoMkdir(path, 0777);
	}
}


/*--------------------------------------------------------
	Check startup directories
 -------------------------------------------------------*/

static void checkStartupDir(void)
{
	int fd = sceIoDopen(startupDir);

	if (fd > 0)
	{
		strcpy(curr_dir, startupDir);
		sceIoDclose(fd);
	}
}


/*--------------------------------------------------------
	Get directory entry
 -------------------------------------------------------*/

static void getDir(const char *path)
{
	static int fd;
	int b = 0;

	memset(&dir, 0, sizeof(dir));

	nfiles = 0;

	if (strcmp(path, "ms0:/") != 0)
	{
		strcpy(files[nfiles].name, "..");
		files[nfiles].type = FIO_SO_IFDIR;
		files[nfiles].icon = ICON_UPPERDIR;
		files[nfiles].has_title = 0;
		nfiles++;
		b = 1;
	}

	fd = sceIoDopen(path);

	while (nfiles < MAX_ENTRY)
	{
		char *ext;
		static SceIoDirent dir = { 0 };

		if (sceIoDread(fd, &dir) <= 0) break;

		if (dir.d_name[0] == '.') continue;

		if (stricmp(dir.d_name, "ipl.txt") == 0)
		{
			neocddir = 1;
			continue;
		}

		if ((ext = strrchr(dir.d_name, '.')) != NULL)
		{
			if (stricmp(ext, ".zip") == 0)
			{
				strcpy(files[nfiles].name, dir.d_name);
				files[nfiles].type = 0;
				files[nfiles].icon = ICON_ZIPFILE;
				files[nfiles].has_title = 0;
				nfiles++;
				continue;
			}
		}

		if (dir.d_stat.st_attr == FIO_SO_IFDIR)
		{
			if (stricmp(dir.d_name, "config") == 0) continue;
			if (stricmp(dir.d_name, "snap") == 0) continue;
			if (stricmp(dir.d_name, "data") == 0) continue;
#ifdef SAVE_STATE
			if (stricmp(dir.d_name, "state") == 0) continue;
#endif

			strcpy(files[nfiles].name, dir.d_name);
			strcat(files[nfiles].name, "/");
			files[nfiles].type = FIO_SO_IFDIR;
			files[nfiles].icon = ICON_FOLDER;
			files[nfiles].has_title = 1;
			nfiles++;
		}
	}

	sceIoDclose(fd);

	if (b)
		sort(files + 1, 0, nfiles - 2);
	else
		sort(files, 0, nfiles - 1);
}


/*--------------------------------------------------------
	Draw title_x.sys to title image buffer
 -------------------------------------------------------*/

static void title_draw_spr(int sx, int sy, u8 *spr, u16 *pal, int tileno)
{
	int y;
	u32 tile;
	u32 *gfx = (u32 *)(spr + tileno * 128);
	u16 *paldata = &pal[tileno << 4];

	for (y = sy; y < sy + 16; y++)
	{
		tile = *gfx++;
		title_img[y][sx +  0] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx +  1] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx +  2] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx +  3] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx +  4] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx +  5] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx +  6] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx +  7] = paldata[tile & 0x0f];
		tile = *gfx++;
		title_img[y][sx +  8] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx +  9] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx + 10] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx + 11] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx + 12] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx + 13] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx + 14] = paldata[tile & 0x0f]; tile >>= 4;
		title_img[y][sx + 15] = paldata[tile & 0x0f];
	}
}


/*--------------------------------------------------------
	Load title_x.sys
 -------------------------------------------------------*/

static int load_title(const char *path)
{
	int i, region, tileno, x, y, found = 0;
	u16 title_pal[0x5a0 >> 1];
	u8  title_spr[0x1680];
	char title_path[MAX_PATH], region_chr[3] = {'j','u','e'};
	FILE *fp;

	for (region = neogeo_region & 0x03; region >= 0; region--)
	{
		sprintf(title_path, "%stitle_%c.sys", path, region_chr[region]);

		if ((fp = fopen(title_path, "rb")) != NULL)
		{
			found = 1;
			break;
		}
	}

	if (!found) return 0;

	fread(title_pal, 0x5a0, 1, fp);
	fread(title_spr, 0x1680, 1, fp);
	fclose(fp);

	swab((u8 *)title_pal, (u8 *)title_pal, 0x5a0);

	for (i = 0; i < 0x5a0 >> 1; i++)
	{
		int r = ((title_pal[i] >> 7) & 0x1e) | ((title_pal[i] >> 14) & 0x01);
		int g = ((title_pal[i] >> 3) & 0x1e) | ((title_pal[i] >> 13) & 0x01);
		int b = ((title_pal[i] << 1) & 0x1e) | ((title_pal[i] >> 12) & 0x01);

		title_pal[i] = (b << 10) | (g << 5) | r;
	}

	neogeo_decode_spr(title_spr, 0, 0x1680);

	tileno = 0;

	for (y = 0; y < 80; y += 16)
	{
		for (x = 0; x < 144; x += 16)
		{
			title_draw_spr(x, y, title_spr, title_pal, tileno);
			tileno++;
		}
	}

	return 1;
}


/*--------------------------------------------------------
	Show title_x.sys
 -------------------------------------------------------*/

static void show_title(int sx, int sy)
{
	u32 *dst = video_frame_addr32(draw_frame, sx, sy);
	int x, y;

	draw_box_shadow(sx, sy, sx + 144, sy + 80);

	for (y = 0; y < 80; y++)
	{
		for (x = 0; x < 144; x++)
		{
			int r = GETR15(title_img[y][x]);
			int g = GETG15(title_img[y][x]);
			int b = GETB15(title_img[y][x]);

			dst[x] = MAKECOL32(r, g, b);
		}
		dst += BUF_WIDTH;
	}
}


/*--------------------------------------------------------
	Modify display path
 -------------------------------------------------------*/

void modify_display_path(char *path, char *org_path, int max_width)
{
	if (uifont_get_string_width(org_path) > max_width)
	{
		int i, j, width, num_dir = 0;
		char temp[MAX_PATH], *dir[256];

		strcpy(temp, org_path);

		dir[num_dir++] = strtok(temp, "/");

		while ((dir[num_dir] = strtok(NULL, "/")) != NULL)
			num_dir++;

		j = num_dir - 1;

		do
		{
			j--;

			path[0] = '\0';

			for (i = 0; i < j; i++)
			{
				strcat(path, dir[i]);
				strcat(path, "/");
			}

			strcat(path, ".../");
			strcat(path, dir[num_dir - 1]);
			strcat(path, "/");

		} while (uifont_get_string_width(path) > max_width);
	}
	else strcpy(path, org_path);
}


/*--------------------------------------------------------
	Run filer
 -------------------------------------------------------*/

void file_browser(const char *arg)
{
	int i, sel = 0, rows = 11, top = 0;
	int title_loaded = -1, run_neogeo = 0, update = 1;
	int prev_sel = 0;
	char path[MAX_PATH], *p;

	bLoop = LOOP_BROWSER;

	checkDir("config");
	checkDir("snap");
	checkDir("data");
#ifdef SAVE_STATE
	checkDir("state");
#endif

	memset(launchDir, 0, sizeof(launchDir));
	strncpy(launchDir, arg, sizeof(launchDir) - 1);
	if ((p = strrchr(launchDir, '/')) != NULL) *(++p) = '\0';

	memset(files, 0, sizeof(files));

	strcpy(curr_dir, launchDir);
	strcpy(startupDir, launchDir);

	load_settings();

	checkStartupDir();
	getDir(curr_dir);

	video_clear_screen32();
	load_background(WP_LOGO);
	show_background();

	ui_popup_reset();

	small_icon_shadow(6, 3, UI_COLOR(UI_PAL_TITLE), ICON_RESET);
	logo(32, 5, UI_COLOR(UI_PAL_TITLE));

	draw_dialog(240-164, 136-80, 240+164, 136+80);
	uifont_print_shadow_center(68, 255,255,120, APPNAME_STR " " VERSION_STR "   by NJ");
	uifont_print_center(70 + 20 * 1, 220,220,220, "http://neocdz.hp.infoseek.co.jp");
	uifont_print_center(70 + 20 * 2, 255,255,255, "PSP hardware GFX render based on");
	uifont_print_center(70 + 20 * 3, 255,255,255, "NEOCDPSP 0.5 by yoyoFR,");
	uifont_print_center(70 + 20 * 4, 220,220,220, "http://yoyofr.fr.st");
	uifont_print_center(70 + 20 * 5, 255,255,255, "Powered by unofficial PSPSDK");
	uifont_print_center(70 + 20 * 6, 220,220,220, "http://www.ps2dev.org");

	video_flip_screen(1);
	pad_wait_press(3000);
	pad_wait_clear();

	load_background(WP_FILER);

	while (bLoop)
	{
		if (run_neogeo)
		{
			run_neogeo = 0;
			bLoop = LOOP_EXEC;

			video_clear_screen32();
			video_exit32();
			video_init();
			video_clear_screen();

			set_cpu_clock();

			neogeo_main();

			scePowerSetClockFrequency(222, 222, 111);

			if (bLoop)
			{
				video_clear_screen();
				video_exit();
				video_init32();
				video_clear_screen32();

				if (fatal_error)
				{
					load_background(WP_LOGO);
					small_icon_shadow(6, 3, UI_COLOR(UI_PAL_TITLE), ICON_RESET);
#if JAPANESE_UI
					uifont_print_shadow(32, 5, UI_COLOR(UI_PAL_TITLE), "致命的なエラー");
#else
					uifont_print_shadow(32, 5, UI_COLOR(UI_PAL_TITLE), "Fatal error");
#endif
					draw_battery_status(1);
					ui_show_popup(1);
					video_flip_screen(1);
					pad_wait_press(-1);
					ui_popup_reset();
				}

				load_background(WP_FILER);
				getDir(curr_dir);
				ui_popup_reset();

				update = 1;
			}
			else break;
		}

		if (update & 1)
		{
			update = 0;

			show_background();

			small_icon_shadow(6, 3, UI_COLOR(UI_PAL_TITLE), ICON_MEMSTICK);
			modify_display_path(path, curr_dir, 368);
			uifont_print_shadow(32, 5, UI_COLOR(UI_PAL_TITLE), path);

			draw_battery_status(1);

			draw_scrollbar(469, 26, 479, 270, rows, nfiles, sel);

			for (i = 0; i < rows; i++)
			{
				if (top + i >= nfiles) break;

				if (top + i == sel)
				{
					boxfill_gradation(4, 37 + i * 20, 464, 56 + i * 20, UI_COLOR(UI_PAL_FILESEL1), UI_COLOR(UI_PAL_FILESEL2), 8, 0);
					small_icon_light(6, 38 + i * 20, UI_COLOR(UI_PAL_SELECT), files[top + i].icon);
					uifont_print_shadow(36, 40 + i * 20, UI_COLOR(UI_PAL_SELECT), files[top + i].name);

					if (files[top + i].icon != ICON_ZIPFILE)
					{
						if (files[top + i].has_title && title_loaded == -1)
						{
							sprintf(path, "%s%s", curr_dir, files[top + i].name);

							if (load_title(path))
								title_loaded = top + i;
							else
								files[top + i].has_title = 0;
						}
					}
				}
				else
				{
					small_icon(6, 38 + i * 20, UI_COLOR(UI_PAL_NORMAL), files[top + i].icon);
					uifont_print(36, 40 + i * 20, UI_COLOR(UI_PAL_NORMAL), files[top + i].name);
				}
			}

			if (title_loaded == sel)
			{
				if (sel < top + rows / 2)
					show_title(315, 169);
				else
					show_title(315, 50);
			}

			update = ui_show_popup(1);
			video_flip_screen(1);
		}
		else if (update & 2)
		{
			int x, y;
			u32 temp[26][32];
			u32 *vptr;

			update = 0;

			show_background();

			for (i = 0; i < rows; i++)
				if (top + i == sel) break;

			boxfill_gradation(4, 37 + i * 20, 464, 56 + i * 20, UI_COLOR(UI_PAL_FILESEL1), UI_COLOR(UI_PAL_FILESEL2), 8, 0);
			small_icon_light(6, 38 + i * 20, UI_COLOR(UI_PAL_SELECT), files[top + i].icon);

			vptr = video_frame_addr32(draw_frame, 0, 34 + i * 20);
			for (y = 0; y < 26; y++)
			{
				for (x = 0; x < 32; x++)
				{
					temp[y][x] = vptr[x];
				}
				vptr += BUF_WIDTH;
			}

			video_copy_rect32(show_frame, draw_frame, &full_rect, &full_rect);

			vptr = video_frame_addr32(draw_frame, 0, 34 + i * 20);
			for (y = 0; y < 26; y++)
			{
				for (x = 0; x < 32; x++)
				{
					vptr[x] = temp[y][x];
				}
				vptr += BUF_WIDTH;
			}

			update = ui_show_popup(0);
			video_flip_screen(1);
		}
		else
		{
			update = ui_show_popup(0);
			video_wait_vsync();
		}

		update |= ui_light_update();
		prev_sel = sel;

		if (pad_pressed(PSP_CTRL_UP))
		{
			sel--;
		}
		else if (pad_pressed(PSP_CTRL_DOWN))
		{
			sel++;
		}
		else if (pad_pressed(PSP_CTRL_LEFT))
		{
			sel -= rows;
			if (sel < 0) sel = 0;
		}
		else if (pad_pressed(PSP_CTRL_RIGHT))
		{
			sel += rows;
			if (sel >= nfiles) sel = nfiles - 1;
		}
		else if (pad_pressed(PSP_CTRL_CIRCLE))
		{
			if (files[sel].type == FIO_SO_IFDIR)
			{
				if (!strcmp(files[sel].name, ".."))
				{
					if (strcmp(curr_dir, "ms0:/") != 0)
					{
						char old_dir[MAX_PATH];

						*strrchr(curr_dir, '/') = '\0';
						p = strrchr(curr_dir, '/') + 1;
						strcpy(old_dir, p);
						strcat(old_dir,  "/");
						*p = '\0';

						getDir(curr_dir);
						sel = 0;
						prev_sel = -1;

						for (i = 0; i < nfiles; i++)
						{
							if (!strcmp(old_dir, files[i].name))
							{
								sel = i;
								top = sel - 3;
								break;
							}
						}
					}
					pad_wait_clear();
				}
	   			else
	   			{
					strcat(curr_dir, files[sel].name);
					getDir(curr_dir);
					sel = 0;

					if (neocddir)
					{
						neocddir = 0;

						if (messagebox(MB_LAUNCHDIRECTORY))
						{
							strcpy(game_dir, curr_dir);
							strcpy(mp3_dir, curr_dir);
							strcat(mp3_dir, mp3_dirname);
							*strrchr(curr_dir, '/') = '\0';
							*(strrchr(curr_dir, '/') + 1) = '\0';

							run_neogeo = 1;
							neogeo_boot_bios = 0;
						}
					}

					if (!run_neogeo)
					{
						prev_sel = -1;
						pad_wait_clear();
					}
				}
			}
			else
			{
				if (messagebox(MB_LAUNCHZIPFILE))
				{
					strcpy(game_dir, curr_dir);
					strcpy(mp3_dir, curr_dir);
					strcat(mp3_dir, "neocd.mp3/");
					strcat(game_dir, files[sel].name);

					run_neogeo = 1;
					neogeo_boot_bios = 0;
				}
				update = 1;
			}
		}
		else if (pad_pressed(PSP_CTRL_SQUARE))
		{
			if (messagebox(MB_BOOTBIOS))
			{
				strcpy(game_dir, curr_dir);
				strcpy(mp3_dir, curr_dir);
				strcat(mp3_dir, "neocd.mp3/");

				run_neogeo = 1;
				neogeo_boot_bios = 1;
			}
			update = 1;
		}
		else if (pad_pressed(PSP_CTRL_TRIANGLE))
		{
			if (messagebox(MB_EXITEMULATOR))
			{
				bLoop = LOOP_EXIT;

				video_clear_screen32();
				video_clear_frame32(work_frame);

				boxfill(0, 0, 479,  271, COLOR_DARKGRAY);
#if JAPANESE_UI
				uifont_print_shadow_center(129, COLOR_WHITE, "しばらくお待ちください。");
#else
				uifont_print_shadow_center(129, COLOR_WHITE, "Please wait...");
#endif
				video_flip_screen(1);
				break;
			}
			update = 1;
		}
		else if (pad_pressed(PSP_CTRL_START))
		{
			ui_save_screenshot("menu1");
			pad_wait_clear();
		}
		else if (pad_pressed(PSP_CTRL_SELECT))
		{
			if (files[sel].icon != ICON_ZIPFILE && strcmp(files[sel].name, "..") != 0)
			{
				if (messagebox(MB_SETSTARTUPDIR))
					sprintf(startupDir, "%s%s", curr_dir, files[sel].name);
			}
		}
		else if (pad_pressed(PSP_CTRL_LTRIGGER))
		{
			show_color_menu();
			update = 1;
		}
		else if (pad_pressed(PSP_CTRL_RTRIGGER))
		{
			help(HELP_FILEBROWSER);
			update = 1;
		}

		if (top > nfiles - rows) top = nfiles - rows;
		if (top < 0) top = 0;
		if (sel >= nfiles) sel = 0;
		if (sel < 0) sel = nfiles - 1;
		if (sel >= top + rows) top = sel - rows + 1;
		if (sel < top) top = sel;

		if (prev_sel != sel)
			update = 1;
		else
			title_loaded = -1;

		pad_update();
	}

	save_settings();
}
