/*
 * lexer.c: Reads in the .calendar files
 */
#include <stdio.h>
#include <glib.h>


#define opener "["
#define closer "]"
#define VersionMajor 2


int skip_chars(FILE *fp, char *terminator)
{
	int c;
	int cnt;

	cnt = 0;
	while( (c = fgetc(fp)) != EOF) {
		if (c == terminator[cnt]) {
			cnt++;
			if (terminator[cnt] == '\0')
				return TRUE;
		} else
			cnt = 0;
	}
	return FALSE;
}

int peek_char(FILE *fp, char *c)
{
	if ( ((*c) = fgetc(fp)) != EOF) {
		ungetc((*c), fp);
		return TRUE;
	} else	
		return FALSE;
}

int skip_whitespace(FILE *fp)
{
	int c;

	while( (c = fgetc(fp)) != EOF)
		if (!isspace(c)) {
			ungetc(c, fp);
			return TRUE;
		}
	return FALSE;
}

int get_until(FILE *fp, char terminator, char *buf) 
{
	int c;

	while( (c = fgetc(fp)) != EOF) {
		if (c == terminator)
			return TRUE;
		*buf = (char)c;
		buf++;
	}
	return FALSE;
}

int get_number(FILE *fp, int *x) 
{
	char buf[50];
	int c;
	int cnt;

	cnt = 0;
	buf[cnt] = '\0';
	while( (c= fgetc(fp)) != EOF) {
	   if (!isdigit(c)) {
	   	ungetc(c, fp);
	   	*x = atoi(buf);
	   	return TRUE;
	   }
	   buf[cnt++] = (char)c;
	   buf[cnt] = '\0';
	}
	*x = atoi(buf);
	return FALSE;
}	

int get_string(FILE *fp, char *string)
{
	int c;
	int cnt;

	cnt = 0;
	while ( (c = fgetc(fp)) != EOF) {
		if (c == closer[0]) {
			string[cnt] = '\0';
			ungetc((char)c, fp);
			return TRUE;
		}
		string[cnt++] = (char)c;
	}	
	return FALSE;
}

int getid(FILE *fp, char *string)
{
	int c;
	int cnt;

	cnt = 0;
	while( (c =fgetc(fp)) != EOF) {
		if (isalnum(c)) 
			string[cnt++] = (char)c;
		else {
			string[cnt] = '\0';
			return TRUE;
		}
	}
	string[cnt] = '\0';
	return FALSE;
}

int parse_appointment(FILE *fp, char keyword[])
{
	char buf[50];
	int x,y,c;

	if (strcmp(keyword, "Start") == 0) {
		if ( ! skip_whitespace(fp) || ! get_number(fp, &x) ) {
			g_error("Unable to get start time");
			return FALSE;
		}
		g_print ("Appointment start = %d\n", x);
		return TRUE;
	}

	if (strcmp(keyword, "Length") == 0) {
		if ( ! skip_whitespace(fp) || ! get_number(fp, &x) ) {
			g_error("Unable to get length");
			return FALSE;
		}
		g_print ("Appointment length = %d\n", x);
		return TRUE;
	}

	if (strcmp(keyword, "Alarms") == 0) {
		while(TRUE) {
			skip_whitespace(fp);
			if (!peek_char(fp, (char*)&c)) {
				g_error("Cannot read alarm list");
				return FALSE;
			}
			if (!isdigit(c))
				break;

			if (! get_number(fp, &x))
				return FALSE;

			g_print("New alarm %d\n", x);
		}
		return TRUE;
	}

	g_print("Unknown keyword %s\n", keyword);
	return FALSE;
}
			
