
	/* read information from a request file into memory so that other
	   routines can figure out whether, and when, to observe this object. */

#include <stdio.h>
#include <math.h>
#include "bait.h"
#include "pf.h"
#include "list.h"


struct s_group *read_request( /* char *rqsfile */ );
static int do_item( /* pf_handle pf_in, struct s_group *group */ );
static int check_group( /* struct s_group *grp */ );
void find_jds( /* double jd, uts, ute, *jds, *jde */ );

#ifndef PI
#define PI 3.14159265359
#endif  PI
#define DEGTORAD(x)    ((x)*(PI/180.0))
#define RADTODEG(x)    ((x)*(180.0/PI))

#ifdef VERBOSE
extern char *progname;
#endif

	/* try to open the given file - if successful, allocate space for
	   a new group structure and try to fill up all
	   the information in the structure we need from the file.

	   returns a pointer to the group if all OK, or NULL if there was 
	   an error of some sort. */

struct s_group *
read_request(rqsfile)
char *rqsfile;
{
	int i, num_hdr, month, day, year;
	pf_handle pf_in;
	char val[PF_LEN + 1], buf[PF_LEN + 1];
	struct s_group *group;

	if ((group = (struct s_group *) malloc(sizeof(struct s_group))) == NULL) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: can't malloc for group\n",
				progname);
#endif
		return((struct s_group *)NULL);
	}

	if ((num_hdr = pf_headers(rqsfile)) < 1) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: pf_headers returns < 1\n", progname);
#endif
		return((struct s_group *)NULL);
	}
	if ((pf_in = pf_open(rqsfile, 1, PF_EXIST)) == PF_ERROR) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: can't pf_open file %s\n", progname,
				rqsfile);
#endif
		return((struct s_group *)NULL);
	}

	/* first, get the information that is tha same for all requests in this
	   file */
	if ((pf_getval(pf_in, "REQID", val) == NULL) ||
	    (pf_valtostr(val, buf) < 0)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: can't get value for REQID ..%s..\n",
				progname, val);
#endif
		return((struct s_group *) NULL);
	}
	strcpy(group->reqid, buf);

	if ((pf_getval(pf_in, "DAYSTART", val) == NULL) || 
		(read_date(val, &day, &month, &year) < 0)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: can't get value for EXPTIME ..%s..\n",
				progname, val);
#endif
		return((struct s_group *)NULL);
	}
	group->daystart = since_1950(day, month, year);

	if ((pf_getval(pf_in, "DAYEND", val) == NULL) || 
		(read_date(val, &day, &month, &year) < 0)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: can't get value for EXPTIME ..%s..\n",
				progname, val);
#endif
		return((struct s_group *)NULL);
	}
	group->dayend = since_1950(day, month, year);
	
	if (pf_getval(pf_in, "UT-START", val) == NULL)
		group->utstart = -1;
	else {
		if (pf_valtostr(val, buf) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.read_request: bad format for UT-START ..%s..\n",
					progname, val);
#endif
			return((struct s_group *) NULL);
		}
	    if (read_timestr(buf, &(group->utstart)) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.read_request: bad value for UT-START ..%s..\n",
					progname, buf);
#endif
			return((struct s_group *)NULL);
		}
	}

	if ((pf_getval(pf_in, "NUM-OBS", val) == NULL) ||
	    (pf_valtoint(val, &(group->num_obs)) < 0)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: bad value for NUM-OBS ..%s..\n",
				progname, val);
#endif
		return((struct s_group *)NULL);
	}

	if ((pf_getval(pf_in, "PRIORITY", val) == NULL) ||
	    (pf_valtoint(val, &(group->priority)) < 0)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: bad value for priority ..%s..\n",
				progname, val);
#endif
		return((struct s_group *)NULL);
	}

	if ((pf_getval(pf_in, "PROBABIL", val) == NULL) ||
	    (pf_valtodouble(val, &(group->probability)) < 0)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: bad value for probability ..%s..\n",
				progname, val);
