/*
  * 9clock.c: a basic graphical clock for plan 9
  * sean caron (scaron@umich.edu)
*/

/*
  * The paradigm here is that we must always include u.h
  * first and libc.h second, and anything else after that.
*/

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <cursor.h>
#include <event.h>

int main(void);
void eresized(int new);

int main(void) {
	Point center, minend, hrend, o_minend, o_hrend;
	Tm *thetime;
	int xdim, ydim, fd;
	double mintheta, hrtheta;
	char *buttons[] = { "quit", 0 };
	Menu menu = { buttons };
	Mouse m;
	Event e;
	ulong timer, evtype;

	initdraw(nil, nil, "9clock");

	/*
	  * initialize event handler for mouse events only. we
	  * must call this subsequent to initdraw().
	*/

	einit(Emouse);

	/* we need to call this resize handler at least once */

	eresized(0);

	/*
	  * we need to set up this timer event because when you use
	  * the event() system call, it basically blocks until some sort
	  * of event occurs. we use the timer event in lieu of a real
	  * event occurring or else the clock would not update until
	  * the user moved the mouse or clicked a button.
	*/

	timer = etimer(0,10);

	/*
	  * initdraw() sets up some global variables,
	  *
	  * Display *display
	  * Image *screen
	  * Screen *_screen
	  * Font *font
	  *
	  * ZP is a constant point (0,0).
	*/

	/* resize the current window. */

	fd = open("/dev/wctl", OWRITE);

	fprint(fd, "resize -dx 200 -dy 200");

	close(fd);

	getwindow(display, Refnone);

	/* initialize some variables. */

	xdim = (screen->r.max.x - screen->r.min.x) / 2;
	ydim = (screen->r.max.y - screen->r.min.y) / 2;

	center.x = screen->r.min.x + xdim;
	center.y = screen->r.min.y + ydim;

	/* main program loop */

	while (1) {

		evtype = event(&e);

		/* look for mouse events */

		if (evtype == Emouse) {

			m = e.mouse;

			/* if left mouse button is clicked, show the menu */

			if (m.buttons == 4) {
			
				/* quit */

				if (emenuhit(3, &m, &menu) == 0) {
					closedisplay(display);
					exits(0);
				}

			}
		}

		/* use the faux timer event to update the clock if there is no real input */

		else if (event(&e) == timer) {

			/* draw outline of clock face. */
	
			ellipse(screen, center, xdim, ydim, 2, display->black, center);

			thetime = localtime(time(nil));

			/* correct 24 hour system time to 12 hour clock. */

			if (thetime->hour > 12) {
				thetime->hour = thetime->hour - 12;
			}

			/*
			  * 6 degrees = 0.104718 rads per minute on the clock face.
			  * 30 degrees = 0.523590 rads per hour on the clock face.
			  *
			  * we subtract the value from PI/2 to correct between standard
			  * form and clock form.
			*/

			mintheta = (PI/2.0) - (0.104718 * (double)thetime->min);
			hrtheta = (PI/2.0) - (0.523590 * (double)thetime->hour);

			/*
			  * calculate the endpoints for the minute and hour hands.
			  * we resize the window to be square so xdim and ydim are actually the same.
			  * we scale down the hour hand to make it shorter than the minute hand.
			*/

			minend.x = center.x + (double)xdim*cos(mintheta);
			minend.y = center.y - (double)ydim*sin(mintheta);

			hrend.x = center.x + (double)xdim*cos(hrtheta);
			hrend.y = center.y - (double)ydim*sin(hrtheta);

			/*
			  * if the time has changed since the last time we looped, get rid of the stale minute or
			  * hour hands.
			*/

			if ( (o_minend.x != minend.x) || (o_minend.y != minend.y) ) {
				line(screen, center, o_minend, Endsquare, Endarrow, 1, display->white, center);
			}

			if ( (o_hrend.x != hrend.x) || (o_hrend.y != hrend.y) ) {
				line(screen, center, o_hrend, Endsquare, Endarrow, 2, display->white, center);
			}

			/* draw the current minute and hour hands. */

			line(screen, center, minend, Endsquare, Endarrow, 1, display->black, center);

			line(screen, center, hrend, Endsquare, Endarrow, 2, display->black, center);

			/* save the minute and hour hand coordinates for comparison the next time around. */

			o_minend = minend;
			o_hrend = hrend;

			/* let some other processes get a word in edgewise. */

			sleep(1);

			/*
			  * the draw (2) manpage suggests that this is not required, but the graphics will not
			  * show up unless we use it.
			*/

			flushimage(display,1);
		}
	}	
}

/*
  * by convention we must declare an eresized() function to
  * handle the window resizing event.
*/

void eresized(int new) {

	/* just refresh the window for now. */

	getwindow(display, Refnone);
}
