/* code to manage the stuff on the moon display.
 */

#include <stdio.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/DrawingA.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/CascadeB.h>
#include <Xm/ToggleB.h>
#include <Xm/RowColumn.h>
#include <Xm/Separator.h>

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


extern Widget toplevel_w;

extern Now *mm_get_now P_((void));
extern Obj *db_basic P_((int id));
extern int any_ison P_((void));
extern int get_color_resource P_((Widget w, char *cname, Pixel *p));
extern void db_update P_((Obj *op));
extern void f_double P_((Widget w, char *fmt, double f));
extern void f_pangle P_((Widget w, double a));
extern void get_something P_((Widget w, char *resource, XtArgVal value));
extern void hlp_dialog P_((char *tag, char *deflt[], int ndeflt));
extern void llibration P_((double JD, double *llatp, double *llonp));
extern void register_selection P_((char *name));
extern void set_something P_((Widget w, char *resource, XtArgVal value));
extern void set_xmstring P_((Widget w, char *resource, char *txt));
extern void timestamp P_((Now *np, Widget w));

static void m_create_form P_((void));
static void m_create_msform P_((void));
static void m_set_buttons P_((int whether));
static void m_set_a_button P_((Widget pbw, int whether));
static void m_mstats_cb P_((Widget w, XtPointer client, XtPointer call));
static void m_activate_cb P_((Widget w, XtPointer client, XtPointer call));
static void m_close_cb P_((Widget w, XtPointer client, XtPointer call));
static void m_mapvw_cb P_((Widget w, XtPointer client, XtPointer call));
static void m_help_cb P_((Widget w, XtPointer client, XtPointer call));
static void m_eshine_cb P_((Widget w, XtPointer client, XtPointer call));
static void m_da_exp_cb P_((Widget w, XtPointer client, XtPointer call));
static void m_all P_((Now *np, int force));
static void m_draw P_((int force, double limb));
static void m_draw_mapview P_((double el, double limb));
static void m_draw_grview P_((double el, double limb));
static void m_mark_libr P_((Display *dsp, Window win, GC gc, double limb,
    int rad, int tb, int lb));

/* get the small moonbit map and its dimension defines.
 * moon is roughly centered within the map, with a radius of some 200 pixels.
 * exact values set by trial and error.
 */
#include "smallfm.xbm"
#define	MRAD	191				/* radius of moon image */
#define	TOPMAR	(smallfm_height/2 - MRAD + 2)	/* top margin */
#define	LEFTMAR	(smallfm_width/2 - MRAD)	/* left margin */

#define	NSTARS	300		/* n fake background stars in graphical view */
#define	LIMBMARKFRAC	20	/* libr marker is 1/x this of total size */

static Widget mform_w;		/* main moon form dialog */
static Widget msform_w;		/* statistics form dialog */
static Widget mda_w;		/* moon drawring area */
static Widget eshine_w;		/* whether we want to show earthshine */
static Widget mapvw_w;		/* toggle button that selects which view */
static Widget dt_w;		/* main date/time stamp widget */
static Widget sdt_w;		/* statistics date/time stamp widget */
static Widget llat_w;		/* lib in lat pushbutton widget */
static Widget llong_w;		/* lib in long pushbutton widget */
static Widget limb_w;		/* limb angle pushbutton widget */
static Widget lib_w;		/* total lib pushbutton widget */
static int mswasman;            /* whether statistics form was managed */

#define	MAPVW_W	(smallfm_width+20)
#define	MAPVW_H	(smallfm_height+40)

static int m_selecting;        /* set while our fields are being selected */

/* called when the moon view 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
m_manage ()
{
	if (!mform_w) {
	    m_create_form();
	    m_create_msform();
	}
	
	if (XtIsManaged(mform_w)) {
	    XtUnmanageChild (mform_w);
	    if ((mswasman = XtIsManaged(msform_w)))
		XtUnmanageChild (msform_w);

	} else {
	    XtManageChild (mform_w);
	    if (mswasman) {
		XtManageChild (msform_w);
		m_set_buttons(m_selecting);
	    }
	    /* let the initial expose do the drawing */
	}
}