#endif
		return((struct s_group *)NULL);
	}
	if ((pf_getval(pf_in, "MOONPHAS", val) == NULL) ||
	    (pf_valtodouble(val, &(group->moon_max)) < 0)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_request: bad value for moon_max ..%s..\n",
				progname, val);
#endif
		return((struct s_group *)NULL);
	}

	/* all the above information pertains to the group as a whole. the 
	   next thing to do is to read in the particulars for each item in the
	   group */
	group->exp_time = 0.0;
	group->object = (struct s_object *) NULL;
	group->done_yet = 0;

	for (i = 1; i <= num_hdr ; i++) {
		if (do_item(pf_in, group) < 0)
			return((struct s_group *)NULL);

#ifdef DEBUG
	printf("  group  ..%20s.. has %1d windows ", group->object->name, 
				group->vis_flag);
	if (group->vis_flag == 0)
		printf("\n");
	if (group->vis_flag >= 1)
		printf("%12.3lf .. %12.3lf \n", group->jd_start[0], group->jd_end[0]);
	if (group->vis_flag == 2)
		printf("%48s%12.3lf .. %12.3lf \n", " ", group->jd_start[1], group->jd_end[1]);
#endif

		if (pf_close(pf_in) == PF_ERROR) {
#ifdef VERBOSE
			fprintf(stderr, "%s.read_request: error pf_closing file %s, item %d\n",
					progname, rqsfile, i);
#endif
			return((struct s_group *)NULL);
		}
		if (i < num_hdr) {
			if ((pf_in = pf_open(rqsfile, i + 1, PF_EXIST)) == PF_ERROR) {
#ifdef VERBOSE
				fprintf(stderr, "%s.read_request: error pf_opening file %s, item %d\n",
						progname, rqsfile, i + 1);
#endif
				return((struct s_group *)NULL);
			}
		}
	}	

	/* check the group to make sure that all the objects in it are visible,	
	   and the UT start time, if present, if within the group window, and
	   the moon isn't in the way, etc. */
	if (check_group(group) != 0) {
		group->vis_flag = 0;		/* make sure that it isn't observed */
	}	

	/* now we've got all the information we need in an 's_group' structure */
	return(group);
}


	/* read into the passed group structure all the information we need to
	   know for the individual item in the header referenced by 'pf_in'.
	   Allocate space for the 's_object' structure inside this routine.
	   Also combine the observation window of this new object with
	   that of the group. 

	   Note the way that observation windows in JD are assigned: if the user
	   explicitly gives a UT starting time, that UT time is used; if she
	   gives an explicit LST for start and end, those times (converted into
	   JD) are used, WITHOUT checking to see if the object is above the
	   horizon at that time; otherwise, the times at which the object rises
	   above/sets below a certain altitude are used as the window.

	   return 0 if all OK, or -1 if there was a problem. */

static int
do_item(pf_in, group)
pf_handle pf_in;
struct s_group *group;
{
	int i, day, month, year;
	double epoch, magnitude, uts, ute, jd, max_airmass, alt, sigtonoise;
	double night_start, night_end;
	char val[PF_LEN + 1], buf[PF_LEN + 1];
	struct s_object *obj, *op;

	if ((obj = (struct s_object *) malloc(sizeof(struct s_object))) == NULL) {
#ifdef VERBOSE
		fprintf(stderr, "%s.do_item: can't malloc for s_object\n", progname);
#endif
		return(-1);
	}
	obj->lst_start = -1.0;
	obj->lst_end = -1.0;

	if ((pf_getval(pf_in, "OBJECT", val) == NULL) ||
	    (pf_valtostr(val, obj->name) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.do_item: bad value for OBJECT ..%s..\n",
				progname, val);
#endif
		return(-1);
	}

