//
// Event.m
// Copyright (c) 1988, 1989, 1990 by Jiro Nakamura 
// All rights reserved
//
// Implementation definition of class Event. Event handles 
// the database management of Cassandra.
//
// RCS Information
// Revision Number->	$Revision: 2.11 $
// Last Revised->	$Date: 91/11/01 17:24:00 $
//
static char rcsid[] = "$Id: Event.m,v 2.11 91/11/01 17:24:00 jiro Exp Locker: jiro $";

#import "Event.h"
#import <objc/objc.h>
#import <strings.h>
#import <appkit/Panel.h>       /* for NXRunAlertPanel */
#import "cass.h"	
#import "calendar.h"
#import "Global.h"
#import "misc.h"
#import <sys/types.h>
#import <sys/stat.h>
    
// Defining DEBUG for Event can sometimes get very screen messy
// since Cassandra uses it so often
#ifdef DEBUG
	#undef DEBUG
	#define DEBUG2
#endif

#define VERSION_15	1
#define	EVENT_VERSION	1

int fgeti( int *i , 	FILE *fp)		
{
	char buffer[10];
	if( fgets(buffer, 10, fp) == NULL || strlen( buffer ) < 1)
		{
		*i = 0;
		return -1;
		}
	*i = atoi(buffer);
	return 0;
}
		

@implementation Event

+ newAt:(const char*) eFile
{
	self = [super new];
	[self	initOurStuff:eFile];
	return self;
}

- initOurStuff: (const char *) eFile	  // used to set our-own default values
{
	strcpy(eventFile,eFile);
	
	eventType = 0;
	priority = 50;
	destroy = snoozeNo = snoozeInt = 0;
	anniversary =  annvSpecial = 0;
	present = next = previous = 0;
	
	strcpy(alarmSound, ALARMSOUND);
	strcpy(msg,"empty");
	return self;
}

+ new
{
	fprintf(stderr,"This (+new for Event) should never be called!\n");
	fprintf(stderr,"%s\n", rcsid);	// dumb reference to rcsid, so
					// we avoid a compiler warning.
	self = [super new];
	return self;
}

- free
{
#ifdef DEBUG
	fprintf(stderr,"Event.m:Free ->Closing event!\n");
#endif
	[super free];
	return nil;
}

//
//
// Messages that affect how the file is stored on disk
//
//

- default: sender
{
	present =  0;
	previous = next = -1;
	destroy =  1;
	anniversary = annvSpecial = snoozeInt = snoozeNo = priority = 0;
	ts.tm_sec =  ts.tm_min =  ts.tm_hour = 0;
	ts.tm_mday = ts.tm_mon =  ts.tm_year = 0;
	ts.tm_wday =  ts.tm_yday =  ts.tm_isdst = 0;
	alarmAction = playSound;
	
	strcpy(msg, "");
	strcpy(alarmSound,[global alarmSound]);
	return self;
}