int
m_ison()
{
	return (mform_w && XtIsManaged(mform_w));
}

void
m_update (np, how_much)
Now *np;
int how_much;
{
	if (!mform_w)
	    return;
	if (!XtIsManaged(mform_w) && !any_ison() && !how_much)
	    return;

	m_all (np, how_much);
}

/* 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.
 */
void
m_selection_mode (whether)
int whether;	/* whether setting up for plotting or for not plotting */
{
	m_selecting += whether ? 1 : -1;

	if (m_ison()) {
	    if ((whether && m_selecting == 1)     /* first one to want on */
		|| (!whether && m_selecting == 0) /* last one to want off */)
		m_set_buttons (whether);
	}
}

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

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

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

static void
m_create_form()
{
	Widget frame_w;
	Widget mb_w, pd_w, cb_w;
	Widget w;
	XmString str;
	Arg args[20];
	int n;

	/* create master form */
	n = 0;
	XtSetArg (args[n], XmNautoUnmanage, False); n++;
	XtSetArg (args[n], XmNhorizontalSpacing, 5); n++;
	XtSetArg (args[n], XmNverticalSpacing, 5); n++;
	XtSetArg (args[n], XmNdefaultPosition, False); n++;
	mform_w = XmCreateFormDialog (toplevel_w, "Moon", args, n);
	XtAddCallback (mform_w, XmNhelpCallback, m_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 Moon view"); n++;
	XtSetValues (XtParent(mform_w), args, n);

	/* create the menu bar across the top */

	n = 0;
	XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	mb_w = XmCreateMenuBar (mform_w, "MB", args, n);
	XtManageChild (mb_w);

	/* make the Control pulldown */

	n = 0;
	pd_w = XmCreatePulldownMenu (mb_w, "ControlPD", args, n);

	    n = 0;
	    XtSetArg (args[n], XmNsubMenuId, pd_w);  n++;
	    XtSetArg (args[n], XmNmnemonic, 'C'); n++;
	    cb_w = XmCreateCascadeButton (mb_w, "ControlCB", args, n);
	    set_xmstring (cb_w, XmNlabelString, "Control");
	    XtManageChild (cb_w);

	    /* add the close button */

	    n = 0;
	    w = XmCreatePushButton (pd_w, "Close", args, n);
	    XtAddCallback (w, XmNactivateCallback, m_close_cb, 0);
	    XtManageChild (w);

	/* make the View pulldown */

	n = 0;
	pd_w = XmCreatePulldownMenu (mb_w, "ViewPD", args, n);

	    n = 0;
	    XtSetArg (args[n], XmNsubMenuId, pd_w);  n++;
	    XtSetArg (args[n], XmNmnemonic, 'V'); n++;
	    cb_w = XmCreateCascadeButton (mb_w, "ViewCB", args, n);
	    set_xmstring (cb_w, XmNlabelString, "View");
	    XtManageChild (cb_w);

	    /* make the earthshine toggle button */

	    n = 0;
	    XtSetArg (args[n], XmNvisibleWhenOff, True); n++;
	    XtSetArg (args[n], XmNmarginHeight, 0); n++;
	    XtSetArg (args[n], XmNindicatorType, XmONE_OF_MANY); n++;
	    eshine_w = XmCreateToggleButton (pd_w, "Earthshine", args, n);
	    XtAddCallback (eshine_w, XmNvalueChangedCallback,m_eshine_cb,0);
	    XtManageChild (eshine_w);

	    /* make the view option toggle button */

	    str = XmStringCreate("Bitmap View", XmSTRING_DEFAULT_CHARSET);
	    n = 0;
	    XtSetArg (args[n], XmNvisibleWhenOff, True); n++;
	    XtSetArg (args[n], XmNmarginHeight, 0); n++;
	    XtSetArg (args[n], XmNindicatorType, XmONE_OF_MANY); n++;
	    XtSetArg (args[n], XmNlabelString, str); n++;
	    mapvw_w = XmCreateToggleButton(pd_w, "BitmapView", args, n);
	    XtAddCallback(mapvw_w, XmNvalueChangedCallback, m_mapvw_cb, 0);
	    XtManageChild (mapvw_w);
	    XmStringFree (str);

	    /* add a separator */

	    n = 0;
	    w = XmCreateSeparator (pd_w, "Sep", args, n);
	    XtManageChild (w);

	    /* add the More Info control */

	    n = 0;
	    w = XmCreatePushButton (pd_w, "Stats", args, n);
	    set_xmstring (w, XmNlabelString, "Libration info...");
	    XtAddCallback (w, XmNactivateCallback, m_mstats_cb, NULL);
	    XtManageChild (w);

	/* make the help pulldown */

	n = 0;
	pd_w = XmCreatePulldownMenu (mb_w, "HelpPD", args, n);

	    n = 0;
	    XtSetArg (args[n], XmNsubMenuId, pd_w);  n++;
	    XtSetArg (args[n], XmNmnemonic, 'H'); n++;
	    cb_w = XmCreateCascadeButton (mb_w, "HelpCB", args, n);
	    set_xmstring (cb_w, XmNlabelString, "Help");
	    XtManageChild (cb_w);
	    set_something (mb_w, XmNmenuHelpWidget, (XtArgVal)cb_w);

	    n = 0;
	    w = XmCreatePushButton (pd_w, "Help", args, n);
	    XtManageChild (w);
	    XtAddCallback (w, XmNactivateCallback, m_help_cb, NULL);

	/* make a label for the date stamp */

	n = 0;
	XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrecomputeSize, False); n++;
	dt_w = XmCreateLabel (mform_w, "DateStamp", args, n);
	timestamp (mm_get_now(), dt_w);	/* sets initial size */
	XtManageChild (dt_w);

	/* make a frame for the drawing area */

	n = 0;
	XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg (args[n], XmNtopWidget, mb_w); n++;
	XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
	XtSetArg (args[n], XmNbottomWidget, dt_w); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNshadowType, XmSHADOW_ETCHED_OUT); n++;
	frame_w = XmCreateFrame (mform_w, "MoonFrame", args, n);
	XtManageChild (frame_w);

	    /* make a drawing area in the frame for the sky */
	    n = 0;
	    XtSetArg (args[n], XmNwidth, MAPVW_W); n++;
	    XtSetArg (args[n], XmNheight, MAPVW_H); n++;
	    XtSetArg (args[n], XmNmarginWidth, 0); n++;
	    XtSetArg (args[n], XmNmarginHeight, 0); n++;
	    mda_w = XmCreateDrawingArea (frame_w, "MoonDA", args, n);
	    XtManageChild (mda_w);
	    XtAddCallback (mda_w, XmNexposeCallback, m_da_exp_cb, NULL);
}