	/* it's a little tricky to get the exposure time, since the user may 
	   specify either EXPTIME directly, or MAGNITUDE (and SIGTONOI); 
	   if the latter, then
	   we have to figure out the conversion from magnitude to exposure time,
	   which depends on the filter. */

	if (pf_getval(pf_in, "EXPTIME", val) == NULL) {
		if (pf_getval(pf_in, "MAGNITUD", val) == NULL) {
#ifdef VERBOSE
			fprintf(stderr, "%s.do_item: neither EXPTIME nor MAGNITUDE given\n",
						progname);
#endif
			return(-1);
		}
	    else if (pf_valtodouble(val, &magnitude) == PF_ERROR) {
#ifdef VERBOSE
			fprintf(stderr, "%s.do_item: bad value for MAGNITUDE ..%s..\n",
					progname, val);
#endif
			return(-1);
		}
		if ((pf_getval(pf_in, "SIGTONOI", val) == NULL) ||
		    (pf_valtodouble(val, &sigtonoise) == PF_ERROR)) {
			sigtonoise = 0.0;		/* causes default value to be used */
		}
		if ((pf_getval(pf_in, "FILTERS", val) == NULL) ||
		    (pf_valtostr(val, buf) == PF_ERROR)) {
#ifdef VERBOSE
			fprintf(stderr, "%s.do_item: bad value for FILTERS ..%s..\n",
					progname, val);
#endif
			return(-1);
		}
		if (mag2exptime(magnitude, sigtonoise, buf, &(obj->exp_time)) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.do_item: invalid magnitude ..%lf.. filter ..%s.. combination\n", 
					progname, magnitude, buf);
#endif
			return(-1);
		}
	}
	else if (pf_valtodouble(val, &(obj->exp_time)) == PF_ERROR) {
#ifdef VERBOSE
		fprintf(stderr, "%s.do_item: bad value for EXPTIME ..%s..\n",
				progname, val);
#endif
		return(-1);
	}
	group->exp_time += (obj->exp_time + SLOP_TIME);	
 
	if ((pf_getval(pf_in, "RA", val) == NULL) ||
	    (val_ra2deg(val, &(obj->ra)) < 0)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.do_item: bad value for RA ..%s..\n", 
				progname, val);