- readEvent: (EFileLink) here		/* read itself from the event file */
{
	FILE * source;
	static char errormsg[128], buffer[255];
	char *tp;	// temporary pointer
	struct stat statBuf;
	int tmp;
	
	#ifdef DEBUG
		fprintf(stderr,	"------------------------\n"
					"[Event readEvent: %d]\n",here);
	#endif

	if( here < 0)
		{
		NXRunAlertPanel("File Seek Error", 
				"Cannot seek to a negative number.",
		 		"OK",NULL,NULL);
		strcpy(msg, "Illegal event/link value....");
		return nil;
		}
		
	/* Let's first check to make sure we aren't reading an empty file */
	stat( eventFile, &statBuf);
	if( statBuf.st_size < 100)	// It can not be this short
		{
		strcpy(msg, CASSANDRAVERSION);
		next = previous = 0;
		present = here;					
		[self writeEvent: here];
		fprintf(stderr, "%s: Event list not initialized. "
			"Initializing....\n",
			PROGNAME);
		return nil;
		}

	sprintf(errormsg, "Error occured while trying to read event file %s", 
		eventFile);
	source = fileOpen(eventFile, "r+",errormsg);
	
	sprintf(errormsg, 	"An fatal error occured when we were trying "
				"to seek to event number %d for reading event "
				"file %s.\n This program has crashed.", 
				here, eventFile);
	if( fileSeek(source, here, errormsg) == NULL)
		{
		next = previous = -1;
		present = here;
		return nil;
		}
	
	fgeti(&present, source);
	if( present != here)
		{
		fprintf(stderr, 
			"%s: File header gives %d instead of %d.\n",
			PROGNAME , present, here);
		fprintf(stderr, 
			"%s: We've most probably run out off the end of %s.\n",
			PROGNAME, [global eventFile]);
		next = previous = -1;
		present = here;
		fclose(source);
		return nil;
		}
	else
		{
		fgeti(&previous, source);
		fgeti(&next, source);
		}

	fgeti( &tmp, source);
	version = tmp / 100;
	eventType = tmp % 100;
	fgets( buffer, 10, source);
	
	fgeti( &priority, source);
	
	switch( version)
		{
		case VERSION_15:
			fgeti( &annvSpecial, source);
			fgeti( &anniversary, source);
			break;
		case 0:
		default:
			fgeti( &destroy, source);
			fgeti( &anniversary, source);
			break;
		}
	#ifdef DEBUG2
		fprintf(stderr, "%s Event: Version = %d, ",
			PROGNAME, version);
		fprintf(stderr, "Anniv. = %03d, ", anniversary);
		fprintf(stderr, "AnnivSpecial = %03d\n", annvSpecial);
	#endif

	fgeti( &snoozeNo, source);
	fgeti( &snoozeInt, source);
	
	fgets( buffer, 10, source);
	fgets( buffer, 10, source);
	
	fgeti( &ts.tm_sec, source);
	fgeti( &ts.tm_min, source);
	fgeti( &ts.tm_hour, source);
	fgeti( &ts.tm_mday, source);
	fgeti( &ts.tm_mon, source);
	fgeti( &ts.tm_year, source);
	
	switch( version)
		{
		case VERSION_15:
			fgeti( &duration, source);
			fgeti( &tmp, source);
			alarmAction = tmp;
			fgeti( &ts.tm_isdst, source);
			break;
		case 0:
		default:
			fgeti( &ts.tm_wday, source);
			fgeti( &ts.tm_yday, source);
			fgeti( &ts.tm_isdst, source);
			break;
		}
	
	fgets( alarmSound, 100, source);	// We gotta strip the final
	tp = rindex( alarmSound, '\n');		// newline and replace it with
	if( tp != NULL)				// a NULL character.
		*tp = '\0';
	
	fgets( buffer, 10, source);
	fgets( buffer, 10, source);
	fgets( buffer, 10, source);
	fgets( buffer, 10, source);

	fgets(msg,MESSAGE_SIZE,source);
				
	ts.tm_wday = wday(ts.tm_mday, ts.tm_mon+1, 1900+ts.tm_year);
	ts.tm_yday = yday(ts.tm_mday, ts.tm_mon+1, ts.tm_year+1900);

	fixTmStructure( &ts);
	
	switch( version)
		{
		case VERSION_15:
			break;
					
		default:
		case 0:
			switch( alarmSound[0] )
				{
				case '-':		// don't play alarm
					strcpy(buffer, alarmSound);
					strcpy(alarmSound, buffer+1);
					alarmAction = nothing;
					break;
				case '?':
					strcpy(buffer, alarmSound);
					strcpy(alarmSound, buffer+1);
					alarmAction = playAndDeleteSound;;
				break;
				case '!':
					strcpy(buffer, alarmSound);
					strcpy(alarmSound, buffer+1);
					alarmAction = runCommand;
					break;
				}
			break;
		}
		
		
	if( msg[0] == ';')
		{
		char buf[MESSAGE_SIZE];
		
		strcpy(buf, msg);
		strcpy(msg, buf+1);
		showMessage = FALSE;
		}
	else
		showMessage = TRUE;
			
	fclose(source);
	return self;
}
				
- firstEvent
{
	#ifdef DEBUG
		fprintf(stderr,"Reading in the first event.\n");
	#endif

	[self readEvent : 0];
	if( [self next] == 0)
		return self;
	[self readEvent : [self next]];
	return self;
}	
	