static void
m_create_msform()
{
	Widget rc_w;
	Widget sep_w;
	Widget w;
	XmString str;
	Arg args[20];
	int n;

	/* create form */
	n = 0;
	XtSetArg (args[n], XmNautoUnmanage, False); n++;
	XtSetArg (args[n], XmNverticalSpacing, 5); n++;
	msform_w = XmCreateFormDialog (toplevel_w, "MoonStats", args, n);

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

	/* make a rowcolumn to hold the labels and info buttons */

	n = 0;
	XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNpacking, XmPACK_COLUMN); n++;
	XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++;
	XtSetArg (args[n], XmNnumColumns, 4); n++;
	XtSetArg (args[n], XmNisAligned, False); n++;
	rc_w = XmCreateRowColumn (msform_w, "SRC", args, n);
	XtManageChild (rc_w);

	    /* make the libration in lat/long rows */

	    str = XmStringCreate ("Libr in Lat:", XmSTRING_DEFAULT_CHARSET);
	    n = 0;
	    XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
	    XtSetArg (args[n], XmNlabelString, str); n++;
	    w = XmCreateLabel (rc_w, "MLLatL", args, n);
	    XtManageChild (w);
	    XmStringFree (str);

	    n = 0;
	    XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
	    XtSetArg (args[n], XmNuserData, "Moon.LibrLat"); n++;
	    llat_w = XmCreatePushButton (rc_w, "MLLatPB", args, n);
	    XtAddCallback (llat_w, XmNactivateCallback, m_activate_cb, NULL);
	    XtManageChild (llat_w);

	    str = XmStringCreate ("Libr in Long:", XmSTRING_DEFAULT_CHARSET);
	    n = 0;
	    XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
	    XtSetArg (args[n], XmNlabelString, str); n++;
	    w = XmCreateLabel (rc_w, "MLLongL", args, n);
	    XtManageChild (w);
	    XmStringFree (str);

	    n = 0;
	    XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
	    XtSetArg (args[n], XmNuserData, "Moon.LibrLong"); n++;
	    llong_w = XmCreatePushButton (rc_w, "MLLongPB", args, n);
	    XtAddCallback (llong_w, XmNactivateCallback, m_activate_cb, NULL);
	    XtManageChild (llong_w);

	    /* make the limb/tilt rows */

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

	    n = 0;
	    XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
	    XtSetArg (args[n], XmNuserData, "Moon.LibrLimb"); n++;
	    limb_w = XmCreatePushButton (rc_w, "MLimbPB", args, n);
	    XtAddCallback (limb_w, XmNactivateCallback, m_activate_cb, NULL);
	    XtManageChild (limb_w);

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

	    n = 0;
	    XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
	    XtSetArg (args[n], XmNuserData, "Moon.LibrTilt"); n++;
	    lib_w = XmCreatePushButton (rc_w, "MLibPB", args, n);
	    XtAddCallback (lib_w, XmNactivateCallback, m_activate_cb, NULL);
	    XtManageChild (lib_w);

	/* add a separator */

	n = 0;
	XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg (args[n], XmNtopWidget, rc_w); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	sep_w = XmCreateSeparator (msform_w, "Sep2", args, n);
	XtManageChild (sep_w);

	/* add a label for the current date/time stamp */

	n = 0;
	XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg (args[n], XmNtopWidget, sep_w); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNalignment, XmALIGNMENT_CENTER); n++;
	sdt_w = XmCreateLabel (msform_w, "SDTstamp", args, n);
	XtManageChild (sdt_w);

	/* add a separator */

	n = 0;
	XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg (args[n], XmNtopWidget, sdt_w); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
	sep_w = XmCreateSeparator (msform_w, "Sep3", args, n);
	XtManageChild (sep_w);

	/* add a close button */

	n = 0;
	XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg (args[n], XmNtopWidget, sep_w); n++;
	XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
	XtSetArg (args[n], XmNleftPosition, 20); n++;
	XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
	XtSetArg (args[n], XmNrightPosition, 80); n++;
	w = XmCreatePushButton (msform_w, "Close", args, n);
	XtAddCallback (w, XmNactivateCallback, m_mstats_cb, NULL);
	XtManageChild (w);
}

