#include "WimpLib:File.h"

#include <string.h>
#include "kernel.h"
#include "swis.h"

#include "WimpLib:Exception.h"
#include "WimpLib:mem.h"
#include "WimpLib:Message.h"
#include "WimpLib:Task.h"

#define File_BufLen 1024
static char File_Buf[File_BufLen];
static char ResPath[File_BufLen] = {0};

const char* throw_File_FromRes(const char* leafname)
{
	char* pleaf;

	// Initialize Resource path
	if (!ResPath[0])
	{
		// Check existance of Resources directory
		snprintf(ResPath, File_BufLen, "%s%cResources", Task_GetDir(), FILE_FOLDER_CHAR);
		if (File_GetFileType(ResPath) < 0x1000)
			throw_string(Msg_Lookup1("NoDir", ResPath));

		// Find subdir corresponding to territory
		pleaf = ResPath + strlen(ResPath);

		snprintf(pleaf, File_BufLen - (pleaf - ResPath), "%c%d", FILE_FOLDER_CHAR, Task_GetTerritoryNumber());

		// If no match to territory, find any subdir
		if (File_GetFileType(ResPath) < 0x1000)
		{
			int entry = 0;

			pleaf[0] = 0;

			while (entry != -1)
			{
				entry = Dir_ReadEntry(ResPath, entry, pleaf + 1);
				if (pleaf[1])
				{
					pleaf[0] = FILE_FOLDER_CHAR;
					if (File_GetFileType(ResPath) >= 0x1000)
						break;
				}

				pleaf[0] = 0;
			}
		}

		// If no match yet, use Resources directory
	}

	snprintf(File_Buf, File_BufLen, "%s%c%s", ResPath, FILE_FOLDER_CHAR, leafname);

	return File_Buf;
}

const char* File_GetLeafName(const char* filename)
{
	char* leaf;
	char* tmp;

	leaf = strrchr(filename, FILE_FOLDER_CHAR);
	tmp  = strrchr(filename, FILE_FILESYSTEM_CHAR);
	if (leaf < tmp) leaf = tmp;

	if (leaf == NULL) return filename;

	if (*(leaf - 1) == FILE_FILESYSTEM_CHAR) return filename + strlen(filename);

	return leaf + 1;
}

/*
 * return values:
 * -2     Not found
 * -1     Untyped
 * 0x1000 Directory
 * 0x2000 App directory
 */

File_Info File_GetInfo(const char* filename)
{
	_kernel_swi_regs regs;
	File_Info info;

	memset(&info, 0, sizeof(info));
	regs.r[0] = 23;
	regs.r[1] = (int) filename;
	if (!_kernel_swi(0x020008, &regs, &regs))
	{
		info.type = regs.r[0];
		info.loadaddr = regs.r[2];
		info.execaddr = regs.r[3];
		info.size = regs.r[4];
		info.attributes = regs.r[5];
		info.filetype = regs.r[6];
	}

	return info;
}

file_type File_GetFileType(const char* filename)
{
	_kernel_swi_regs regs;

	regs.r[0] = 23;
	regs.r[1] = (int) filename;
	if (_kernel_swi(0x020008, &regs, &regs) != NULL) return -2;
	if (regs.r[0] == 0) return -2;

	return regs.r[6];
}

unsigned int File_TypeFromString(const char* str)
{
	_kernel_swi_regs regs;

	regs.r[0] = 31;
	regs.r[1] = (int) str;
	if (_kernel_swi(0x020029, &regs, &regs) != NULL) return -1;

	return regs.r[2];
}

const _kernel_oserror* File_SetFileType(const char* filename, unsigned int type)
{
	_kernel_swi_regs regs;

	regs.r[0] = 18;
	regs.r[1] = (int) filename;
	regs.r[2] = type;
	return _kernel_swi(0x020008, &regs, &regs);
}

const _kernel_oserror* File_Copy(const char* oldfilename, const char* newfilename)
{
	_kernel_swi_regs regs;

	regs.r[0] = 26;
	regs.r[1] = (int) oldfilename;
	regs.r[2] = (int) newfilename;
	regs.r[3] = 0;
	return _kernel_swi(0x020029, &regs, &regs);
}

const _kernel_oserror* File_Delete(const char* filename)
{
	_kernel_swi_regs regs;

	regs.r[0] = 6;
	regs.r[1] = (int) filename;
	return _kernel_swi(0x020008, &regs, &regs);
}

