/* this file contains functions to support iterative xephem searches.
 * we support several kinds of searching and solving algorithms.
 * the expressions being evaluated are compiled and executed from compiler.c.
 */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#if defined(__STDC__)
#include <stdlib.h>
#endif
#include <X11/Xlib.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/Separator.h>
#include <Xm/SelectioB.h>
#include <Xm/ToggleB.h>
#include <Xm/Text.h>

#include "P_.h"
#include "astro.h"
#include "circum.h"

extern Widget	toplevel_w;
#define	XtD	XtDisplay(toplevel_w)

extern Now *mm_get_now P_((void));
extern int any_ison P_((void));
extern int compile_expr P_((char *ex, char *errbuf));
extern int execute_expr P_((double *vp, char *errbuf));
extern int prog_isgood P_((void));
extern void all_selection_mode P_((int whether));
extern void compiler_log P_((char *name, double value));
extern void f_double P_((Widget w, char *fmt, double f));
extern void f_scansex P_((double o, char bp[], double *np));
extern void f_time P_((Widget w, double t));
extern void get_xmstring P_((Widget w, char *resource, char **txtp));
extern void hlp_dialog P_((char *tag, char *deflt[], int ndeflt));
extern void lst_selection P_((char *name));
extern void plt_selection P_((char *name));
extern void prompt_map_cb P_((Widget w, XtPointer client, XtPointer call));
extern void redraw_screen P_((int how_much));
extern void set_something P_((Widget w, char *resource, XtArgVal value));
extern void set_xmstring P_((Widget w, char *resource, char *txt));
extern void xe_msg P_((char *msg, int app_modal));

static void srch_create_form P_((void));
static srch_isup P_((void));
static srching_now P_((void));
static void srch_set_buttons P_((int whether));
static void srch_use_cb P_((Widget w, XtPointer client, XtPointer call));
static void srch_funcactivate_cb P_((Widget w, XtPointer client,
    XtPointer call));
static void srch_compile_cb P_((Widget w, XtPointer client, XtPointer call));
static void srch_help_cb P_((Widget w, XtPointer client, XtPointer call));
static void srch_fields_cb P_((Widget w, XtPointer client, XtPointer call));
static void srch_close_cb P_((Widget w, XtPointer client, XtPointer call));
static void srch_acc_ok_cb P_((Widget w, XtPointer client, XtPointer call));
static void srch_acc_cb P_((Widget wid, XtPointer client, XtPointer call));
static void srch_goal_cb P_((Widget w, XtPointer client, XtPointer call));
static void srch_on_off_cb P_((Widget w, XtPointer client, XtPointer call));
static srch_minmax P_((double Mjd, double v, double *tmincp));
static srch_solve0 P_((double Mjd, double v, double *tmincp));
static srch_binary P_((double Mjd, double v, double *tmincp));


/* the widgets we need direct access to */
static Widget srchform_w;
static Widget help_w;
static Widget acc_w;
static Widget field_w;
static Widget func_w;	/* contains the search function [to be] compiled */
static Widget err_w;
static Widget compile_w;
static Widget valu_w;	/* used to display most recent calulated value */
static Widget use_w;	/* use to select srch value for use in plotting */
static Widget on_w;	/* whether searching is active comes directly from this
			 * widget's XmToggleButtonGetState()
			 */

/* name is it appears when valu_w is selected for plotting or lising */
static char srchvname[] = "SrchValue";

static int (*srch_f)();		/* 0 or pointer to one of the search functions*/
static int srch_tmscalled;	/* number of iterations so far */
static double tmlimit = 1./60.;	/* search accuracy, in hrs; def is one minute */

static int srch_selecting;	/* whether our value is currently selectable */
static int srch_self_selection_mode;	/* flag to prevent self-selection */

/* called when the search menu is activated via the main menu pulldown.
 * if never called before, create and manage all the widgets as a child of a
 * form. otherwise, just toggle whether the form is managed.
 */
void
srch_manage ()
{
	if (!srchform_w)
	    srch_create_form();
	
	if (XtIsManaged(srchform_w))
	    XtUnmanageChild (srchform_w);
	else {
	    XtManageChild (srchform_w);
	    srch_set_buttons(srch_selecting);
	}
}

