/*
 * Acess2 - URI Parser and opener
 * By John Hodge (thePowersGang)
 */
#include <acess/sys.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <uri.h>

// === STRUCTURES ===
struct sURIFile
{
	 int	Handle;
	 int	Mode;
	tURIHandler	*Handler;
	 int	CurBlockOffset;
	char	Buffer[];
};

// === PROTOTYPES ===
 int	SoMain(void);
tURI	*URI_Parse(const char *String);
tURIFile	*URI_MakeHandle(int Mode, int Handle, tURIHandler *Handler);
tURIFile	*URI_Open(int Mode, tURI *URI);
size_t	URI_Read(tURIFile *File, size_t Bytes, void *Buffer);
size_t	URI_Write(tURIFile *File, size_t Bytes, void *Buffer);
void	URI_Close(tURIFile *File);
// --- file:/// handler
 int	URI_file_Open(char *Host, int Port, char *Path, int Mode);
size_t	URI_file_Read(int Handle, size_t Bytes, void *Buffer);
size_t	URI_file_Write(int Handle, size_t Bytes, void *Buffer);
void	URI_file_Close(int Handle);

// === CONSTANTS ===
// Builtin URI protocol handlers
tURIHandler	caBuiltinHandlers[] = {
	{"file", 0, URI_file_Open, URI_file_Close, URI_file_Read, URI_file_Write}
};
#define NUM_BUILTIN_HANDLERS	(sizeof(caBuiltinHandlers)/sizeof(caBuiltinHandlers[0]))

// === CODE ===
int SoMain(void)
{
	return 0;
}

tURI *URI_Parse(const char *String)
{
	const char	*tmp = String;
	tURI	*ret;
	 int	protolen;
	
	if(!String)	return NULL;
	
	protolen = 0;
	while( isalpha(*tmp) )	tmp++, protolen++;
	
	// true URI
	if(tmp[0] == ':' && tmp[1] == '/' && tmp[2] == '/')
	{
		 int	hostlen, portlen, pathlen;
		tmp += 3;	// Eat '://'
		ret = malloc(sizeof(tURI) + strlen(String) - 2);
		
		ret->Proto = (char*)ret + sizeof(tURI);
		memcpy(ret->Proto, String, protolen);
		ret->Proto[protolen] = '\0';
		
		ret->Host = ret->Proto + protolen + 1;
		hostlen = 0;
		
		// IPv6
		if( *tmp == '[' )
		{
			tmp ++;
			while( *tmp != ']' ) {
				ret->Host[hostlen] = *tmp;
				tmp ++;
				hostlen ++;
			}
			tmp ++;
			ret->Host[hostlen] = '\0';
		}
		// IPv4/DNS
		else
		{
			while( *tmp != '/' && *tmp != ':' )
			{
				ret->Host[hostlen] = *tmp;
				tmp ++;
				hostlen ++;
			}
			ret->Host[hostlen] = '\0';
		}
		
		// Port
		if( *tmp == ':' ) {
			ret->PortStr = ret->Host + hostlen + 1;
			tmp ++;
			portlen = 0;
			while(isalpha(*tmp) || isdigit(*tmp))
			{
				ret->PortStr[portlen] = *tmp;
				portlen ++;
				tmp ++;
			}
			ret->PortStr[portlen] = '\0';
			
			ret->PortNum = atoi(ret->PortStr);
			if(!ret->PortNum && !(ret->PortStr[0] == '0' && portlen == 1) )
			{
				// error!
				ret->Path = NULL;
				return ret;
			}
		}
		else {
			ret->PortStr = NULL;
			ret->PortNum = -1;
		}
		
		if(*tmp == '\0')
		{
			ret->Path = NULL;
			return ret;
		}
		
		// TODO: What to do on a parse error
		if(*tmp != '/') {
			ret->Path = NULL;
			return ret;
		}
		
		if(ret->PortStr)
			ret->Path = ret->PortStr + portlen + 1;
		else
			ret->Path = ret->Host + hostlen + 1;
		
		tmp ++;
		pathlen = 0;
		while(*tmp)
		{
			ret->Path[pathlen] = *tmp;
			tmp ++;
			pathlen ++;
		}
		
		return ret;
	}
	else
	{
//		char	*cwd;
//		 int	retlen;
		
		// Path
		// TODO: What to do?
		// Probably return file:///<path>
		// but should I get the CWD and use append that?
		ret = malloc( sizeof(tURI) + strlen(String) + 1 );
		ret->Path = (char*)ret + sizeof(tURI);
		strcpy(ret->Path, String);
		ret->Proto = "file";
		ret->Host = NULL;
		ret->PortNum = 0;
		ret->PortStr = NULL;
		return ret;
	}
}