const _kernel_oserror* File_EnsurePath(const char* filename)
{
	_kernel_swi_regs regs;
	_kernel_oserror* e = NULL;
	char* pos = (char*) File_Buf;
	snprintf(File_Buf, File_BufLen, "%s", filename);

	while ((pos = strchr(pos, FILE_FOLDER_CHAR)) != NULL)
	{
		*pos = 0;
		regs.r[0] = 8;
		regs.r[1] = (int) File_Buf;
		regs.r[4] = 0;
		e = _kernel_swi(0x020008, &regs, &regs);
		if (e) return e;
		*pos = FILE_FOLDER_CHAR;
		pos++;
	}

	return NULL;
}

const _kernel_oserror* Path_Ensure(const char* pathname)
{
	_kernel_swi_regs regs;
	_kernel_oserror* e = NULL;
	char* pos = (char*) File_Buf;
	snprintf(File_Buf, File_BufLen, "%s", pathname);

	while ((pos = strchr(pos, FILE_FOLDER_CHAR)) != NULL)
	{
		*pos = 0;
		regs.r[0] = 8;
		regs.r[1] = (int) File_Buf;
		regs.r[4] = 0;
		e = _kernel_swi(0x020008, &regs, &regs);
		if (e) return e;
		*pos = FILE_FOLDER_CHAR;
		pos++;
	}

	regs.r[0] = 8;
	regs.r[1] = (int) File_Buf;
	regs.r[4] = 0;
	e = _kernel_swi(0x020008, &regs, &regs);

	return e;
}

int Dir_ReadEntry(const char* dirname, int entry, char* buffer)
{
	_kernel_swi_regs regs;
	_kernel_oserror* e;

	regs.r[0] = 9;
	regs.r[1] = (int) dirname;
	regs.r[2] = (int) buffer;
	regs.r[3] = 1;
	regs.r[4] = entry;
	regs.r[5] = 256;
	regs.r[6] = 0;
	if ((e = _kernel_swi(0x02000c, &regs, &regs)) != NULL)
	{
		buffer[0] = 0;
		return -1;
	}
	if (regs.r[3] == 0)
		buffer[0] = 0;

	return regs.r[4];
}

bool File_FindSection(FILE* file, const char* section, bool bRestart)
{
	int  len = strlen(section);

	if (bRestart)
	{
		// Set position to start of file
		fseek(file, 0, SEEK_SET);
	}

	// Scan file until end of file
	while(true)
	{
		// Try to read a line
		if (fgets(File_Buf, File_BufLen, file) == NULL)
			return false;

		// Test for start of section marker
		if (File_Buf[0] != '[')
			continue;

		// Test if text following is section
		if (strncmp(File_Buf + 1, section, len))
			continue;

		// Test for end of section marker
		if (File_Buf[len + 1] == ']')
			return true;
	}

	return false;
}

void File_FindEndOfSection(FILE* file)
{
	fpos_t pos;

	// Scan file until end of file
	while(true)
	{
		// Store current pos in file
		fgetpos(file, &pos);

		// Try to read a line
		if (fgets(File_Buf, File_BufLen, file) == NULL)
			break;

		// Test for start of section marker
		if (File_Buf[0] == '[')
		{
			// Restore pos in file to start of this line
			fsetpos(file, &pos);
			break;
		}
	}
}

char* throw_File_CanonicalPath(const char* pname) throws(os mem)
{
	_kernel_swi_regs regs;
	char* volatile pcanonical;

	// Convert Dir to cannonical path using OS_FSControl 37
	regs.r[0] = 37;
	regs.r[1] = (int) pname;
	regs.r[2] = 0;
	regs.r[3] = 0;
	regs.r[4] = 0;
	regs.r[5] = 0;
	throw_os(_kernel_swi(0x020029, &regs, &regs));

	pcanonical = throw_mem_alloc(1 - regs.r[5]);

	try
	{
		regs.r[0] = 37;
		regs.r[1] = (int) pname;
		regs.r[2] = (int) pcanonical;
		regs.r[3] = 0;
		regs.r[4] = 0;
		regs.r[5] = 1 - regs.r[5];
		throw_os(_kernel_swi(0x020029, &regs, &regs));
	}
	catch
	{
		mem_free(pcanonical);
		throw_current();
	}
	catch_end

	return pcanonical;
}