- writeEvent: (EFileLink) here	
/* write itself into the event file using present as its index */
{
	FILE *source;
	char errormsg[256], bufAlarm[256], bufMessage[256];
		
	if( here < 0)
		{
		NXRunAlertPanel( "Write Error", 
				"We can't write to a negative event number.",
				"OK",NULL,NULL);
		return nil;
		}
		
	#ifdef DEBUG
		fprintf(stderr,	"------------------------\n"
				"[Event writeEvent: %d]: Opening event with "
				"<%s>!\n",here, eventFile);
	#endif

	sprintf(errormsg, "Fatal error when trying to write to event file %s", 
		eventFile);
	source = fileOpen(eventFile, "r+",errormsg);
	
	sprintf(errormsg, "%s: An fatal error occured when we were trying to "
			"seek to event number %d for writing in event file %s.\n"
			" This program has crashed.", PROGNAME, here, eventFile);
	fileSeek(source, here, errormsg);
	
	present = here;
	
	if( ts.tm_year > 1900)
		ts.tm_year -= 1900;

	strcpy(bufAlarm, alarmSound);

	if( showMessage == FALSE)
		{		
		strcpy(bufMessage, ";");
		strcat(bufMessage, msg);
		}
	else
		strcpy(bufMessage, msg);

	#ifdef DEBUG2
		fprintf(stderr, "%s Event: wAnniversary = %d, ",
			PROGNAME, anniversary);
		fprintf(stderr, "wAnniversarySpecial = %d\n", annvSpecial);
	#endif


	if(fprintf( source, 		
					"%d\n%d\n%d\n"
					"%d\n\n"
					"%d\n%d\n%d\n"
					"%d\n%d\n"
					"\n\n"
					"%d\n%d\n%d\n"
					"%d\n%d\n%d\n"
					"%d\n%d\n%d\n"
					"%s\n\n"
					"\n\n\n"
					"%s\n",
					present, previous, next,
					EVENT_VERSION * 100 + eventType,
					priority, annvSpecial, anniversary,
					snoozeNo, snoozeInt,
					ts.tm_sec, ts.tm_min, ts.tm_hour,
					ts.tm_mday, ts.tm_mon, ts.tm_year,
					duration, alarmAction, ts.tm_isdst,
					bufAlarm, bufMessage) == -1)
		{
		fprintf(stderr, "%s: Event.m:   Error occured writing file.",
			 PROGNAME);
		strcpy(msg, "Error occured in writing out Elink number.\n\n");
		fclose(source);
		return nil;
		}
	
		#ifdef DEBUG
			fprintf(stderr, "%s: Wrote out Efilelink.\n  Here=%d,"
				"Previous = %d, Next=%d\n", PROGNAME,
				here, previous, next);
			fprintf(stderr, "%s: Date:   time = %d:%d, day= %d, "
				"month = %d, year = %d.\n\n",PROGNAME,
				ts.tm_hour, ts.tm_min, ts.tm_mday, ts.tm_mon, 
				ts.tm_year);
			fprintf(stderr,"%s: AlarmSound =%s\n", PROGNAME, bufAlarm);
		#endif
		
	fclose(source);
	return self;
}

- (EFileLink) insertEvent
{
	return [self insertEventFrom : 1];
}



/* insert itself into the file with current day as index */
/* and starting from <here> in searching for a open deleted space */
- (EFileLink) insertEventFrom : (EFileLink) here
{
	id event, step;
	int loop;
	

	// First things first, we must check the format of our time structure  
	// since we have given other functions full freedom to scew it up

	fixTmStructure( &ts);
			
	event = [Event newAt:eventFile];
	step = [Event newAt:eventFile];
	[event readEvent:0];
	for( [event readEvent: [event next]]; 
	      [event present] != 0 && timeCompare([event time], &ts) < 0;
	      [event readEvent:[event next]])
	      	{
	      	#ifdef DEBUG
			fprintf(stderr, "From %d looping onto %d\n",
				[event present],[event next]);
		#endif
		}
		
		
	if( timeCompare([event time], &ts)  == 0 )
		{
		if( NXRunAlertPanel("Scheduling Conflict?",  
			"You have another "
			"scheduled event at that time. Should I insert this "
			"event anyway?", "Insert anyway", "Cancel it", 
			NULL) == 0 )
			[event free];
			[step free];
			return 0;
		}
			
			
			
	#ifdef DEBUG	
		fprintf(stderr, "inserting between %d <- event -> %d.\n",
			[event previous], [event present]);
	#endif
	
	previous = [event previous];
	next = [event present];
	
	[step setNext: 0];
	for( loop = here; !([step next] == -1 && [step previous] == -1); [step readEvent: loop++])
		{
		#ifdef DEBUG
			fprintf(stderr, "reading at %d.\n",loop);
		#endif
		}
		
	present = [step present];
	
	[self writeEvent: present];
	
	[step readEvent: previous];
	[step setNext : present];
	[step writeEvent:previous];
	
	[step readEvent: next];
	[step setPrevious : present];
	[step writeEvent:next];
	
	[step free];
	[event free];
	#ifdef DEBUG
		fprintf(stderr,"successfully inserted %d <- %d -> %d\n\n",[self previous],[self present], [self next]);
	#endif

	
	return( [self present] );
}