tURIFile *URI_MakeHandle(int Mode, int Handle, tURIHandler *Handler)
{
	tURIFile	*ret;
	
	ret = malloc(sizeof(tURIFile)+Handler->BlockSize);
	if(!ret)	return NULL;
	
	ret->Handle = Handle;
	ret->Mode = Mode;
	ret->Handler = Handler;
	ret->CurBlockOffset = 0;
	
	return ret;
}

tURIFile *URI_Open(int Mode, tURI *URI)
{
	tURIHandler	*handler;
	tURIFile	*ret;
	 int	handle;
	 int	i;
	
	if(!URI)
		return NULL;
	
	for( i = 0; i < NUM_BUILTIN_HANDLERS; i ++ )
	{
		if(strcmp(URI->Proto, caBuiltinHandlers[i].Name) == 0)
			break;
	}
	
	if( i == NUM_BUILTIN_HANDLERS )
	{
		// TODO: Dynamics
		printf("URI_Open: Warning - Unknown URI handler\n");
		return NULL;
	}
	else
		handler = &caBuiltinHandlers[i];
	
	printf("URI_Open: handler->Open = %p\n", handler->Open);
	
	handle = handler->Open(URI->Host, URI->PortNum, URI->Path, Mode);
	printf("URI_Open: handle = %i\n", handle);
	if(handle == -1)	return NULL;
	
	printf("URI_MakeHandle(Mode=%i, handle=%i, handler=%p)\n",
		Mode, handle, handler);
	ret = URI_MakeHandle(Mode, handle, handler);
	if(!ret) {
		handler->Close( handle );
		return NULL;
	}
	return ret;
}

/**
 * \brief Read from a URI file
 */
size_t URI_Read(tURIFile *File, size_t Bytes, void *Buffer)
{
	size_t	rem = Bytes;
	void	*buf = Buffer;
	size_t	tmp;
	
	printf("URI_Read(File=%p, Bytes=%u, Buffer=%p)\n",
		File, Bytes, Buffer);
	
	if(!File || !Buffer)	return -1;
	if(Bytes == 0)	return 0;
	
	if( !(File->Mode & URI_MODE_READ) )	return -1;
	
	// Read from cache if avaliable
	if(File->Handler->BlockSize && File->CurBlockOffset)
	{
		 int	avail;
		
		avail = File->Handler->BlockSize - File->CurBlockOffset;
		
		if(avail >= Bytes) {
			memcpy(Buffer, File->Buffer, Bytes);
			File->CurBlockOffset += Bytes;
			File->CurBlockOffset %= File->Handler->BlockSize;
			return Bytes;
		}
		
		rem -= avail;
		memcpy(Buffer, File->Buffer, avail);
		File->CurBlockOffset = 0;
		buf += avail;
	}
	
	
	if( File->Handler->BlockSize )
	{
		// Read whole blocks
		while( rem >= File->Handler->BlockSize )
		{
			tmp = File->Handler->Read( File->Handle, File->Handler->BlockSize, buf );
			if(tmp < File->Handler->BlockSize)
				return Bytes - rem - tmp;
			buf += File->Handler->BlockSize;
		}
		
		// Read the trailing part
		if(rem)
		{
			File->Handler->Read( File->Handle, File->Handler->BlockSize, File->Buffer );
			memcpy( buf, File->Buffer, rem );
			File->CurBlockOffset += rem;
		}
		return Bytes;
	}
	
	return File->Handler->Read( File->Handle, Bytes, Buffer );
}

/**
 * \brief Write to a URI file
 */


// ====
// Builtin Handlers
// ====
int URI_file_Open(char *Host, int Port, char *Path, int Mode)
{
	 int	smode = 0;
	if(Mode & URI_MODE_READ)	smode |= OPENFLAG_READ;
	if(Mode & URI_MODE_WRITE)	smode |= OPENFLAG_WRITE;
	
	printf("URI_file_Open: open('%s', 0x%x)\n", Path, smode);
	{
		 int	ret;
		ret = open(Path, smode);
		return ret;
	}
}
size_t URI_file_Read(int Handle, size_t Bytes, void *Buffer)
{
	printf("URI_file_Read: (Handle=%i, Bytes=%i, Buffer=%p)\n",
		Handle, Bytes, Buffer);
	return read(Handle, Bytes, Buffer);
}
size_t URI_file_Write(int Handle, size_t Bytes, void *Buffer)
{
	return write(Handle, Bytes, Buffer);
}
void URI_file_Close(int Handle)
{
	close(Handle);
}