/* go through all the buttons pickable for plotting and set whether they
 * should appear to look like buttons or just flat labels.
 */
static void
m_set_buttons (whether)
int whether;	/* whether setting up for plotting or for not plotting */
{
	m_set_a_button (llat_w, whether);
	m_set_a_button (llong_w, whether);
	m_set_a_button (limb_w, whether);
	m_set_a_button (lib_w, whether);
}

/* set whether the given button looks like a label.
 */
static void
m_set_a_button(pbw, whether)
Widget pbw;
int whether;
{
	static Arg look_like_button[] = {
	    {XmNtopShadowColor, (XtArgVal) 0},
	    {XmNbottomShadowColor, (XtArgVal) 0},
            {XmNtopShadowPixmap, (XtArgVal) 0},
            {XmNbottomShadowPixmap, (XtArgVal) 0},
	    {XmNfillOnArm, (XtArgVal) True},
	    {XmNtraversalOn, (XtArgVal) True},
	};
	static Arg look_like_label[] = {
	    {XmNtopShadowColor, (XtArgVal) 0},
	    {XmNbottomShadowColor, (XtArgVal) 0},
            {XmNtopShadowPixmap, (XtArgVal) 0},
            {XmNbottomShadowPixmap, (XtArgVal) 0},
	    {XmNfillOnArm, (XtArgVal) False},
	    {XmNtraversalOn, (XtArgVal) False},
	};
	static int called;
	Arg *ap;
	int na;

	if (!called) {
	    /* get baseline label and shadow appearances.
	     */
            Pixel topshadcol, botshadcol, bgcol;
            Pixmap topshadpm, botshadpm;
	    Arg args[20];
	    Widget tmpw;
	    int n;

	    n = 0;
	    tmpw = XmCreatePushButton (mform_w, "tmp", args, n);

	    n = 0;
            XtSetArg (args[n], XmNtopShadowColor, &topshadcol); n++;
            XtSetArg (args[n], XmNbottomShadowColor, &botshadcol); n++;
            XtSetArg (args[n], XmNtopShadowPixmap, &topshadpm); n++;
            XtSetArg (args[n], XmNbottomShadowPixmap, &botshadpm); n++;
	    XtSetArg (args[n], XmNbackground, &bgcol); n++;
	    XtGetValues (tmpw, args, n);

            look_like_button[0].value = topshadcol;
            look_like_button[1].value = botshadcol;
            look_like_button[2].value = topshadpm;
            look_like_button[3].value = botshadpm;
            look_like_label[0].value = bgcol;
            look_like_label[1].value = bgcol;
            look_like_label[2].value = XmUNSPECIFIED_PIXMAP;
            look_like_label[3].value = XmUNSPECIFIED_PIXMAP;

	    XtDestroyWidget (tmpw);
	     
	    called = 1;
	}

	if (whether) {
	    ap = look_like_button;
	    na = XtNumber(look_like_button);
	} else {
	    ap = look_like_label;
	    na = XtNumber(look_like_label);
	}

	XtSetValues (pbw, ap, na);
}