/* called by other menus as they want to hear from our buttons or not.
 * the "on"s and "off"s stack - only really redo the buttons if it's the
 * first on or the last off.
 * N.B. we cooperate with a flag from the Enable pushbutton to prevent
 *   being able use the search function result as a term in the search funtion.
 */
void
srch_selection_mode (whether)
int whether;	/* whether setting up for plotting or for not plotting */
{
	if (srch_self_selection_mode)
	    return;

	srch_selecting += whether ? 1 : -1;

	if (srchform_w && XtIsManaged(srchform_w))
	    if ((whether && srch_selecting == 1)     /* first one to want on */
		|| (!whether && srch_selecting == 0) /* last one to want off */)
		srch_set_buttons (whether);
}

/* called when other modules, such as data menu, have a button pushed
 * and we have asked (by enabling field buttons) that they inform us that
 * that button is to be included in the search function.
 * other modules that use buttons, such as plotting and listing, might ask
 * too so it might not really be us that wants to use it.
 */
void
srch_selection (name)
char *name;
{
	int ins;
	char *qname;

	if (!srchform_w
		|| !XtIsManaged(srchform_w)
		|| !XmToggleButtonGetState(field_w))
	    return;

	ins = XmTextGetInsertionPosition (func_w);
	qname = XtMalloc (strlen(name) + 3);	/* two '"' and \0 */
	(void) sprintf (qname, "\"%s\"", name);
	XmTextReplace (func_w, /* from */ ins, /* to */ ins, qname);
	XtFree (qname);

	/* move the focus right back to the search menu so the insertion point
	 * remains visible.
	 */
	XSetInputFocus (XtDisplay(srchform_w), XtWindow(srchform_w),
						RevertToParent, CurrentTime);
}

/* if searching is in effect call the search type function.
 * it might modify *tmincp according to where it next wants to eval.
 * (remember tminc is in hours, not days).
 * if searching ends for any reason it is also turned off.
 * if we are not searching but are plotting or listing we still execute the
 *   search function (if it is ok) and flog and display it.
 * return 0 if caller can continue or -1 if it is time to stop.
 */
int
srch_eval(Mjd, tmincp)
double Mjd;
double *tmincp;
{
	int s;

	if (prog_isgood() && any_ison()) {
	    char errbuf[128];
	    double v;
	    s = execute_expr (&v, errbuf);
	    if (s == 0) {
		f_double (valu_w, "%g", v);
		if (srching_now()) {
		    s = (*srch_f)(Mjd, v, tmincp);
		    srch_tmscalled++;
		}
	    } else {
		char srchmsg[256];
		(void) sprintf (srchmsg, "Search evaluation error: %.200s",
								    errbuf);
		xe_msg (srchmsg, 0);
	    }
	} else
	    s = 0;

	if (s < 0)
	    XmToggleButtonSetState(on_w, False, /*invoke cb*/True);

	return (s);
}

/* called by other systems to decide whether it is worth computing and
 *   logging values to the search system.
 * we say True whenever there is a validly compiled function and either we are
 *   searching (obviously) or the srch control menu is up.
 */
int
srch_ison()
{
	return (prog_isgood() && (srching_now() || srch_isup()));
}

/* called as each different field is written -- just tell the compiler
 * if we are interested in it.
 * we have to check if *anything* is on because we might be plotting/listing
 *   the srch function itself.
 */
void
srch_log (name, value)
char *name;
double value;
{
	if (any_ison())
	    compiler_log (name, value);
}

/* called to put up or remove the watch cursor.  */
void
srch_cursor (c)
Cursor c;
{
	Window win;

	if (srchform_w && (win = XtWindow(srchform_w))) {
	    Display *dsp = XtDisplay(srchform_w);
	    if (c)
		XDefineCursor (dsp, win, c);
	    else
		XUndefineCursor (dsp, win);
	}
}

