
	/* these functions select the next group to be observed from the
	   list of already-created and sorted groups. Note that the
	   'select_group()' function may return a group which is not
	   to be observed immediately, but only after some time has gone by
	   so that the UT is equal to the group's "utstart" field. 
	   (If the returned group's "utstart" field is < 0.0, then the
	   object IS to be observed immediately.) */

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


#ifdef VERBOSE
extern char *progname;
#endif

void set_clock( /* int day, month, year, double ut */ );
void set_jclock( /* double jd */ );
void read_clock( /* double *jd, int sim_flag */ );
void advance_clock( /* double seconds */ ); 
static int is_visible( /* struct s_group *grp */ );
struct s_group * select_group( /* struct s_group *list[], 
			int num_grp, priority, double end_jd */ );
static double get_random();
void till_visible( /* struct s_group *grp, double clock_jd, *jd_till */ );

#define TIME_BITS     10		/* # of times to check for new objects  */
                        		/*   popping over horizon while waiting */
                        		/*   for a fixed-UT observation         */
#define START_PRIOR -1000		/* signals we need to read_min_priority() */

	/* this "clock" is used to simulate the passage of time over the
	   course of a night, so that we can examine the group lists
	   generated without actually having to wait all night long */
static int clock_day, clock_month, clock_year;
static double clock_jd;

	/* set a "clock" to the given Julian Date and Universal Time */

void
set_clock(day, month, year, ut)
int day, month, year;
double ut;
{
	clock_day = day;
	clock_month = month; 
	clock_year = year;
	get_jd(day, month, year, ut, &clock_jd);
}

	/* set the clock to a given JD */

void
set_jclock(jd)
double jd;
{
	clock_jd = jd;
}

	/* read the time from the fake "clock" variable if 'sim_flag' == 1, or
	   from the real system time if 'sim_flag' == 0. */

void
read_clock(jd, sim_flag)
double *jd;
int sim_flag;
{
	double ut;

	if (sim_flag == 1)
		*jd = clock_jd;
	else {
		get_ut(&ut);
		get_jd(clock_day, clock_month, clock_year, ut, jd);
	}		
}

	/* advance the "clock" by the given number of seconds */

void
advance_clock(seconds)
double seconds;
{
	clock_jd += seconds/86400.0;
}

	/* return 1 if the given group is 'currently' visible, or 0 otherwise */

static int
is_visible(grp)
struct s_group *grp;
{
	if (grp->vis_flag == 0)
		return(0);
	else if (grp->vis_flag == 1)
		return((grp->jd_start[0] <= clock_jd) && (grp->jd_end[0] >= clock_jd));
	else {
		if ((grp->jd_start[0] < clock_jd) && (grp->jd_end[0] > clock_jd))
			return(1);
		else if ((grp->jd_start[1] < clock_jd) && (grp->jd_end[1] > clock_jd))
			return(1);
		else
			return(0);
	}
}
	
	/* select the next group to be observed at the given priority.
	   return a pointer to the group, or NULL if there is no 
	   group to observe at this priority. 

	   Actually, what this function does is return the next group which
	   should be observed at ANY priority; it works like this:
	       1. first, check to see if there are any groups of the given
	          proirity which can be observed, and finished, before the
	          given JD endtime.  If there is one, return it.

	       2. If not, or if we'd have to wait for the next group, try
	          getting a group which has a less important priority - 
	          but only if it would fit into the space between NOW and
	          the time of the next important group (if there is one).
	          If there's any such less important group, return it.

	       3. if there's just NOTHING which can be observed right now,
	          return the next group which should be observed later on.
	          The calling routine must figure out how long to wait itself.

	       4. If there's NOTHING to observe, for the rest of the night, 
	          return NULL.

	   Oh, one more thing - before returning any group, make a check 
	   against its probability; that is, choose a random number between
	   0 and 1, and, if the number is LESS than the group's probability,
	   then return the group. if the number is MORE, then mark the group
	   as already observed (or picked and discarded?) and go on to the
	   next one.
	 */