#endif
		return(-1);
	}
	if ((pf_getval(pf_in, "DEC", val) == NULL) ||
	    (val_dec2deg(val, &(obj->dec)) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.do_item: bad value for DEC ..%s..\n", 
				progname, val);
#endif
		return(-1);
	}
	if ((pf_getval(pf_in, "EPOCH", val) == NULL) ||
	    (pf_valtodouble(val, &epoch) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.do_item: bad value for EPOCH ..%s..\n", 
				progname, val);
#endif
		return(-1);
	}
	if ((pf_getval(pf_in, "MAXAIRMA", val) == NULL) ||
	    (pf_valtodouble(val, &max_airmass) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.do_item: bad value for MAXAIRMA ..%s..\n", 
				progname, val);
#endif
		return(-1);
	}
	/* figure out the altitude limit which gives this airmass */
	if (max_airmass < 1.0)
		alt = 0.0;
	else alt = 90.0 - RADTODEG(acos(1.0/max_airmass));

	/* check to see if the user specified any LST limits explicitly - if so,
	   then use the LST window as the ONLY criterion for the object window.
	   Object altitude is irrelevant - so there is no guarantee at all that
	   the object will even be visible during this window.  Too bad. */
	if (pf_getval(pf_in, "LSTSTART", val) != NULL) {
		if ((pf_valtostr(val, buf) < 0) || 
	       (read_timestr(buf, &(obj->lst_start)) < 0)) {
#ifdef VERBOSE
			fprintf(stderr, "%s.do_item: bad format for LSTSTART ..%s..\n",
					progname, val);
#endif
			return(-1);
		}
		if ((pf_getval(pf_in, "LSTEND", val) == NULL) ||
		    (pf_valtostr(val, buf) < 0) || 
		    (read_timestr(buf, &(obj->lst_end)) < 0)) {
#ifdef VERBOSE
			fprintf(stderr, "%s.do_item: no LSTEND, or bad format ..%s..\n",
					progname, val);
#endif
			return(-1);
		}
	}

	/* now transform the RA and Dec to epoch J2000.0 */
	to_j2000(epoch, obj->ra, obj->dec, &(obj->ra), &(obj->dec));

	/* and now figure out the beginnings and ends of the JD window(s) for
	   this object, for the current or next observing period (note how
	   we set the variable 'jd' to the JD of midnight on the next night. */
	get_today(&day, &month, &year);
	get_ut(&uts);
	get_jd(day, month, year, uts, &jd);
	night_limits(jd, -TWI_DEGREES, &night_start, &night_end);
	jd = (night_start + night_end)/2.0;

	/* if there is an explicit LST window, figure out the limits imposed
	   by it and use them (without checking for actual visibility) */
	if (obj->lst_start != -1.0) {
		if ((lst2ut(jd, obj->lst_start, &uts) < 0) ||
		    (lst2ut(jd, obj->lst_end, &ute) < 0)) {
#ifdef VERBOSE
			fprintf(stderr, "%s.do_item: can't convert lst times to ut\n",
					progname);
#endif
			return(-1);
		}
		obj->vis_flag = 1;
		obj->win_used[0] = 1;
		obj->win_used[1] = 0;
		find_jds(jd, uts, ute, &(obj->jd_start[0]), &(obj->jd_end[0]));
	}
	else {
		/* otherwise, if no LST window supplied by user, calculate the 
		   beginning and ending times of visibility */
		 if (obj_jdlimits(jd, obj->ra, obj->dec, alt, &(obj->vis_flag), 
			&(obj->jd_start[0]), &(obj->jd_end[0]), &(obj->jd_start[1]), 
			&(obj->jd_end[1])) != 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.do_item: obj_jdlimits returns error\n", 
					progname);
#endif
			return(-1);
		}
	}

	/* shift the window ends backwards so that there's enough time to make
	   the exposure (plus slew telescope, etc.), and delete a window if this
	   subtraction moves its end before the beginning. */
	obj->win_used[0] = 0;
	obj->win_used[1] = 0;
	for (i = 0; i < obj->vis_flag; i++)
		obj->win_used[i] = 1;
	if (obj->vis_flag == 2) {
		obj->jd_end[1] -= (obj->exp_time + SLOP_TIME)/86400.0;
		if (obj->jd_end[1] <= obj->jd_start[1])
			obj->vis_flag = 1;
	}
	if (obj->vis_flag >= 1) {
		obj->jd_end[0] -= (obj->exp_time + SLOP_TIME)/86400.0;
		if (obj->jd_end[0] <= obj->jd_start[0]) {
			if (--obj->vis_flag == 1) {
				obj->jd_start[0] = obj->jd_start[1];
				obj->jd_end[0] = obj->jd_end[1];
			}
		}
	}

#ifdef DEBUG
	printf("  object ..%20s.. has %1d windows ", obj->name, obj->vis_flag);
	if (obj->vis_flag == 0)
		printf("\n");
	if (obj->vis_flag >= 1)
		printf("%12.3lf .. %12.3lf \n", obj->jd_start[0], obj->jd_end[0]);
	if (obj->vis_flag == 2)
		printf("%48s%12.3lf .. %12.3lf \n", " ", obj->jd_start[1], obj->jd_end[1]);