static void
srch_create_form()
{
	static struct {
	    char *title;
	    XtPointer cb_data;
	} rbtb[] = {
	    {"Find Extreme", (XtPointer)srch_minmax},
	    {"Find 0", (XtPointer)srch_solve0},
	    {"Binary", (XtPointer)srch_binary},
	};
	XmString str;
	Widget w, rc_w, f_w, rb_w;
	Arg args[20];
	int i, n;

	/* create form dialog */
	n = 0;
	XtSetArg (args[n], XmNautoUnmanage, False); n++;
	XtSetArg (args[n], XmNdefaultPosition, False); n++;
	srchform_w = XmCreateFormDialog (toplevel_w, "Srch", args, n);
	XtAddCallback (srchform_w, XmNhelpCallback, srch_help_cb, 0);

	/* set some stuff in the parent DialogShell.
	 * setting XmNdialogTitle in the Form didn't work..
	 */
	n = 0;
	XtSetArg (args[n], XmNtitle, "xephem Search Control"); n++;
	XtSetValues (XtParent(srchform_w), args, n);

	/* create a RowColumn to hold stuff */
	n = 0;
	XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNisAligned, False); n++;
	XtSetArg (args[n], XmNadjustMargin, False); n++;
	rc_w = XmCreateRowColumn (srchform_w, "SrchRC", args, n);
	XtManageChild (rc_w);

	/* searching on/off toggle button */

	n = 0;
	on_w = XmCreateToggleButton (rc_w, "Searching is Active", args, n);
	XtAddCallback (on_w, XmNvalueChangedCallback, srch_on_off_cb, 0);
	XtManageChild (on_w);

	/* create a separator */

	n = 0;
	w = XmCreateSeparator (rc_w, "Sep1", args, n);
	XtManageChild (w);

	/* compiler area title */

	str = XmStringCreate ("Search Function:",
						    XmSTRING_DEFAULT_CHARSET);
	n = 0;
	XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
	XtSetArg (args[n], XmNlabelString, str); n++;
	w = XmCreateLabel (rc_w, "FuncL", args, n);
	XtManageChild (w);

	/* function text.
	 * arrange for Return to activate the Compile button.
	 */

	n = 0;
	func_w = XmCreateText (rc_w, "SrchFunction", args, n);
	XtAddCallback (func_w, XmNactivateCallback, srch_funcactivate_cb, NULL);
	XtManageChild (func_w);

	/* compiler message label */

	n = 0;
	XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
	err_w = XmCreateLabel (rc_w, "SrchErrsL", args, n);
	XtManageChild (err_w);
	set_xmstring (err_w, XmNlabelString, " ");

	/* use-fields button */

	str = XmStringCreate("Enable field buttons", XmSTRING_DEFAULT_CHARSET);
	n = 0;
	XtSetArg (args[n], XmNlabelString, str); n++;
	field_w = XmCreateToggleButton (rc_w, "SrchFEnable", args, n);
	XtAddCallback (field_w, XmNvalueChangedCallback, srch_fields_cb, 0);
	XtManageChild (field_w);
	XmStringFree(str);

	/* COMPILE push button */

	n = 0;
	XtSetArg (args[n], XmNshowAsDefault, True); n++;
	compile_w = XmCreatePushButton (rc_w, "Compile", args, n);
	XtAddCallback (compile_w, XmNactivateCallback, srch_compile_cb, 0);
	XtManageChild (compile_w);
	set_something (srchform_w, XmNdefaultButton, (XtArgVal)w);

	/* create a separator */

	n = 0;
	w = XmCreateSeparator (rc_w, "Sep2", args, n);
	XtManageChild (w);

	/* create goal radio box and its toggle buttons */

	str = XmStringCreate ("Search goal:", XmSTRING_DEFAULT_CHARSET);
	n = 0;
	XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
	XtSetArg (args[n], XmNlabelString, str); n++;
	w = XmCreateLabel (rc_w, "SrchRBL", args, n);
	XtManageChild(w);
	XmStringFree(str);

	n = 0;
	XtSetArg (args[n], XmNorientation, XmVERTICAL); n++;
	rb_w = XmCreateRadioBox (rc_w, "SrchGoalRadioBox", args, n);
	XtManageChild (rb_w);

	    for (i = 0; i < XtNumber(rbtb); i++) {
		n = 0;
		str = XmStringCreate(rbtb[i].title, XmSTRING_DEFAULT_CHARSET);
		XtSetArg (args[n], XmNlabelString, str); n++;
		w = XmCreateToggleButton (rb_w, "SrchRBTB", args, n);
		XtAddCallback (w, XmNvalueChangedCallback, srch_goal_cb,
						    (XtPointer)rbtb[i].cb_data);
		XtManageChild (w);
		XmStringFree(str);
	    }

	/* create a separator */

	n = 0;
	w = XmCreateSeparator (rc_w, "Sep3", args, n);
	XtManageChild (w);

	/* Accuracy label and its push button in a form */

	n = 0;
	f_w = XmCreateForm (rc_w, "SrchAF", args, n);
	XtManageChild (f_w);

	    n = 0;
	    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	    acc_w = XmCreatePushButton (f_w, "SrchAcc", args, n);
	    XtAddCallback (acc_w, XmNactivateCallback, srch_acc_cb, 0);
	    f_time (acc_w, tmlimit);
	    XtManageChild (acc_w);

	    str = XmStringCreate("Desired accuracy:", XmSTRING_DEFAULT_CHARSET);
	    n = 0;
	    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
	    XtSetArg (args[n], XmNrightWidget, acc_w); n++;
	    XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
	    XtSetArg (args[n], XmNlabelString, str); n++;
	    w = XmCreateLabel (f_w, "SrchAccL", args, n);
	    XmStringFree (str);
	    XtManageChild (w);

	/* create a separator */

	n = 0;
	w = XmCreateSeparator (rc_w, "Sep4", args, n);
	XtManageChild (w);

	/* current search value in a form */

	n = 0;
	f_w = XmCreateForm (rc_w, "SrchVF", args, n);
	XtManageChild (f_w);

	    n = 0;
	    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	    valu_w = XmCreateLabel (f_w, "SrchValu", args, n);
	    XtManageChild (valu_w);
	    set_xmstring (valu_w, XmNlabelString, "");

	    str = XmStringCreate ("Current value: ", XmSTRING_DEFAULT_CHARSET);
	    n = 0;
	    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
	    XtSetArg (args[n], XmNrightWidget, valu_w); n++;
	    XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
	    XtSetArg (args[n], XmNlabelString, str); n++;
	    w = XmCreateLabel (f_w, "SrchValuL", args, n);
	    XtManageChild (w);
	    XmStringFree (str);

	    /* add the name in the userData field in case it's plotted or
	     * listed
	     */
	    set_something (valu_w, XmNuserData, (XtArgVal)srchvname);

	/* button to select using for plotting. 
	 * ONLY MANAGED WHEN WE ARE TOLD TO SELECT FOR PLOTTING.
	 */

	n = 0;
	use_w = XmCreatePushButton (rc_w, "SrchUse", args, n);
	XtAddCallback (use_w, XmNactivateCallback, srch_use_cb, 0);
	set_xmstring (use_w, XmNlabelString, "Use for plotting");

	/* create a separator */

	n = 0;
	w = XmCreateSeparator (rc_w, "Sep4", args, n);
	XtManageChild (w);

	/* form to hold bottom control buttons */

	n = 0;
	XtSetArg (args[n], XmNfractionBase, 7); n++;
	f_w = XmCreateForm (rc_w, "SrchCF", args, n);
	XtManageChild (f_w);

	    n = 0;
	    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
	    XtSetArg (args[n], XmNleftPosition, 1); n++;
	    XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
	    XtSetArg (args[n], XmNrightPosition, 3); n++;
	    w = XmCreatePushButton(f_w, "Close", args,n);
	    XtManageChild (w);
	    XtAddCallback(w, XmNactivateCallback, srch_close_cb, 0);

	    n = 0;
	    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	    XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
	    XtSetArg (args[n], XmNleftPosition, 4); n++;
	    XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
	    XtSetArg (args[n], XmNrightPosition, 6); n++;
	    help_w = XmCreatePushButton(f_w, "Help", args, n);
	    XtManageChild (help_w);
	    XtAddCallback(help_w, XmNactivateCallback, srch_help_cb, 0);
}

