// ClockView.m
// By Jayson Adams, NeXT Developer Support Team
// You may freely copy, distribute and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.
#import "ClockView.h"
#import "Date.h"

#import <appkit/appkit.h>
#import <math.h>
 
static void MinuteSecondTimer(port_t timerPort)
{
msg_header_t nullMsg, updateMsg;
port_t aPort;
kern_return_t error;

	if ( port_allocate(task_self(), &aPort) != KERN_SUCCESS )
		cthread_exit(0);
	/* Set the thread's priority. */
	error = cthread_priority(cthread_self(), 0, FALSE);
	if ( error != KERN_SUCCESS )
		mach_error("Call to cthread_priority() failed", error);

	while( (int)cthread_data(cthread_self()) != 0 )
	{
		nullMsg.msg_local_port = aPort;
		nullMsg.msg_size = sizeof(msg_header_t);
		/* wait until timeout */
		msg_receive(&nullMsg, RCV_TIMEOUT, 1000);
		/* Now send an update message to the appkit thread */
		updateMsg.msg_simple = TRUE;
		updateMsg.msg_size = sizeof(msg_header_t);
		updateMsg.msg_type = MSG_TYPE_NORMAL;
		updateMsg.msg_local_port = PORT_NULL;
		updateMsg.msg_remote_port = timerPort;
		updateMsg.msg_id = 0;
		msg_send(&updateMsg, MSG_OPTION_NONE, 0);
	}
	port_deallocate(task_self(), aPort);
	cthread_exit(0);
}

@implementation ClockView

static void UpdateHandler(msg_header_t *msg, ClockView *self)
{
Date *date = self->dateObj;
NXRect dummy;

	/* If we are not visible do nothing */
	if( [self getVisibleRect: &dummy] == NO )
		return;

	/* Display the current time */
	self->t_now = [date sysTime];
	
	self->t_min = (self->t_now - self->t_start) / 60;
	self->t_sec = self->t_now - self->t_start - 60 * self->t_min;
	self->t_min %= 60;

	[self display];
}


- initFrame:(const NXRect *)frameRect
{
NXSize oneSize, twoSize;
int i;
char *numberNames[] = {"DigitalZero", "DigitalOne", "DigitalTwo",
    				  "DigitalThree", "DigitalFour", "DigitalFive",
				  "DigitalSix", "DigitalSeven", "DigitalEight",
				  "DigitalNine", ""};
				  
	[super initFrame : frameRect];
	
/* get the images we'll use in drawSelf:: */
	clockImage = [NXImage findImageNamed : "Clock"];
	
	for (i = 0; *numberNames[i]; i++)
			numbers[i] = [NXImage findImageNamed:numberNames[i]];

	colon = [NXImage findImageNamed : "Colon"];
	amImage = [NXImage findImageNamed : "AM"];
	pmImage = [NXImage findImageNamed : "PM"];
	
/* compute the colon's location (it never changes) */
	[numbers[1] getSize : &oneSize];
	[numbers[2] getSize : &twoSize];
	colonPosition_clock.x = oneSize.width + twoSize.width + 4.0;
	colonPosition_clock.y = ceil((bounds.size.height - twoSize.height) / 2.0); 
	/* Center the ':' for the timer */
	colonPosition_timer.x = bounds.size.width / 2.0;
	colonPosition_timer.y = colonPosition_clock.y;

	/* Init the date to the current time */
	dateObj = [[Date alloc] initFromNow];
	/* Check my time to see if it is AM or PM */
	if ([dateObj hours] < 12)
		meridianImage = amImage;
	else
		meridianImage = pmImage;

	return self;
}

- free
{
	if( timerActive == YES )
	{
		DPSRemovePort(timerPort);
		port_deallocate(task_self(), timerPort);
		cthread_set_data(timerCthread, (any_t) 0);
	}

	return [super free];
}

/* Date initialization methods */
- setDate : (short) d : (short) m : (short) y
{
	if([dateObj initFromDate : d : m : y] == nil)
		return nil;
	/* Check my time to see if it is AM or PM */
	if ([dateObj hours] < 12)
		meridianImage = amImage;
	else
		meridianImage = pmImage;
	[self display];
	return self;
}