/* callback from the Close button is activated on the stats menu or when
 * the More Info button is activated. they each do the same thing.
 */
/* ARGSUSED */
static void
m_mstats_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	if ((mswasman = XtIsManaged(msform_w)))
	    XtUnmanageChild (msform_w);
	else {
	    XtManageChild (msform_w);
	    m_set_buttons(m_selecting);
	}
}

/* callback from any of the data menu buttons being activated.
 */
/* ARGSUSED */
static void
m_activate_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	if (m_selecting) {
	    char *name;
	    get_something (w, XmNuserData, (XtArgVal)&name);
	    register_selection (name);
	}
}

/* callback from the Close button.
 */
/* ARGSUSED */
static void
m_close_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	XtUnmanageChild (mform_w);
	if ((mswasman = XtIsManaged(msform_w)))
	    XtUnmanageChild (msform_w);
}

/* callback from the Map view toggle button.
 */
/* ARGSUSED */
static void
m_mapvw_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	m_update (mm_get_now(), 1);
}

/* callback from the Help button
 */
/* ARGSUSED */
static void
m_help_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
static char *msg[] = {
"This is a display of the current phase of the Moon.",
"Included is libration information."
};

     hlp_dialog ("Moon", msg, XtNumber(msg));
     }


/* callback from the earthshine toggle button.
 */
/* ARGSUSED */
static void
m_eshine_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	m_update (mm_get_now(), 1);
}

/* expose (or reconfig) of moon drawing area.
 * just redraw the scene to the current window size.
 */