/* return True whenever the srch control menu is up */
static int
srch_isup()
{
	return (srchform_w && XtIsManaged(srchform_w));
}

/* return True whenever we are actually in the midst of controlling a search.
 */
static int
srching_now()
{
	return (on_w && XmToggleButtonGetState(on_w));
}

/* go through all the buttons pickable for plotting and set whether they
 * should appear to look like buttons.
 */
static void
srch_set_buttons (whether)
int whether;	/* whether setting up for plotting or for not plotting */
{
	if (whether)
	    XtManageChild (use_w);
	else
	    XtUnmanageChild (use_w);
}

/* callback from the "use" button.
 * if plotting or listing has put us in selecting mode, we look like a button
 * and we should inform them we have been picked.
 * otherwise, we do nothing (we didn't look like a button anyway).
 */
/* ARGSUSED */
static void
srch_use_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	static char msg[] =
	    "You must first successfully compile a search\nfunction before value may be selected.";

	if (srch_selecting) {
	    if (prog_isgood()) {
		plt_selection (srchvname);
		lst_selection (srchvname);
	    } else
		xe_msg (msg, 0);
	}
}

/* callback when Return is typed in the function text widget.
 * we just stimulate the Compile button as per Heller, pg 219.
 */
/* ARGSUSED */
static void
srch_funcactivate_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *) call;

	XtCallActionProc (compile_w, "ArmAndActivate", cbs->event, NULL, 0);
}