#endif

	/* now recalculate the group observation windows, if necessary */
	if (group->object == NULL) {
		group->vis_flag = obj->vis_flag;
		group->win_used[0] = 0;
		group->win_used[1] = 0;
		for (i = 0; i < group->vis_flag; i++) {
			group->win_used[i] = obj->win_used[i];
			group->jd_start[i] = obj->jd_start[i];
			group->jd_end[i] = obj->jd_end[i];
		}
	}
	else {
		/* don't bother if the group is already not observable */
		if (group->vis_flag != 0) {
			if (fit_object(obj, group) == -1) {
#ifdef DEBUG
				printf("  object ..%s.. won't fit in group \n", obj->name);
#endif
			}
		}
	}

	/* stick this object at the end of the linked list of objects 
	   in the group */
	obj->next = (struct s_object *) NULL;
	if ((op = group->object) == NULL) 
		group->object = obj;
	else {
		while (op->next != NULL)
			op = op->next;
		op->next = obj;
	}

	return(0);
}

	/* check the group for any inconsistencies - return 0 if there are 
	   none, or 1 if there is something which will prevent the group
	   from being observed. return -1 if there is a program error of
	   some kind. */

static int
check_group(grp)
struct s_group *grp;
{
	int day, month, year;
	double ut, jd, night_start, night_end, night_mid;
	static double moon_rise = 0.0, moon_set = 0.0, phase = 0.0;

	if (moon_rise == 0.0) {
		get_today(&day, &month, &year);
		get_ut(&ut);
		get_jd(day, month, year, ut, &jd);
		night_limits(jd, -TWI_DEGREES, &night_start, &night_end);
		night_mid = (night_start + night_end)/2.0;
		if (moon_riseset(night_mid, &moon_rise, &moon_set) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.check_group: can't calculate moon times for JD %15.5lf\n",
					progname, night_mid);
#endif
			return(-1);
		}	
		moon_phase(night_mid, &phase);
	}

	if (grp->vis_flag == 0)
		return(1);

	/* make sure that the utstart time of the group falls within its
	   observing window. If not, return 1.  If it does, then
	   modify the window so that it is just one second wide and 
	   centered on the UT time specified. */
	if (grp->utstart >= 0.0) {
#ifdef DEBUG
		printf("originally jd_start %.4lf, jd_end %.4lf \n", grp->jd_start[0],
				grp->jd_end[0]);
#endif
		calc_jd(grp->jd_start[0], grp->utstart, &jd);
		if ((jd < grp->jd_start[0]) || (jd > grp->jd_end[0])) {
			if (grp->vis_flag == 1) {
#ifdef DEBUG
				printf("  oops - now invisible \n");
#endif
				return(1);
			}
			if ((jd < grp->jd_start[1]) || (jd > grp->jd_end[1])) {
#ifdef DEBUG
				printf("  oops - now invisible \n");
#endif
				return(1);
			}
		}
		grp->vis_flag = 1;
		grp->win_used[1] = 0;
		grp->jd_start[0] = jd;
		grp->jd_end[0] = jd + 1.0/86400.0;
#ifdef DEBUG
		printf("now        jd_start %.4lf, jd_end %.4lf \n", grp->jd_start[0],
				grp->jd_end[0]);
#endif 
	}

	/* make sure that the group avoids the moon, if it must */
	if (phase > grp->moon_max) {
		if (avoid_moon(moon_rise, moon_set, grp) < 0) {
			grp->vis_flag = 0;
			return(1);
		}
	}

	return(0);
}

	/* given the current JD and a start and end UT, calculate the JDs 
	   which correspond to the given UTs during the next observing
	   period */

void
find_jds(jd, uts, ute, jds, jde)
double jd, uts, ute, *jds, *jde;
{
	double night_start, night_end, night_mid, jd_base;

	night_limits(jd, -TWI_DEGREES, &night_start, &night_end);
	night_mid = (night_start + night_end)/2.0;
	jd_base = floor(night_mid + 0.5) - 0.5;

	*jds = jd_base + uts/24.0;
	*jde = jd_base + ute/24.0;
	if (*jde < *jds)
		*jde += 1.0;
}