/* ARGSUSED */
static void
m_da_exp_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
	XmDrawingAreaCallbackStruct *c = (XmDrawingAreaCallbackStruct *)call;

	switch (c->reason) {
	case XmCR_EXPOSE: {
	    /* turn off gravity so we get expose events for either shrink or
	     * expand.
	     */
	    static before;
	    XExposeEvent *e = &c->event->xexpose;

	    if (!before) {
		XSetWindowAttributes swa;
		swa.bit_gravity = ForgetGravity;
		XChangeWindowAttributes (e->display, e->window,
							    CWBitGravity, &swa);
		before = 1;
	    }
	    /* wait for the last in the series */
	    if (e->count != 0)
		return;
	    break;
	    }
	default:
	    printf ("Unexpected mda_w event. type=%d\n", c->reason);
	    exit(1);
	}

	m_all (mm_get_now(), 1);
}

/* draw everything from scratch.
 * don't bother with the map if we are not managed.
 */
static void
m_all (np, force)
Now *np;
int force;
{
	double llat, llong;
	double limb, tilt;

	llibration (mjd+MJD0, &llat, &llong);
	limb = atan2 (-llong, llat);
	if (limb < 0.0)
	    limb += 2*PI;	/* limb angle is traditionally 0..360 */
	tilt = raddeg(sqrt(llat*llat + llong*llong));


	f_pangle (llat_w, llat);
	f_pangle (llong_w, llong);
	f_pangle (limb_w, limb);
	f_double (lib_w, "%6.3f", tilt);

	timestamp (np, dt_w);
	timestamp (np, sdt_w);

	if (XtIsManaged(mda_w))
	    m_draw (force, limb);
}

/* draw moon, always if force or scene has changed.
 */
static void
m_draw (force, limb)
int force;
double limb;
{
	static double last_el = -12321.0;	/* anything mighty unlikely */
	Obj *op;
	double el;

	/* get new elongation */
	op = db_basic (MOON);
	db_update (op);
	el = degrad(op->s_elong);

	/* don't bother if not forcing and hasn't changed */
	if (!force && el == last_el)
	    return;
	last_el = el;

	if (XmToggleButtonGetState(mapvw_w)) {
	    /* drawing pixmap view so insure a good initial size at least
	     */
	    Arg args[20];
	    int n;

	    n = 0;
	    XtSetArg (args[n], XmNwidth, MAPVW_W); n++;
	    XtSetArg (args[n], XmNheight, MAPVW_H); n++;
	    XtSetValues (mda_w, args, n);
	    XtSetSensitive (eshine_w, True);
	    m_draw_mapview (el, limb);
	} else {
	    /* want graphical view */
	    XtSetSensitive (eshine_w, False);
	    m_draw_grview (el, limb);
	}
}