/* callback from the compile button.
 */
/* ARGSUSED */
static void
srch_compile_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	char *newexp;
	char errbuf[256];

	newexp = XmTextGetString (func_w);
	if (compile_expr (newexp, errbuf) <  0)
	    set_xmstring (err_w, XmNlabelString, errbuf);
	else
	    set_xmstring (err_w, XmNlabelString, "<no compile errors>");

	XtFree (newexp);

	/* as a courtesy, turn off searching if it's currently active */
	if (XmToggleButtonGetState(on_w))
	    XmToggleButtonSetState(on_w, False, True/*invoke cb*/);

	/* compute the new function.
	 * must update everything else first because our function uses that
	 *   stuff in all likelihood.
	 */
	if (prog_isgood()) {
	    Now *np = mm_get_now();
	    redraw_screen(1);
	    (void) srch_eval (mjd, (double *)NULL);
	}
}

/* callback from the help button.
 */
/* ARGSUSED */
static void
srch_help_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
static char *help_msg[] = {
"This menu controls the automatic searching facility. You define an arithmetic",
"or boolean function, using most of the fields xephem displays, then xephem",
"will automatically evaluate the function and adjust the time on each",
"iteration to search for the goal.",
"",
"To perform a search:",
"   enter a function,",
"   compile it,",
"   select a goal,",
"   set the desired accuracy,",
"   enable searching,",
"   perform the search by stepping xephem."
};

	hlp_dialog ("Search", help_msg, sizeof(help_msg)/sizeof(help_msg[0]));
}

/* callback from the "field enable" push button.
 * inform the other menues whether we are setting up for them to tell us
 * what fields to plot.
 */
/* ARGSUSED */
static void
srch_fields_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	int whether = XmToggleButtonGetState(w);;

	/* don't use our own result to srch with */
	srch_self_selection_mode = 1;
	all_selection_mode(whether);
	srch_self_selection_mode = 0;
}

/* callback from the Close button.
 */
/* ARGSUSED */
static void
srch_close_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	XtUnmanageChild (srchform_w);
}

/* user typed OK to the accuracy prompt. get his new value and use it */
/* ARGSUSED */
static void
srch_acc_ok_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	char *text;
	
	get_xmstring(w, XmNtextString, &text);
	f_scansex (tmlimit, text, &tmlimit);
	XtFree (text);
	f_time (acc_w, tmlimit);
	XtUnmanageChild (w);
}

/* callback from the accuracy-change-request pushbutton.
 * put up a prompt dialog to ask for a new value.
 */