/* delete itself from the file with present as index */
- deleteEvent  : (EFileLink) here 
{
	id previousEvent, nextEvent;
	
	#ifdef DEBUG
		fprintf(stderr, "Deleting <%d>\n\n",here);
	#endif
	
	if( here <= 0)
		{
		NXRunAlertPanel(NULL,"You cannot delete link 0 or below. OK?",
				 "Shucks...",NULL,NULL);
		fprintf(stderr,"Can't delete the header.  Here = %d.\n\n",
			 here);
		return nil;
		}
	
	previousEvent= [Event newAt: eventFile];
	nextEvent = [Event newAt:eventFile];
	
	[previousEvent readEvent  :here];
	if( ( [previousEvent previous] == -1  )   || 
	    ( [previousEvent next]  == -1  )		)
		{
		/*   link was already deleted, so we can't delete it..... */
		strcpy(msg, "Can't delete link because it's already been "
			"deleted!?!\n\n");
		return nil;
		}
	
	/* Set the previous_link of the next link */
	/* to the one of the previous one */					
	[nextEvent readEvent :[previousEvent next]];	
	[nextEvent setPrevious  : [previousEvent previous]];			
	[nextEvent writeEvent  :[previousEvent next]];	
	
	/* do the same confusing spiel for previous */
	[nextEvent readEvent  :[previousEvent previous] ];	
	[nextEvent setNext  : [previousEvent next]];
	[nextEvent writeEvent  :[previousEvent previous] ];
	
	/* cancel out the header to finally kill it */
	/* and write it out */
	[previousEvent setNext  : -1];	
	[previousEvent setPrevious  :-1];	
	[previousEvent writeEvent  :here];	
	
	#ifdef DEBUG
		fprintf(stderr, "Record %d successfully deleted.\n\n", 
			here);
	#endif
	
	[previousEvent free];
	[nextEvent free];
	return self;
}

/* delete if it is not an anniversary event, update otherwise */
- (int) murderEvent : (EFileLink) here
{
	Event *event;
	
	#ifdef DEBUG
		fprintf(stderr,"Arrggh! Help! I'm getting murdered! "
			"(says Event <%d>)\n\n",
			here);
	#endif
	
	event = [Event newAt :eventFile];
	[event readEvent : here];		//	Read the event in
	[event deleteEvent : here];		//	Delete the event
	
	if( (int) ([event anniversary]/100) == 0)		// No anniversary
		return -1;
		
	[event setTime: fixAnniversary( [event time], [event anniversary], 
		[event annvSpecial])];
	// 	Fix the next event time and reinsert it
								
	// Use the same EFilelink space that we deleted, this saves 
	// us time and maybe some confusion 
	return( [event insertEventFrom:here]  );
}


- setMday : (int) x	{ ts.tm_mday = x;	return self; }
- setMon: (int) x	{ ts.tm_mon = x; return self; }
- setYear: (int) x	{ ts.tm_year = x; return self; }
	