struct s_group *
select_group(list, num_grp, priority, end_jd)
struct s_group *list[];
int num_grp, priority;
double end_jd;
{
	static int level = 0;
	int i, num_first, ok_flag;
	static int min_priority = START_PRIOR;
	double fixed_jd, dt, min_till, test_jd;
	struct s_group *grp, *grp2, *first, *fixed;
	struct s_group *till_grp = NULL;

#ifdef DEBUG
	level++;
	printf("entering select, level %d, pri %d, end_jd %.4lf\n", level,
			priority, end_jd);
#endif

	if (min_priority == START_PRIOR) {
		if (read_min_priority(&min_priority) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.select_group: can't read_min_priority()\n",
					progname);
#endif
			level--;
			return((struct s_group *) NULL);
		}
	}

	/* check to make sure that we're not beyond the lowest priority */
	if (priority > min_priority) {
#ifdef DEBUG
		printf("  select bottoms out at priority %d, returns NULL\n",
				priority);
#endif
		level--;
		return((struct s_group *) NULL);
	}

	/* skip down to the groups with this priority */	
	for (i = 0, grp = list[0]; (i < num_grp) && (grp->priority < priority); i++)
		grp = list[i + 1];

	/* skip past any that have already been observed */
	for ( ; (i < num_grp) && (grp->priority == priority); i++) {
		if (grp->done_yet == 0) {
			break;
		}
		grp = list[i + 1];
	}

	if (i == num_grp) {
#ifdef DEBUG
		printf("  select end of line, ret NULL, level %d pri %d end_jd %.4lf\n", 
				level, priority, end_jd);
#endif
		level--;
		return((struct s_group *) NULL);
	}

	if (grp->priority != priority) {
#ifdef DEBUG
		printf("  select goes to lower pri, level %d pri %d end_jd %.4lf\n",
				level, priority, end_jd);
#endif
		grp = select_group(list, num_grp, priority + 1, end_jd);
		level--;
#ifdef DEBUG
		printf("  select comes from lower pri, level %d pri %d end_jd %.4lf\n",
				level, priority, end_jd);
#endif
		return(grp);
	}

	first = grp;
	num_first = i;

	/* look for the first fixed-UT group at this priority which can 
	   be observed between now and the end_jd */
	ok_flag = 0;
	while ((i < num_grp) && (grp->priority == priority)) {
		if ((grp->utstart >= 0.0) && (grp->done_yet == 0)) {
			calc_jd(clock_jd, grp->utstart, &fixed_jd);
			if ((fixed_jd + grp->exp_time/86400.0 < end_jd) &&
					(fixed_jd > clock_jd)) {
				if (get_random() <= grp->probability) {
					ok_flag = 1;
					break;
				}
				else {
#ifdef DEBUG
					printf("  rejecting group with object ..%s.. due to probability\n",
							grp->object->name);
#endif
					grp->done_yet = 1;
				}
			}
		}
		grp = list[++i];
	}

	/* if there is a good fixed-UT candidate, try to find some other
	   object that can be observed between now and the time the fixed-UT
	   group should go */
	if (ok_flag == 1) {
#ifdef DEBUG
			printf("  calling select for fitter in before fixed_jd %.4lf\n",
					fixed_jd);
#endif
		if ((grp2 = select_group(list, num_grp, priority, fixed_jd)) == NULL) {
#ifdef DEBUG
			printf("  select fitter in, level %d, pri %d, end_jd %.4lf\n",
					level, priority, end_jd);
#endif
			level--;
			return(grp);
		}
		else {
#ifdef DEBUG
			printf("  select fixed & wait, level %d, pri %d, end_jd %.4lf\n",
					level, priority, end_jd);
#endif
			/* if this group can fit in before the fixed_jd, then return it.
			   otherwise, return the fixed_jd group */
			level--;
			if (clock_jd + (grp2->exp_time + SLOP_TIME)/86400.0 < fixed_jd) {
				return(grp2);
			}
			else {
				return(grp);
			}
		}
	}
		
	/* this is the "floating-group loop", which tries to find the next 
	   group from all which have no fixed-UT at the current priority */
	while (1) {
		min_till = 1.0;
		till_grp = (struct s_group *) NULL;
		for (i = num_first, grp = first; (i < num_grp) && 
				(grp->priority == priority); grp = list[++i]) {
#ifdef DEBUG
			printf("  next group at pri %d is %s\n", priority, 
						grp->object->name);
#endif
			if ((grp->done_yet == 1) || (grp->utstart >= 0.0)) {
#ifdef DEBUG
				printf("  skipping group %s: done already or utstart > 0.0\n",
						grp->object->name);
#endif
				continue;
			}
			if (!is_visible(grp)) {
#ifdef DEBUG
				printf("  skipping group %s: not visible yet \n",
						grp->object->name);
#endif
				till_visible(grp, clock_jd, &dt);
				if ((dt < min_till) && (dt > 0.0)) {
					min_till = dt;
					till_grp = grp;
				}
				continue;
			}
			if ((clock_jd + grp->exp_time/86400.0) >= end_jd) {
#ifdef DEBUG
				printf("  skipping group %s: sets before exposure over \n",
						grp->object->name);
#endif
				continue;
			}
			if (get_random() > grp->probability) {
#ifdef DEBUG
				printf("  rejecting group with object ..%s.. due to probability\n",
						grp->object->name);
#endif
				grp->done_yet = 1;
				continue;
			}
#ifdef DEBUG
			printf("  select floating grp, level %d, pri %d, end_jd %.4lf\n",
					level, priority, end_jd);
#endif
			level--;
			return(grp);
		}

#ifdef DEBUG
		printf("  nothing observable now at pri %d; ", priority);
		if (till_grp != NULL)
			printf("next is %s at jd=%lf\n", till_grp->object->name,
					clock_jd + min_till);
		else
			printf("\n");
#endif
	
		/* nothing at the current priority is observable now. try something
		   at a lower priority, but only if it will finish before the
		   beginning of the first higher-priority group's window */
#ifdef DEBUG
		printf("  calling select at lower priority %d -> %d, end_jd %.4lf\n",
				priority, priority + 1, end_jd);
#endif
		if (till_grp != NULL) {
			if ((grp = select_group(list, num_grp, priority + 1, 
						clock_jd + min_till)) != NULL) {
				till_visible(grp, clock_jd, &test_jd);
				test_jd += clock_jd;
				if (test_jd + (grp->exp_time + SLOP_TIME)/86400.0 < 
							clock_jd + min_till) {
#ifdef DEBUG
					printf("  fit in lower pri, level %d, pri %d, end_jd %.4lf\n",
							level, priority, clock_jd + min_till);
#endif
					level--;
					return(grp);
				}
			}
			/* if no lower-priority group could be fit in, make the 
			   probability test on the 'till_grp' NOW - if it fails, go
			   back to the top of the floating-group loop */
			if (get_random() <= till_grp->probability) {
#ifdef DEBUG
				printf("  returning till_grp %s\n", till_grp->object->name);
#endif
				level--;
				return(till_grp);
			}
			else {
#ifdef DEBUG
				printf("  skipping till group %s due to probability \n",
						till_grp->object->name);
#endif
				till_grp->done_yet = 1;
				continue;
			}
		}
		else {
			if ((grp = select_group(list, num_grp, priority + 1, 
						end_jd)) != NULL) {
#ifdef DEBUG
				printf("  selected lower pri, level %d, pri %d, end_jd %.4lf\n",
						level, priority, end_jd);
#endif
				level--;
				return(grp);
			}
#ifdef DEBUG
			printf("  select_group at pri %d fails; returning NULL\n",
					priority);
#endif
			level--;
			return((struct s_group *) NULL);
		}

		/* if we get here, then there's nothing to observe for the 
		   rest of the night.  So return NULL */
#ifdef DEBUG
		printf("  end of the line - no objects. returning NULL\n");
#endif
		level--;
		return((struct s_group *) NULL);
	}

