/*-------------------------------------------------------------------------
	SunClock

	Amiga version by Mark Waggoner, 
	waggoner@ichips.intel.com or wagnell@PDaXcess.techbook.com
	December 1991

	Sun.c
	Routines to calculate and set the illumination mask for the sun clock
-------------------------------------------------------------------------*/
#include <time.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <functions.h>
#include <string.h>
#include <graphics/gfx.h>
#include "sunmath.h"

extern struct BitMap MapBitMap,IllumBitMap;
extern UWORD DataWidth,DataHeight;
extern WORD *Illum;
extern void (*MapRefresh)(void);
extern void (*TextRefresh)(void);
extern char	loctimestr[],gmtimestr[];

/*-------------------------------------------------------------------------
	Draw a horizontal line segment into the bitmap data
	x0 must be less than x1 and both must be > 0
-------------------------------------------------------------------------*/
Segment(UBYTE *line,int x0,int x1)
{
	UBYTE *sp,*ep,sm,em;

	sp = line + x0/8;
	ep = line + x1/8;
	sm = 0xff>>(x0%8);
	em = 0xff<<(7-x1%8);

	if (sp == ep) {
		*sp = (sm & em);
		return;
	}

	*sp |= sm;
	while (++sp<ep) *sp = 0xff;
	*ep |= em;
}

/*-------------------------------------------------------------------------
	Set the illumination mask bitmap.
	y is the row to set, 
	noon is the midpoint of the mask
	width is half the width of the mask
-------------------------------------------------------------------------*/
void
SetIllum(int noon,int y,int width)
{
	UBYTE *line;
	int x0,x1;

	if (width == 0) width = 1;
	if (width < 0) return;

	if (y >= DataHeight) return;

	line = (UBYTE *)IllumBitMap.Planes[0] + (y * ((DataWidth + 15) / 16) * 2);

	x0 = noon - width;
	x1 = noon + width;
	if (x0<0) {
		Segment(line,DataWidth+(x0%DataWidth),DataWidth-1);
		x0 = 0;
	}
	if (x1 >= DataWidth) {
		Segment(line,0,x1%DataWidth);
		x1 = DataWidth-1;
	}

	Segment(line,x0,x1);
}

/*-------------------------------------------------------------------------
	Project the suns illumination onto the bitmap mask
-------------------------------------------------------------------------*/
#define tableval(i,v)	if (i>=0 && i<DataHeight) { Illum[i] = ((v)==0) ? 1 : (v); }

static void
ProjectIllumination(int noon,double dec)
{
	int i,j, ftf = 1;
	int ilon, ilat, lilon, lilat, xt;
	double m, x, y, z, th, lon, lat, s, c;

	for(i=0;i<DataHeight;i++)
		Illum[i] = -1;

	/* Build transformation for declination */
	dec = dtr(dec);
	s = sin(-dec);
	c = cos(-dec);

	/* Increment over a semicircle of illumination */

	for (th = -(PI / 2); th <= PI / 2 + 0.001; th += PI / TERMINC) {

		/* Transform the point through the declination rotation. */

		x = -s * sin(th);
		y = cos(th);
		z = c * sin(th);

		/* Transform the resulting co-ordinate through the
		   map projection to obtain screen co-ordinates. */

		lon = (y == 0 && x == 0) ? 0.0 : rtd(atan2(y, x));
		lat = rtd(asin(z));

		ilat = DataHeight - (lat + 90) * (DataHeight / 180.0);
		ilon = lon * (DataWidth / 360.0);

		if (ftf) {		/* First time.  Just save start co-ordinate. */
			lilon = ilon;
			lilat = ilat;
			ftf = 0;
		} else {
			if (lilat == ilat) {	/* Set width */
				j = (DataHeight - 1) - ilat;
				tableval(j,ilon);
			} else {				/* interpolate intermediate points */
				m = ((double) (ilon - lilon)) / (ilat - lilat);
				for (i = lilat; i != ilat; i += sgn(ilat - lilat)) {
					xt = lilon + floor((m * (i - lilat)) + 0.5);
					j = (DataHeight - 1) - i;
					tableval(j,xt);
				}
			}
			lilon = ilon;
			lilat = ilat;
		}
	}

	/* Now tweak the widths to generate full illumination for
	   the correct pole. */

	if (dec < 0.0) {
		ilat = DataHeight - 1;
		lilat = -1;
	} else {
		ilat = 0;
		lilat = 1;
	}

	for (i = ilat; i != DataHeight / 2; i += lilat) {
		if (Illum[i] != -1) {
			while (1) {
				tableval(i,DataWidth/2);
				if (i == ilat)
					break;
				i -= lilat;
			}
			break;
		}
	}

	for(i=0;i<DataHeight;i++)
		SetIllum(noon,i,Illum[i]);

}

/*-------------------------------------------------------------------------
	Update the illumination if it has been long enough since the
	last update.
-------------------------------------------------------------------------*/
void
UpdateImages(int force)
{
	time_t			timeval;
	static time_t	lasttime = 0;
	struct tm		lt,*ct;
	double			jt,gt;
	double			sunra,sundec,sunrv,sunlong;
	double			lastdec;
	double			a;
	int				xl;
	static int		lastxl = -1;
	LONG			sec0,mic0,sec1,mic1;
	char			*tz;

	/* Find out what time it is */
	time(&timeval);
	lt = *localtime(&timeval);
	ct = gmtime(&timeval);
	if (!ct) ct = &lt;
	jt = jtime(ct);

	if (tz = getenv("TZ")) {
		strncpy(loctimestr,tz,3);
		loctimestr[3] = ':';
		loctimestr[4] = ' ';
		loctimestr[5] = '\0';
		strcat(loctimestr,asctime(&lt));
		loctimestr[strlen(loctimestr)-1] = '\0';
	}
	else
		strcpy(loctimestr,"TZ Environment variable not set!");

	strcpy(gmtimestr,"GMT: ");
	strcat(gmtimestr,asctime(ct));
	gmtimestr[strlen(gmtimestr)-1] = '\0';

	TextRefresh();

	if ((timeval > lasttime) && ((timeval - lasttime) < 60) && !force) return;
	lasttime = timeval;

	sunpos(jt, 0, &sunra, &sundec, &sunrv, &sunlong);
	gt = gmst(jt);

/*	printf("RA: %g  DEC: %g  RV: %g  LONG: %g\n",sunra,sundec,sunrv,sunlong); */

	a = 180.0 + (sunra - (gt * 15));
	sunlong = fixangle(a);
	xl = sunlong * ((double) DataWidth / 360.0);

	/* If the subsolar point has moved at least one pixel, update
	   the illuminated area on the screen.	*/

	if ((lastxl != xl) || (fabs(sundec - lastdec) > 5) || force) {
		BltClear((char *)IllumBitMap.Planes[0],
					((DataWidth+15)/16)*2*DataHeight,1);
		ProjectIllumination(xl,sundec);

		BltBitMap(&MapBitMap,0,0,&IllumBitMap,0,0,
				DataWidth,DataHeight,0x60,0x01,0);

		(*MapRefresh)();
		lastxl = xl;
		lastdec = sundec;
	}
}