/* version that draws moon using genuine bitmap image. */
static void
m_draw_mapview (el, limb)
double el;
double limb;
{
	static GC m_fgc, m_lgc;
	Display *dsp = XtDisplay (mda_w);
	Window win = XtWindow (mda_w);
	double cosel;
	XImage *xim;
	unsigned char *m;
	int y;	/* y coord: 0 is center, up is + */
	int earthshine = XmToggleButtonGetState(eshine_w);

	if (!m_fgc) {
	    /* make gc from MoonDA colors
	     */
	    XGCValues gcv;
	    unsigned int gcm;
	    unsigned long fg, bg;
	    Pixel p;

	    gcm = GCForeground | GCBackground;
	    get_something (mda_w, XmNforeground, (XtArgVal)&fg);
	    get_something (mda_w, XmNbackground, (XtArgVal)&bg);
	    gcv.background = fg;
	    gcv.foreground = bg;
	    m_fgc = XCreateGC (dsp, win, gcm, &gcv);

	    /* make the libration marker gc
	     * beware of monochrome screens that allocate ok for any ol' color.
	     */
	    if (get_color_resource (mda_w, "MoonLibrColor", &p) < 0
							|| p == bg || p == fg) {
		gcm = GCForeground | GCFunction;
		gcv.foreground = fg ^ bg;
		gcv.function = GXxor;
		m_lgc = XCreateGC (dsp, win, gcm, &gcv);
	    } else {
		gcm = GCForeground;
		gcv.foreground = p;
		m_lgc = XCreateGC (dsp, win, gcm, &gcv);
	    }
	}

	/* make an image copy so we can darken some of it */
	m = (unsigned char *)XtMalloc(smallfm_width*smallfm_height/8);
	(void) memcpy ((void *)m, (void *)smallfm_bits,
						smallfm_width*smallfm_height/8);

	cosel = cos(el);
	for (y = MRAD; y > -MRAD; y--) {
	    int lx, rx;	/* left and right edge of scan line to darken */
	    int r, c;	/* X row/col coords */
	    lx = -sqrt((double)(MRAD*MRAD - y*y));
	    rx = -lx * cosel;
	    if (el < 0) {
		int tmp = rx;
		rx = -lx;
		lx = -tmp;
	    }
	    r = TOPMAR + MRAD - y;
	    for (c = LEFTMAR + MRAD + lx; c < LEFTMAR + MRAD + rx; c++)
		if (!earthshine || (c & 3) != 3 || (y & 3) != 3)
		    m[r*smallfm_width/8 + c/8] |= (1 << (c%8)); /* 1 is bkgnd */
	}

	xim = XCreateImage (dsp, XDefaultVisual (dsp, 0),
	    /* depth */		1,
	    /* format */	XYBitmap, 
	    /* offset */	0,
	    /* data */		(char *)m,
	    /* width */		smallfm_width, 
	    /* height */	smallfm_height,
	    /* pad */		8,
	    /* bpl */		0);
	xim->bitmap_bit_order = LSBFirst;
	xim->byte_order = LSBFirst;

	XPutImage (dsp, win, m_fgc, xim, 0, 0, 0, 0,
					    smallfm_width, smallfm_height);
	XDestroyImage (xim);	/* also frees m */

	m_mark_libr (dsp, win, m_lgc, limb, MRAD, TOPMAR, LEFTMAR);
}

