#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include "memory.h"

#define reader_IMPORT
#include "reader.h"


struct reader_Type {
	char * path;  // path of the file
	FILE * file;  // opened file
	char * err; // error string
	int    line_no; // current line no., first is 1
};


static void reader_destruct(void *p)
{
	reader_Type *this = (reader_Type *) p;
	fclose(this->file);
	memory_dispose(this->path);
}


reader_Type * reader_new(char *path)
{
	reader_Type *this = memory_allocate(sizeof(reader_Type), reader_destruct);
	this->path = memory_strdup(path);
	this->file = fopen(path, "r");
	if( this->file == NULL )
		this->err = strerror(errno);
	else
		this->err = NULL;
	this->line_no = 0;
	return this;
}


int reader_getLine(reader_Type *this, char *line, int line_capacity)
{
	if( this->err != NULL )
		return 0;
	if( fgets(line, line_capacity, this->file) == NULL ){
		if( ferror(this->file) )
			this->err = strerror(errno);
		return 0;
	}

	int l = strlen(line);

	/* Remove trailing spaces: */
	while( l > 0 && isspace(line[l-1]) ){
		line[l-1] = '\0';
		l--;
	}

	/* Remove leading spaces: */
	int start = 0;
	while( isspace(line[start]) )
		start++;
	if( start > 0 )
		memmove(&line, line + start, l - start + 1);

	this->line_no++;
	return 1;
}


char * reader_getPath(reader_Type *this)
{
	return this->path;
}

int reader_getLineNumber(reader_Type *this)
{
	return this->line_no;
}

char * reader_getError(reader_Type *this)
{
	return this->err;
}


static int isSept(char c)
{
#ifdef WINNT
	return c == '/' || c == '\\';
#else
	return c == '/';
#endif
}


int reader_isAbsolute(char *path)
{
	if( path == NULL || *path == 0 )
		return 1;
#ifdef WINNT
	return isSept(path[0])
	|| ((('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z'))
		&& path[1] == ':');
#else
	return isSept(*path);
#endif
}


#ifdef WINNT
static char * rindex(char *s, int c)
{
	char *found = NULL;
	while(*s != 0){
		if( *s == c )
			found = s;
		s++;
	}
	return found;
}
#endif


char * reader_resolveRelativePath(char *from, char *to)
{
	if( reader_isAbsolute(to) )
		return memory_strdup(to);
	char *last_slash_ptr;
	int res_capacity;
	char *res;
	if( to[0] == '/' )
		// 'to' is already absolute -- nothing to do.
		return memory_strdup(to);
	last_slash_ptr = rindex(from, '/');
	if( last_slash_ptr == NULL )
		return memory_strdup(to);
	res_capacity = 1 + strlen(to) + (last_slash_ptr - from + 1);
	res = memory_allocate(res_capacity, NULL);
	strncpy(res, from, last_slash_ptr - from + 1);
	strcat(res, to);
	return res;
}


int reader_isReadable(char *path)
{
	FILE *f = fopen(path, "r");
	if( f == NULL ){
		return 0;
	} else {
		fclose(f);
		return 1;
	}
}


int reader_split(char *s, int *argc, char *argv[], int argv_capacity)
{
	*argc = 0;
	while(1) {
		while( isspace(*s) )
			s++;
		if( *s == 0 )
			return 1;
		if( argv_capacity <= 0 )
			return 0;
		argv_capacity--;
		(*argc)++;
		*argv = s;
		argv++;
		while(1) {
			s++;
			if( *s == 0 )
				return 1;
			if( isspace(*s) ){
				*s = 0;
				s++;
				break;
			}
		}
	}
}


int reader_splitDoubleQuotedCommaSeparated(char *s, int *argc, char *argv[], int argv_capacity)
{
	*argc = 0;
	while(1) {
		while( isspace(*s) )
			s++;
		if( *s == 0 )
			return 1;
		if( argv_capacity <= 0 )
			return 0;
		argv_capacity--;
		(*argc)++;
		*argv = s;
		argv++;
		char *dst = s;
		char inQuotes = 0;
		while(1) {
			if( *s == '"' ){
				if( inQuotes ){
					s++;
					if( *s == '"' ){
						s++;
						*dst = '"';
						dst++;
					} else {
						inQuotes = 0;
					}
				} else {
					inQuotes = 1;
					s++;
				}
			} else if( *s == 0 ){
				*dst = 0;
				return 1;
			} else if( inQuotes ){
				*dst = *s;
				s++;
				dst++;
			} else {
				if( isspace(*s) ){
					s++;
				} else if( *s == ',' ){
					s++;
					*dst = 0;
					break;
				} else {
					*dst = *s;
					s++;
					dst++;
				}
			}
		}
	}
}