- setTime : (short) hr : (short) min : (short) sec
{
	if([dateObj setTime : hr : min : sec] == nil)
		return nil;
	/* Check my time to see if it is AM or PM */
	if ([dateObj hours] < 12)
		meridianImage = amImage;
	else
		meridianImage = pmImage;
	[self display];
	return self;
}

- now
{
	if([dateObj initFromNow] == nil)
		return nil;
	[self display];
	return self;
}

- startMinSecTimer : sender
{
	t_start = [dateObj sysTime];
	if( timerPort == PORT_NULL )
	{	/* Create a port and add the port handler. If timerPort is not
			PORT_NULL, we assume we are just resetting the timer. */
		port_allocate(task_self(), &timerPort);
		DPSAddPort(timerPort, (DPSPortProc) UpdateHandler, sizeof(msg_header_t), self,
			NX_MODALRESPTHRESHOLD +1);
		timerCthread = cthread_fork((cthread_fn_t) MinuteSecondTimer, (any_t) timerPort);
		cthread_set_data(timerCthread, (any_t) 1);
		cthread_detach(timerCthread);
		timerActive = YES;
	}
	[self display];

	return self;
}

- endTimer : sender
{
	if( timerActive == YES )
	{
		DPSRemovePort(timerPort);
		port_deallocate(task_self(), timerPort);
		cthread_set_data(timerCthread, (any_t) 0);
		timerPort = PORT_NULL;
		timerCthread = (cthread_t) 0;
	}
	timerActive = NO;

	return self;
}

- drawSelf:(NXRect *) rects :(int) count
{
NXPoint	position;
NXSize	size, hoursTensSize = {0.0, 0.0}, hoursUnitsSize;
int t0,t1;

	/* Set the hour & minute (minute & second) positions
		depending on the timer state */
	if(timerActive == YES)
	{
		t0 = t_min;
		t1 = t_sec;
	}
	else
	{
		t0 = [dateObj hours];
		if(t0 > 12)
			t0 %= 12;
		t1 = [dateObj minutes];
	}

/* draw the background */
	[clockImage composite:NX_SOVER toPoint:&(bounds.origin)];

/* AM or PM */
	if(timerActive == NO)
	{
		[meridianImage getSize:&size];
		position.x = bounds.size.width - size.width - 4.0;
		position.y = floor((bounds.size.height - size.height) / 2.0);
		[meridianImage composite:NX_SOVER toPoint:&position];
	}

/* compute starting position for all drawing;  the colon is stationary */
	if(timerActive == NO)
		position = colonPosition_clock;
	else
		position = colonPosition_timer;

	if (t0 / 10)
	{
		[numbers[t0 / 10] getSize:&hoursTensSize];
	}
	[numbers[t0 % 10] getSize:&hoursUnitsSize];
	position.x -= hoursTensSize.width + hoursUnitsSize.width;
	
/* draw the hours tens digit */
	if (t0 / 10)
	{
		[numbers[t0 / 10] composite:NX_SOVER toPoint:&position];
		position.x += hoursTensSize.width;
	}
	
/* hours units digit */
	[numbers[t0 % 10] composite:NX_SOVER toPoint:&position];
	position.x += hoursUnitsSize.width;
	
/* draw the colon */  
	[[colon getSize:&size] composite:NX_SOVER toPoint:&position];
	position.x += size.width;
	
/* tens digit of minutes */
	[[numbers[t1 / 10] getSize:&size] composite:NX_SOVER
								toPoint:&position];
	position.x += size.width;
	
/* minutes units digit */
	[numbers[t1 % 10] composite:NX_SOVER toPoint:&position];
	
	return self;
}

@end
/* RCS Information:
	$Author: me $;
	$Date: 93/02/23 02:00:26 $;
	$Source: /usr1/me/NeXTSrc/MyClasses/RCS/ClockView.m,v $;
	$Revision: 1.1 $;
*/