/* ARGSUSED */
static void
srch_acc_cb (wid, client, call)
Widget wid;
XtPointer client;
XtPointer call;
{
	static Widget dw;
	Widget w;
	
	if (!dw) {
	    XmString str, title;
	    Arg args[20];
	    int n;

	    str = XmStringCreate ("search accuracy (h:m:s):",
						    XmSTRING_DEFAULT_CHARSET);
	    title = XmStringCreate ("xephem Search Accuracy",
						    XmSTRING_DEFAULT_CHARSET);
	    n = 0;
	    XtSetArg(args[n], XmNdefaultPosition, False);  n++;
	    XtSetArg(args[n], XmNselectionLabelString, str);  n++;
	    XtSetArg(args[n], XmNdialogTitle, title);  n++;
	    dw = XmCreatePromptDialog(toplevel_w, "Accuracy", args, n);
	    XtAddCallback (dw, XmNokCallback, srch_acc_ok_cb, NULL);
	    XtAddCallback (dw, XmNmapCallback, prompt_map_cb, NULL);
	    XmStringFree (str);
	    XmStringFree (title);

	    w = XmSelectionBoxGetChild (dw, XmDIALOG_HELP_BUTTON);
	    XtUnmanageChild (w);
	}

	set_xmstring(dw, XmNtextString, "");

	XtManageChild (dw);

#if XmVersion >= 1001
	w = XmSelectionBoxGetChild (dw, XmDIALOG_TEXT);
	XmProcessTraversal (w, XmTRAVERSE_CURRENT);
	XmProcessTraversal (w, XmTRAVERSE_CURRENT); /* yes, twice!! */
#endif
}

/* callback from the search goal selection radio buttons.
 * same callback used for all of them.
 * client is pointer to desired search function.
 */
/* ARGSUSED */
static void
srch_goal_cb(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	int (*sfp)()= (int (*)())client;

	if (XmToggleButtonGetState(w)) {
	    /* better turn off searching if changing the search function! */
	    if (srch_f != sfp && srching_now())
		XmToggleButtonSetState(on_w, False, True /* invoke cb */);
	    srch_f = sfp;
	}
}

/* callback from the on/off toggle button activate.
 */
/* ARGSUSED */
static void
srch_on_off_cb(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	static char *msg =
	    "You must first successfully compile a search function and\n select a goal algorithm before searching may be enabled.";

	if (XmToggleButtonGetState(w)) {
	    /* insure there is a valid function and goal algorithm selected
	     * if turning searching on.
	     */
	    if (!prog_isgood()) {
		XmToggleButtonSetState(on_w, False, True/* invoke cb */);
		xe_msg (msg, 0);
		 
	    } else if (srch_f == 0) {
		XmToggleButtonSetState(on_w, False, True/* invoke cb */);
		xe_msg (msg, 0);
	    } else {
		srch_tmscalled = 0;
		/* turning on searhing so as a courtesy turn off the
		 * field selection mechanism if it's on now.
		 */
		if (XmToggleButtonGetState(field_w))
		    XmToggleButtonSetState(field_w, False, True);
	    }
	}
}

/* use successive paraboloidal fits to find when expression is at a
 * local minimum or maximum.
 */
static int
srch_minmax(Mjd, v, tmincp)
double Mjd;
double v;
double *tmincp;
{
	static double base;		/* for better stability */
	static double x_1, x_2, x_3;	/* keep in increasing order */
	static double y_1, y_2, y_3;
	double xm, a, b;

	if (srch_tmscalled == 0) {
	    base = Mjd;
	    x_1 = 0.0;
	    y_1 = v;
	    return (0);
	}
	Mjd -= base;
	if (srch_tmscalled == 1) {
	    /* put in one of first two slots */
	    if (Mjd < x_1) {
	        x_2 = x_1;  y_2 = y_1;
		x_1 = Mjd; y_1 = v;
	    } else {
		x_2 = Mjd; y_2 = v;
	    }
	    return (0);
	}
	if (srch_tmscalled == 2 || fabs(Mjd - x_1) < fabs(Mjd - x_3)) {
	    /* closer to x_1 so discard x_3.
	     * or if it's our third value we know to "discard" x_3.
	     */
	    if (Mjd > x_2) {
		x_3 = Mjd; y_3 = v;
	    } else {
		x_3 = x_2;  y_3 = y_2;
		if (Mjd > x_1) {
		    x_2 = Mjd; y_2 = v;
		} else {
		    x_2 = x_1;  y_2 = y_1;
		    x_1 = Mjd; y_1 = v;
		}
	    }
	    if (srch_tmscalled == 2)
		return (0);
	} else {
	    /* closer to x_3 so discard x_1 */
	    if (Mjd < x_2) {
		x_1 = Mjd;  y_1 = v;
	    } else {
		x_1 =  x_2;  y_1 = y_2;
		if (Mjd < x_3) {
		    x_2 = Mjd; y_2 = v;
		} else {
		    x_2 =  x_3; y_2 = y_3;
		    x_3 = Mjd; y_3 = v;
		}
	    }
	}

#ifdef TRACEMM
	{ char buf[NC];
	  (void) sprintf (buf, "x_1=%g y_1=%g x_2=%g y_2=%g x_3=%g y_3=%g",
						x_1, y_1, x_2, y_2, x_3, y_3);
	  xe_msg (buf,0);
	}
#endif
	a = y_1*(x_2-x_3) - y_2*(x_1-x_3) + y_3*(x_1-x_2);
	if (fabs(a) < 1e-10) {
	    /* near-0 zero denominator, ie, curve is pretty flat here,
	     * so assume we are done enough.
	     * signal this by forcing a 0 tminc.
	     */
	    *tmincp = 0.0;
	    return (-1);
	}
	b = (x_1*x_1)*(y_2-y_3) - (x_2*x_2)*(y_1-y_3) + (x_3*x_3)*(y_1-y_2);
	xm = -b/(2.0*a);
	*tmincp = (xm - Mjd)*24.0;
	return (fabs (*tmincp) < tmlimit ? -1 : 0);
}