/* version that draws moon using just simple graphics */
static void
m_draw_grview (el, limb)
double el;
double limb;
{
	static GC m_fgc, m_bgc, m_lgc;
	static XPoint *stars;
	static int last_w, last_h;
	Display *dsp = XtDisplay (mda_w);
	Window win = XtWindow (mda_w);
	Window root;
	int x, y;
	unsigned int bw, d;
	unsigned w, h;		/* actual size of drawing area window */
	unsigned md;		/* moon circle diameter */
	int la1, la2, ta1, ta2; /* limb and terminator start and extent */
	int wid;		/* distance from meridian to terminator */
	int xb, yb;		/* x and y borders, eg, (w-nx)/2 */
	Pixmap pm;

	if (!m_fgc) {
	    /* make gcs from MoonDA colors
	     */
	    XGCValues gcv;
	    unsigned int gcm;
	    unsigned long fg, bg;
	    Pixel p;

	    get_something (mda_w, XmNforeground, (XtArgVal)&fg);
	    get_something (mda_w, XmNbackground, (XtArgVal)&bg);

	    gcm = GCForeground | GCBackground;
	    gcv.foreground = fg;
	    gcv.background = bg;
	    m_fgc = XCreateGC (dsp, win, gcm, &gcv);

	    gcv.foreground = bg;
	    gcv.background = fg;
	    m_bgc = XCreateGC (dsp, win, gcm, &gcv);

	    /* make the libration marker gc
	     * beware of monochrome screens that allocate ok for any ol' color.
	     */
	    if (get_color_resource (mda_w, "MoonLibrColor", &p) < 0
							|| p == bg || p == fg) {
		gcm = GCForeground | GCFunction;
		gcv.foreground = fg ^ bg;
		gcv.function = GXxor;
		m_lgc = XCreateGC (dsp, win, gcm, &gcv);
	    } else {
		gcm = GCForeground;
		gcv.foreground = p;
		m_lgc = XCreateGC (dsp, win, gcm, &gcv);
	    }
	}


	/* get size of window now and make a fresh pixmap to match.
	 * we draw the scene in the pixmap then copy it to the window.
	 * otherwise, you can see it drawing and it flashes and looks cheap.
	 */
	XGetGeometry (dsp, win, &root, &x, &y, &w, &h, &bw, &d);
	pm = XCreatePixmap (dsp, win, w, h, d);
	XFillRectangle (dsp, pm, m_bgc, 0, 0, w, h);

	/* set moon diameter so it is centered within the bounding rectangle
	 * of the window.
	 * this code sets it to match the borders of the map view and makes
	 * the diameter even so it divides evenly.
	 */
	md = ((w > h) ? MRAD*h/(MRAD+TOPMAR) : MRAD*w/(MRAD+LEFTMAR)) & (~1);
	xb = (w - md)/2;
	yb = (h - md)/2;

	la1 = el >= 0.0 ? -90*64 : 90*64;
	la2 = 180*64;
	wid = fabs(md/2*cos(el))+0.5;
	ta1 = el >= PI/2 || (el <= 0 && el >= -PI/2) ? 90*64 : -90*64;
	ta2 = 180*64;

	/* dsp, win, gc, x, y, w, h, start_ang, ang_extent */
	/* draw the lit hemisphere to the limb */
	XFillArc (dsp, pm, m_fgc, xb, yb, md, md, la1, la2);

	/* draw the portion from the terminator to the meridian */
	if ((la1 < 0 && ta1 < 0) || (la1 > 0 && ta1 > 0)) {
	    /* crescent, so draw in background color */
	    XFillArc (dsp, pm, m_bgc, md/2-wid+xb, yb, 2*wid, md, ta1, ta2);
	} else {
	    /* gibbous, so draw in foreground color */
	    XFillArc (dsp, pm, m_fgc, md/2-wid+xb, yb, 2*wid, md, ta1, ta2);
	}

	/* add in the background stars */
	if (!stars || last_w != w || last_h != h) {
	    /* sprinkle NSTARS stars outside the moon's circle of radius r.
	     */
	    double rr = 0.25*md*md; /* = (md/2)*(md/2); */
	    int i;
	    if (stars)
		XtFree ((char *)stars);
	    stars = (XPoint *) XtMalloc (NSTARS * sizeof(XPoint));
	    for (i = 0; i < NSTARS; ) {
		x = ((rand() >> 2) & 0xfff) * (w-1) / 0xfff;
		y = ((rand() >> 2) & 0xfff) * (h-1) / 0xfff;
		/* compare the candidate y with the y on the ellipse at the
		 * candidate x to decide whether to draw the point.
		 */
#define		SQR(x) ((x)*(x))
		if (SQR(y-h/2) > rr - SQR(x-w/2)) {
		    stars[i].x = (short)x;
		    stars[i].y = (short)y;
		    i++;
		}
	    }
	}
	XDrawPoints (dsp, pm, m_fgc, stars, NSTARS, CoordModeOrigin);

	m_mark_libr (dsp, pm, m_lgc, limb, md/2, yb, xb);

	XCopyArea (dsp, pm, win, m_fgc, 0, 0, w, h, 0, 0);
	XFreePixmap (dsp, pm);

	last_w = w;
	last_h = h;
}

/* draw a marker to show the limb angle favored by libration */
static void
m_mark_libr (dsp, win, gc, limb, rad, tb, lb)
Display *dsp;
Window win;
GC gc;
double limb;		/* limb angle, rads ccw from up */
int rad;		/* circle radius */
int tb, lb;		/* circle top and left borders */
{
	int r;		/* radius of marker */
	int x, y;	/* center of marker */

	r = rad/LIMBMARKFRAC;
	x = lb + rad*(1 - sin(limb));
	y = tb + rad*(1 - cos(limb));

	XFillArc (dsp, win, gc, x-r, y-r, 2*r, 2*r, 0, 360*64);
}