#ifdef OOOLLLDDDDDD
		/* there seems to be NOTHING observable right now.  So return the next
		   group that WILL be available in a while - if there is one. If not,
		   return NULL. */
		if (clock_jd < end_jd) {
			if (till_grp != NULL) {
#ifdef DEBUG
				printf("  returning till group, wait %.4lf, cur jd %.4lf, end jd %.4lf\n",	
						min_till, clock_jd, end_jd);
#endif
				level--;
				return(till_grp);
			}
		}

	/* there really is NOTHING else to observe up to the end_jd. */
#ifdef DEBUG
	printf("  select return NULL, level %d, pri %d, end_jd %.4lf\n",
			level, priority, end_jd);
#endif
	level--;
	return((struct s_group *) NULL);
#endif OOOOOLLLLLLDDDDD
}
	

	/* return a random number beween 0 and 1 */

#define RAND_MAX 32768.0

static double
get_random()
{
	static long t = 0;

	if (t == 0) {
		t = time((long *) 0);
		srand((unsigned int) t);
	}
	return((double)rand()/(double)RAND_MAX);
}


	/* return the amount of time between the current clock_jd and the 
	   beginning of the next jd window for the given group (thus the
	   time is in units of days) in the argument 'jd_till'. A positive
	   number means the group will rise in the future, while a negative
	   number means it won't rise again tonight. place -1.0 in 'jd_till'
	   for groups which aren't visible at all, or have some problem. */

void
till_visible(grp, clock_jd, jd_till)
struct s_group *grp;
double clock_jd, *jd_till;
{
	double t1, t2;
	
	if (grp->vis_flag == 0) {
		*jd_till = -1.0;
		return;
	}
	if (grp->vis_flag == 1) {
		*jd_till = grp->jd_start[0] - clock_jd;
		return;
	}
	t1 = grp->jd_start[0] - clock_jd;
	t2 = grp->jd_start[1] - clock_jd;
	if ((t1 > 0) || (t2 < 0)) 
		*jd_till = t1;
	else if ((t1 < 0) || (t2 > 0))
		*jd_till = t2;
	else if ((t1 < 0) && (t2 < 0))
		*jd_till = (t1 < t2 ? t2 : t1);
	else
		*jd_till = (t1 < t2 ? t1 : t2);
}