/* use secant method to solve for time when expression passes through 0.
 */
static int
srch_solve0(Mjd, v, tmincp)
double Mjd;
double v;
double *tmincp;
{
	static double x0, x_1;	/* x(n-1) and x(n) */
	static double y_0, y_1;	/* y(n-1) and y(n) */
	double x_2;		/* x(n+1) */
	double df;		/* y(n) - y(n-1) */

	switch (srch_tmscalled) {
	case 0: x0 = Mjd; y_0 = v; return(0);
	case 1: x_1 = Mjd; y_1 = v; break;
	default: x0 = x_1; y_0 = y_1; x_1 = Mjd; y_1 = v; break;
	}

	df = y_1 - y_0;
	if (fabs(df) < 1e-10) {
	    /* near-0 zero denominator, ie, curve is pretty flat here,
	     * so assume we are done enough.
	     * signal this by forcing a 0 tminc.
	     */
	    *tmincp = 0.0;
	    return (-1);
	}
	x_2 = x_1 - y_1*(x_1-x0)/df;
	*tmincp = (x_2 - Mjd)*24.0;
	return (fabs (*tmincp) < tmlimit ? -1 : 0);
}

/* binary search for time when expression changes from its initial state.
 * if the change is outside the initial tminc range, then keep searching in that
 *    direction by tminc first before starting to divide down.
 */
static int
srch_binary(Mjd, v, tmincp)
double Mjd;
double v;
double *tmincp;
{
	static double lb, ub;		/* lower and upper bound */
	static int initial_state;
	int this_state = v >= 0.5;

#define	FLUNDEF	-9e10

	if (srch_tmscalled == 0) {
	    if (*tmincp >= 0.0) {
		/* going forwards in time so first Mjd is lb and no ub yet */
		lb = Mjd;
		ub = FLUNDEF;
	    } else {
		/* going backwards in time so first Mjd is ub and no lb yet */
		ub = Mjd;
		lb = FLUNDEF;
	    }
	    initial_state = this_state;
	    return (0);
	}

	if (ub != FLUNDEF && lb != FLUNDEF) {
	    if (this_state == initial_state)
		lb = Mjd;
	    else
		ub = Mjd;
	    *tmincp = ((lb + ub)/2.0 - Mjd)*24.0;
#ifdef TRACEBIN
	    { char buf[NC];
	      (void) sprintf (buf, "lb=%g ub=%g tminc=%g Mjd=%g is=%d ts=%d",
			    lb, ub, *tmincp, Mjd, initial_state, this_state);
	      xe_msg(buf, 0);
	    }
#endif
	    /* signal to stop if asking for time change less than TMLIMIT */
	    return (fabs (*tmincp) < tmlimit ? -1 : 0);
	} else if (this_state != initial_state) {
	    /* gone past; turn around half way */
	    if (*tmincp >= 0.0)
		ub = Mjd;
	    else
		lb = Mjd;
	    *tmincp /= -2.0;
	    return (0);
	} else {
	    /* just keep going, looking for first state change but we keep
	     * learning the lower (or upper, if going backwards) bound.
	     */
	    if (*tmincp >= 0.0)
		lb = Mjd;
	    else
		ub = Mjd;
	    return (0);
	}
}