- setHour: (int) x	{ ts.tm_hour = x;	return self; }
- setMin: (int) x	{ ts.tm_min = x; return self; }
- setSec: (int) x	{ ts.tm_sec = x; return self; }
- setDestroy : (int) dst	{ destroy = dst; return self; }
- setAnniversary : (int) anv	{ anniversary = anv; return self; }
- setAnnvSpecial : (int) anvS	{ annvSpecial = anvS; return self; }
- setSnoozeNo : (int) sn	{ snoozeNo = sn; return self; }
- setSnoozeInt : (int) si	{ snoozeInt = si; return self; }
- setPriority : (int) si 	{ priority = si; return self; }
- setAlarmSound : (const char *) aS	{ strcpy(alarmSound,aS); return self; }
- setShowMessage: (BOOL) aFlag	{ showMessage = aFlag; return self; }
- setMessage : (const char *) message { strcpy( msg, message); return self; }
- setWday : (int) wd		{ ts.tm_wday = wd; return self; }
- setYday : (int) yd		{ ts.tm_yday = yd;  return self; }
- setPresent : (EFileLink) apresent	{ present = apresent; return self; }
- setPrevious  : (EFileLink) aprevious	{ previous =aprevious; return self; }
- setNext  : (EFileLink) anext		{ next = anext; return self; }
- setTime : (struct tm *) time		{ ts = *time; return self; }


- setPlayAlarm: (BOOL) aFlag	{ alarmAction = playSound; return self; }
- setMailPerson:(BOOL) aFlag	{ alarmAction = mailPerson; return self; }
- setRunCommand: (BOOL) aFlag	{ alarmAction = runCommand; return self; }
- setDeleteSound: (BOOL) aFlag	{ alarmAction = playAndDeleteSound; 
					return self; }

- setAlarmAction: (int) aa	{ alarmAction = aa; return self; }

- (EFileLink) present	{ return present; }
- (EFileLink) previous	{ return previous; }
- (EFileLink) next	{ return next; }

- (char *) message	{ return msg; }
- (const char *) alarmSound	{ return alarmSound; }

- (BOOL) deleteSound	{ return alarmAction == playAndDeleteSound;	}
- (BOOL) playAlarm	{ return (alarmAction == playAndDeleteSound ||
				alarmAction == playSound) ; }
- (BOOL) runCommand	{ return  alarmAction == runCommand; }
- (BOOL) mailPerson	{ return alarmAction == mailPerson; }
- (BOOL) showMessage	{ return showMessage; }

- (int) alarmAction	{ return alarmAction; }

- (int) anniversary	{ return anniversary; }
- (int) annvSpecial	{ return annvSpecial; }
- (int) destroy		{ return destroy; }
- (int) hour		{ return (ts.tm_hour); }
- (int) mday;		{ return( ts.tm_mday); }
- (int) min		{ return(ts.tm_min); }
- (int) mon		{ return (ts.tm_mon); }
- (int) priority	{ return priority; }
- (int) sec		{ return(ts.tm_sec); }
- (int) snoozeInt	{ return snoozeInt; }
- (int) snoozeNo	{ return snoozeNo; }
- (int) wday		{ return ts.tm_wday; }
- (int) yday		{ return ts.tm_yday; }
- (int) year		{ return (ts.tm_year); }

- (struct tm *) time	{ return &ts; }

@end		


struct tm *fixAnniversary( struct tm *time, int mode, int sub)
{
	#ifdef DEBUG
	fprintf(stderr,"Fixing weekly special: %s, %d, %d\n",
		ascMyTime(time, YES, NO), 
		mode, sub);
	#endif

	switch( (int) mode  / 100)      
		{
		/* Don't worry about overflows as we add intervals to */
		/* our dates, insertEvent is smart enough to handle it */
		case 0:
			return time;	/* No anniversary */
		case 1:			/* If it is a day anniversary */
			time->tm_mday+=  (mode - 100);
			break;
		case 2:			/* Weekly anniversary */
			time -> tm_mday +=  ((mode - 200) * 7 );
			break;
		case 3:			/* Monthly anniversary */
			time-> tm_mon += (mode - 300);
			break;
		case 4:			/* Yearly anniversary */
			time -> tm_year +=  (mode - 400);
			break;
		case 5:			/* Special weekly */
		default:
			 fprintf(stderr,
				"%s: I do not recognize "
				"anniversary event: %d.\n", 
				PROGNAME,
			 	mode);
			 return time;
		}

	fixTmStructure( time);
	return time;
}


