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


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

GSList *eventlist;

void print_glist(gpointer data, gpointer user_data)
{
	struct event *myevent = (struct event*)data;

	if (data == NULL)
		return;
	printf ("===============================================\nNew event\n");
	printf ("Start: %s %02d:%02d  End: %s %02d:%02d\n", myevent->start.date, myevent->start.time / 60, myevent->start.time % 60, myevent->end.date, myevent->end.time / 60, myevent->end.time % 60);
	printf ("Contents: %s\n", myevent->description);
	printf ("Repeat = %d (%d)", (int)(myevent->repeat), myevent->repeatcount);
}


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) {
		        *buf = '\0';
			return TRUE;
	        }
		*buf = (char)c;
		buf++;
	}
	*buf = '\0';
	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;
}	

/* Get string until EOF or closer_char */
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 get_dates(FILE *fp, char *keyword, struct event *ptr)
{
	char *c;
	int x;

	if (strncmp("Single", keyword, 6) == 0) {
		ptr->repeat = Single;
		/* It's a single date */
		if (! skip_whitespace(fp) || !get_until(fp, ' ', ptr->start.date))
			return FALSE;
		if (! skip_chars(fp, "End"))
			return FALSE;
		return TRUE;
	} else if (strncmp("Days", keyword, 4) == 0) {
		ptr->repeat = Days;
		if (! skip_whitespace(fp) || !get_until(fp, ' ', ptr->start.date))
			return FALSE;
		if (! skip_whitespace(fp) || !get_number(fp, &(ptr->repeatcount)))
			return FALSE;
		if (! skip_chars(fp, "End"))
			return FALSE;
		return TRUE;
	}

	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, struct event *ptr, 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 = %02d:%02d\n", x/60, x % 60);
		ptr->start.time = 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);
		ptr->end.time = ptr->start.time + 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, struct event *ptr, 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);
		strcpy(ptr->description,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 ( ! getid(fp, buf)) {
			g_error("Cannot get date");
			return FALSE;
		}
		return get_dates(fp, buf,ptr);
	}

	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;
	struct event *myevent;

	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;
		myevent = g_malloc0(sizeof(struct event));
		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;
				g_slist_append(eventlist, myevent);
				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, myevent, keyword) && ! parse_appointment(fp, myevent, 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[])
{

	eventlist = g_slist_alloc();
	parse_ical_file("/home/csmall/.calendar");
	g_slist_foreach(eventlist, print_glist, NULL);
	return 0;
}