int parse_item(FILE *fp, char keyword[]) 
{
	char buf[50];
	int x, y, c;

	if (strcmp(keyword, "Remind") == 0) {
		if (! skip_whitespace(fp) || ! get_number(fp, &x)) {
			g_error("Cannot get remind level");
			return FALSE;
		}
		g_print("Remind level = %d\n", x);
		return TRUE;
	}

	if (strcmp(keyword, "Owner") == 0) {
		if (!get_string(fp, buf)) {
			g_error("Cannot get owner information");
			return FALSE;
		}
		g_print("Owner = %s\n", buf);
		return TRUE;
	}

	if (strcmp(keyword, "Uid") == 0) {
		if (!skip_whitespace(fp) || !get_until(fp, *closer, buf)) {
			g_error("Cannot get unique ID");
			return FALSE;
		}
		g_print("UID = %s\n", buf);
		return TRUE;
	}

	if (strcmp(keyword, "Contents") == 0) {
		if (!get_string(fp, buf)) {
			g_error("Cannot get item text");
			return FALSE;
		}
		g_print("Contents = %s\n", buf);
		return TRUE;
	}

	if (strcmp(keyword, "Text") == 0) {
		if (! skip_whitespace(fp) || ! get_number(fp, &x) ||
		    (x < 0) || ! skip_whitespace(fp) || ! skip_chars(fp, opener) ) {
			g_error("Cannot get item text");
			return FALSE;
		}
		y = 0;
		while(y < x) {
			if ( (c = fgetc(fp)) == EOF) {
				g_error("Short item text");
				return FALSE;
			}
			buf[y++] = (char)c;
		}
		buf[y] = '\0';
		g_print("Text = %s\n", buf);
		return TRUE;
	}
	
	if (strcmp(keyword, "Dates") == 0) {
		if (! get_string(fp, buf)) {
			g_error("Cannot get date");
			return FALSE;
		}
		g_print("Date = %s\n", buf);
		return TRUE;
	}

	if (strcmp(keyword, "Deleted") == 0) {
		if (! skip_whitespace(fp) || ! get_number(fp, &x)) {
			g_error("Cannot get deleted day");
			return FALSE;
		}
		g_print("%d/", x);
		if (! skip_whitespace(fp) || ! get_number(fp, &x)) {
			g_error("Cannot get deleted month");
			return FALSE;
		}
		g_print("%d/", x);
		if (! skip_whitespace(fp) || ! get_number(fp, &x)) {
			g_error("Cannot get deleted year");
			return FALSE;
		}
		g_print("%d\n", x);
		return TRUE;
	}

	if (strcmp(keyword, "Hilite") == 0) {
		if (! get_string(fp, buf) ) {
			g_error("Cannot get hilite data");
			return FALSE;
		}
		g_print("Hilite = %s\n", buf);
		return  TRUE;
	}

	if (strcmp(keyword, "Todo") == 0) {
		g_print("Todo\n");
		return TRUE;
	}


	if (strcmp(keyword, "Done") == 0) {
		g_print("Done\n");
		return TRUE;
	}

	return FALSE;
}

void parse_ical_file(char const *file)
{
	FILE *fp;
	int finished;
	char keyword[50];
	int file_major, file_minor;
	char c;
	int item_type;
	int incomplete_item;


	if ( (fp = fopen(file, "r")) == NULL) {
		g_error("couldn't open file");
		return;
	}

	finished = FALSE;

	if (!skip_whitespace(fp))
		return;

	if (! skip_chars(fp, "Calendar") || ! skip_whitespace(fp) ) {
		g_error("unable to find calendar file");
		fclose(fp);
		return;
	}

	if (! skip_chars(fp, opener) || ! skip_chars(fp, "v") ) {
		g_error("Unable to get version line");
		fclose(fp);
		return;
	}
	if (! get_number(fp, &file_major) || ! (file_major >=0) || (file_major > VersionMajor)) {
		g_error("Missing/bad major version");
		fclose(fp);
		return;
	}

	if (! skip_chars(fp, ".") || ! get_number(fp, &file_minor) ||
	    ! skip_chars(fp, "]") || ! skip_whitespace(fp) ) {
		g_error("Missing  minor version");
		fclose(fp);
		return;
	}
	if (file_minor > 0) {
		g_error("Bad  minor version");
		fclose(fp);
		return;
	}

	while(TRUE) {
		g_print("----------------------------------------\n");
		item_type= 0;
		skip_whitespace(fp);
		if (! getid(fp,keyword) || ! skip_whitespace(fp) ||
		    ! skip_chars(fp, opener) || ! skip_whitespace(fp) ) {
			fclose(fp);
			return;
		}

		if (strcmp(keyword, "Appt") == 0) {
			g_print("New Appointment\n");
			item_type = 1;

		} else if (strcmp(keyword, "Note") == 0) {
			g_print("New Note\n");
			item_type = 2;
		} else 
			g_print("New ??? (%s)\n", keyword);

		incomplete_item = TRUE;
		while(incomplete_item) {
			if (! skip_whitespace(fp) || ! peek_char(fp, &c)) {
				g_warning("Incomplete item\n");
				fclose(fp);
				return;
			}
			if (c == closer[0]) {
				(void)fgetc(fp);
				g_print("done!\n");
				incomplete_item = FALSE;
				break;
			}
		
			if (! getid(fp,keyword) || ! skip_whitespace(fp) ||
			    ! skip_chars(fp, opener) ) {
				g_error("Error reading item property name");
				fclose(fp);
				return;
			}
			if ( ! parse_item(fp, keyword) && ! parse_appointment(fp, keyword) ) {
				g_warning("Unable to parse line\n");
				fclose(fp);
				return;
			}
			if ( ! skip_whitespace(fp) || ! skip_chars(fp, closer)) {
				g_error("Error reading item property");
				fclose(fp);
				return;
			}
		} /* while */
	} /* while */
}
		
	


int main(int argc, char *argv[])
{

	parse_ical_file("/home/csmall/.calendar");
	return 0;
}