#include <stdio.h>
#include <Xm/Xm.h>
#include <Xm/List.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/BulletinB.h>
#include <Xm/Text.h>
#include <Xm/Separator.h>
#include <Xm/MessageB.h>
#include <Xm/Label.h>
#include        "layout.h"
#include	"db.h"

XmStringCharSet charset = XmSTRING_DEFAULT_CHARSET;

static void clickcallback();
static void quitcallback();
static void savecallback();
static void erasecallback();

void	ErrorMessage();

#define PrompterOK 1
#define PrompterCancel 0

Class		selections[MAX_CHOICES];/* course and section names */
int		currentchoice = 0;	/* where the user's next choice goes */
int		origcurrentchoice = 0;
ClassPtr	*classlist;
int		classcount = 0;
ForminstancePtr myforminstance;
char		*ReadFile();
Boolean		changesmade = False;
Widget		parent;

static Widget	feedbackW[MAX_CHOICES];
static XmStringTable	str_list;	/* long course descriptions */
static XtAppContext	app;
static Widget		toplevel;

extern char		*getenv();
extern int	        PhSendAuthenticator();
extern int		PhOpenServer();
extern int		PhSaveChoices();
extern ForminstancePtr	PhGetForm();
extern int		PhDeleteForm();
extern char		*ReadFile();
char			*whoami = NULL;


main(argc, argv)
int	argc;
char	*argv[];
{

	int	i;

	if (argc == 2) {
		if (argv[1][0] == '-') {
			printf ("This program allows you to select your choices for the physical education lottery.\n", argv[0]);
			printf ("usage:  %s\n", argv[0]);
			exit(0);
		}
		whoami = argv[1];
	}

	SetUpPath();

	toplevel = XtVaAppInitialize(&app, "Enroll", NULL, 0,
					&argc, argv, NULL, NULL);
	parent = XtVaCreateManagedWidget("parent", xmBulletinBoardWidgetClass, 
					toplevel, NULL);
	PutUpIntro(parent);

/*
** Introduce myself to the server
*/
	if (!PhOpenServer()) {
		fprintf(stderr, "Cannot reach database server.\n");
		exit(-1);
	}

	if (!PhSendAuthenticator()) {
		fprintf(stderr, "Kerberos authentication failed.\n");
		exit(-1);
	}

	if (!whoami) {
		whoami = getenv ("USER");
	}
	else {
		(void) PhAssumeIdentity(whoami);
	}

	if (!whoami) {
		fprintf(stderr, "Unable to read your username.\n");
		exit(-1);
	}

/*
** Start by building the form instance data structure
*/

	myforminstance = PhGetForm("pelott", whoami);
	if (!myforminstance) {
		fprintf(stderr, "Cannot read your record from the database\n");
		exit(-1);
	}

/*
** Transfer values from form instance data into this client's data structures.
*/
	ReadOutData(myforminstance);
	BuildUI(toplevel);
	XtRealizeWidget(toplevel);

	for (i = 0; i < MAX_CHOICES; i++) {
		if ((selections[i].course)[0] != NULL) {
			SetSelection(i);
			currentchoice = i + 1;
		}
	}
	origcurrentchoice = currentchoice;

	XtAppMainLoop(app);
}

BuildUI()
{
	MakeText(parent);
	MakeActionButtons(parent);
	MakeScrolledList(parent);
	MakeFeedbackBoxes(parent);
}

static void prompterokcallback(Widget w, int *a, void *foo) {
  *a = PrompterOK;
}

static void promptercancelcallback(Widget w, int *a, void *foo) {
  *a = PrompterCancel;
}

/*
** Display an arbitrary screen of text.  If the first character is '-'
** we return without doing anything.  If it is 'Y' we display the text
** and allow the users to continue; otherwise, we exit after letting
** them read the text.
*/

PutUpIntro(parent)
Widget parent;
{
	char		*introduction;
	Boolean		continueflag;
	XmString	str;
	Widget		dialog;
	Arg		args[5];
	int		answer = -1;

	introduction = ReadFile(INTROFILE);
	if (!introduction)
		return(-1);

	if (introduction[0] == '-') {
		free(introduction);
		return;
	}

	continueflag = ((introduction[0] == 'y' || introduction[0] == 'Y') ? True : False);

	str = XmStringCreateLtoR(introduction+1, charset);
	XtSetArg(args[0], XmNmessageString, str);
	XtSetArg(args[1], XmNcancelLabelString, NULL);

	dialog = XmCreateMessageDialog(parent, "pelott intro", args, 2);
	XtAddCallback (dialog, XmNokCallback, prompterokcallback, &answer);

	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));

	XtManageChild(dialog);
  	while (answer == -1)
		XtAppProcessEvent(app, XtIMAll);

	free(introduction);
	XmStringFree(str);
	XtDestroyWidget(dialog);

	if (!continueflag)
		exit();

}

MakeText(parent)
Widget parent;
{
	Widget		intro, sep;
	XmString	str;
	char		*introduction;

	introduction = ReadFile(WORKINGTEXT);
	if (!introduction)
		exit(-1);


	str = XmStringCreateLtoR(introduction, charset);

	intro = XtVaCreateManagedWidget ("introduction",
			xmLabelWidgetClass, parent,
			XmNlabelString, str,
		 	NULL);

	XmStringFree(str);

	sep = XtVaCreateManagedWidget
	      ("sep1", xmSeparatorWidgetClass, parent,
		XmNorientation, XmHORIZONTAL,
		NULL);

	sep = XtVaCreateManagedWidget
	      ("sep2", xmSeparatorWidgetClass, parent,
		XmNorientation, XmHORIZONTAL,
		NULL);
}

MakeActionButtons(parent)
Widget parent;
{
	Widget		pushme;

	pushme = XtVaCreateManagedWidget("Cancel", xmPushButtonWidgetClass, 
					parent, NULL);
	XtAddCallback(pushme, XmNactivateCallback, quitcallback, NULL);

	pushme = XtVaCreateManagedWidget("Save", xmPushButtonWidgetClass, 
					parent, NULL);
	XtAddCallback(pushme, XmNactivateCallback, savecallback, NULL);

	pushme = XtVaCreateManagedWidget("Erase", xmPushButtonWidgetClass, 
					parent, NULL);
	XtAddCallback(pushme, XmNactivateCallback, erasecallback, NULL);
}

/*
** Make the scrolled list of classes offered
*/

Widget		list;

MakeScrolledList(parent)
Widget	parent;

{
	Arg		args[5];
	int		n;
	Widget		bboard;
	FILE		*infile;
	char		buffer[MAX_LINE + 1];	/* + 1 for NULL byte */
	int		charsread;
	int		newchar;
	char		*rovingpointer;
	char		shortname[COURSELEN + 1];
	int		sectionnum;
	char		*longname;
/*
** Open the data file and read in the list of courses.
**
** Format:  shortname  sectionnum  one-line-description
**          8 chars    3 chars     remainder of line
**
** The long descriptions go into the Motif scrolling list, and the short
** descriptions and section numbers stay in the internal data, "classlist"
*/

	infile = fopen(OFFEREDCLASSES, "r");
	if (infile == NULL) {
		fprintf(stderr, "Cannot open file %s\n", OFFEREDCLASSES);
		exit(1);
	}

	classcount = 0;

	while (1) {
/*
** Get one line from the data file
*/
		rovingpointer = (char *) &buffer[0];
		newchar = getc(infile);
		charsread = 0;
		while (newchar != '\n' && newchar != EOF && charsread < MAX_LINE) {
			*(rovingpointer++) = (char) newchar;
			newchar = getc(infile);
			charsread++;
		}
		*rovingpointer = '\0';

		if (newchar == EOF)
			break;

		if (sscanf (buffer, "%8s%3d", shortname, &sectionnum) != 2) {
/*
			fprintf (stderr, "WARNING:  Malformed entry \"%s\" in list of classes; ignored.\n", buffer);
			fflush (stderr);
*/
			continue;
		}
/*
** Allocate space to store the data
*/
		classcount++;

		if (classcount == 1) {
			str_list = (XmStringTable)
				XtMalloc(classcount * sizeof(XmString *));
			classlist = (ClassPtr *)
				XtMalloc(classcount * sizeof(ClassPtr));
		}
		else {
			str_list = (XmStringTable) 
				XtRealloc(str_list, classcount * sizeof(XmString *));
			classlist = (ClassPtr *) 
				XtRealloc(classlist, classcount * sizeof(ClassPtr));
		}

/*
** Store it.  Note the potentially dangerous assumption that the long 
** course description begins in column 12 of the text.
*/

		classlist[classcount-1] = (ClassPtr) XtMalloc(1 * sizeof(Class));
		strcpy ((classlist[classcount-1])->course, shortname);
		classlist[classcount-1]->section = sectionnum;

		str_list[classcount-1] = XmStringCreateSimple(buffer+11);
	}

	n = 0;
	XtSetArg(args[n], XmNitemCount, classcount);			n++;
	XtSetArg(args[n], XmNitems, str_list);				n++;
	XtSetArg(args[n], XmNselectionPolicy, XmSINGLE_SELECT);		n++;

	list = XmCreateScrolledList(parent, "list1", args, n);

	XtManageChild(list);

/*
	for (n = 0; n < classcount; n++) {
		XmStringFree(str_list[n]);
	}
	XtFree(str_list);
*/

	XtAddCallback(list, XmNsingleSelectionCallback, clickcallback, NULL);
}

/*
** Make a set of labels that will be updated with the user's selections
**
** If there's data in the "selections" data structure, fill it into the
** appropriate selections box.
*/


MakeFeedbackBoxes(parent)
Widget	parent;
{
	static char *indices[MAX_CHOICES] = {"1st", "2nd", "3rd", "4th", "5th", "6th"};
	char		namebuf[10];
	XmString	str;
	Widget		item, feedrowcol, promptrowcol;
	int		i;

	promptrowcol = XtVaCreateManagedWidget("promptrowcol", 
				xmRowColumnWidgetClass, parent, 
					NULL);

	feedrowcol = XtVaCreateManagedWidget("feedrowcol", 
				xmRowColumnWidgetClass, parent, 
					NULL);

	for (i = 0; i < MAX_CHOICES; i++) {
		sprintf (namebuf, "label%d", i);
		str = XmStringCreateSimple(indices[i]);

		item = XtVaCreateManagedWidget (namebuf, 
				xmLabelWidgetClass, promptrowcol,
				XmNlabelString, str,
			 	NULL);

		XmStringFree(str);

		sprintf (namebuf, "class%d", i);
		feedbackW[i] = XtVaCreateManagedWidget (namebuf, 
				xmTextWidgetClass, feedrowcol,
				XmNautoShowCursorPosition, True,
			 	NULL);
/*
** Is there a value for this selection?  If so, 
** copy the appropriate string out of the scrolling list.

** OOPS!  Doing this BEFORE the widget has been realized causes the text
** widget to scroll all the way to the right.  LOVE that ^%$#@! Motif!

		if ((selections[i].course)[0] != NULL) {
			SetSelection(i);
			currentchoice = i + 1;
		}
*/
	}
}

/*
** Look through the list of offered classes until we find the one with
** the same short name and section number as the previously-made selection.
** Then copy it into the text for the "index"th selection.
*/

SetSelection(index)
int	index;
{
	char		*choice;
	int		i;

	for (i = 0; i < classcount; i++) {
		if ( (!strcmp (classlist[i]->course, selections[index].course) &&
		     (classlist[i]->section == selections[index].section) ) ) {

			XmStringGetLtoR(str_list[i], charset, &choice);
			XmTextSetInsertionPosition(feedbackW[index], 0);
			XmTextSetString(feedbackW[index], choice);
		}
	}
}


static void 
clickcallback(w, client_data, cbs)
Widget			w;
void			*client_data;
XmListCallbackStruct	*cbs;
{
	char	*choice;
	int	choiceindex, j, doup=0;

	changesmade = True;

	XmStringGetLtoR(cbs->item, charset, &choice);

/*
** Nonselectable text?
*/
	if (!strcmp ((classlist[cbs->item_position - 1])->course, "NONSEL")) {
		XBell(XtDisplay(w), 0);
	}
        else if (currentchoice != MAX_CHOICES && *choice != '\0') {

          for (j = 0; j < currentchoice; j ++)  {
  if ( (!strcmp (selections[j].course, ((classlist[cbs->item_position - 1])->course))) && ((classlist[cbs->item_position - 1])->section == selections[j].section ) )
	
       	      {
        	 XBell(XtDisplay(w), 0); 
                 doup = 1;
	       }
	 }  
           if (!doup)    
           { 
		XmTextSetString(feedbackW[currentchoice], choice); 
		strcpy (	selections[currentchoice].course,
				(classlist[cbs->item_position - 1])->course);
		selections[currentchoice].section = 
				(classlist[cbs->item_position - 1])->section;
		currentchoice++;
	       
            } 
	}
	else {
		XBell(XtDisplay(w), 0);
	}

	XtFree (choice);
}


static void 
quitcallback(w, client_data, call_data)
Widget	w;
void	*client_data;
void	*call_data;
{
	int	doit;

	if (changesmade)
		doit = Confirm ("Do you really want to exit without saving your changes?");
	else
		doit = 1;

       	if (doit)  {
		if (!origcurrentchoice) 
			PhDeleteForm("pelott", whoami);
		exit();
	}
}

static void
savecallback(w, client_data, call_data)
Widget	w;
void	*client_data;
void	*call_data;
{
	int	doit;
	if (changesmade)
		doit = Confirm ("Do you really want to save your choices and exit?");
	else
		doit = 1;
 
/*
** Copy the data from the "selections" structure into the form instance
** and send the data to the Phaedo saving routine.  The first step would
** be unnecessary if we were using a "real" form displayer, since the
** data would be kept in the form instance in the first place.
*/
	if (doit) {
		if  (!currentchoice) { 
			PhDeleteForm("pelott", whoami);
                        ErrorMessage("Warning:  You have not made any choices.");
			exit();
		}
		PlugInData(myforminstance, selections);
		if (PhSaveForm(myforminstance) == 1) {
			ErrorMessage("Your choices have been saved.");
			exit();
		      }
	}
}

static void
erasecallback(w, client_data, call_data)
Widget	w;
void	*client_data;
void	*call_data;
{
	changesmade = True;

	if (currentchoice) {
		currentchoice--;
		XmTextSetString(feedbackW[currentchoice], NULL);
		*(selections[currentchoice].course) = NULL;
		selections[currentchoice].section = 0;
	}
	else {
		XBell(XtDisplay(w), 0);
	}
}


/*
** Code originally from eddietwo's xforms package's "main.c"
** with some reversion back to p. 154 of O'Reilly volume 6.
*/



int
Confirm(prompt)
char	*prompt;
{
  static Widget	dialog = NULL;
  int		answer = -1;
  XmString	text, yes, no;


    dialog = XmCreateQuestionDialog (toplevel, "prompt-dialog", NULL, 0);

    XtSetSensitive(
	XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON),
	False);

    yes = XmStringCreateSimple("Yes");
    no = XmStringCreateSimple("No");

    XtVaSetValues (dialog,
       XmNdialogStyle,		XmDIALOG_APPLICATION_MODAL,
       XmNokLabelString,	yes,
       XmNcancelLabelString,	no,
       XmNdefaultButtonType,	XmDIALOG_OK_BUTTON,
       NULL);

       XtAddCallback (dialog, XmNokCallback, prompterokcallback, &answer);
       XtAddCallback (dialog, XmNcancelCallback, promptercancelcallback, &answer);
    
  text = XmStringCreateSimple(prompt);
  XtVaSetValues(dialog,
	XmNmessageString,	text,
	NULL);

  XmStringFree(text);
  XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
  XtManageChild(dialog);
    
  while (answer == -1)
	XtAppProcessEvent(app, XtIMAll);

  XtDestroyWidget(dialog);
  return (answer);
}

/*
** Display an arbitrary message in a modal dialog.
*/

void
ErrorMessage(text)
char	*text;
{
	XmString	str;
	Widget		dialog;
	Arg		args[5];
	int		answer = -1;

	if (!text)
		return;

	str = XmStringCreateLtoR(text, charset);
	XtSetArg(args[0], XmNmessageString, str);
	XtSetArg(args[1], XmNcancelLabelString, NULL);

	dialog = XmCreateMessageDialog(toplevel, "Messagebox", args, 2);
	XtAddCallback (dialog, XmNokCallback, prompterokcallback, &answer);

	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));

	XtManageChild(dialog);
  	while (answer == -1)
		XtAppProcessEvent(app, XtIMAll);

	XmStringFree(str);
	XtDestroyWidget(dialog);
}


/*
** Find our app-defaults file.
*/

SetUpPath()
{
	char	*oldpath, *newpath;

	oldpath = getenv("XFILESEARCHPATH");

#ifdef ultrix

	if (!oldpath) {
		newpath = (char *) malloc (100);
		strcpy (newpath, "XFILESEARCHPATH=/afs/athena.mit.edu/astaff/project/pelott-dev/datafiles/%N");
	}
	else {
		newpath = (char *) malloc (100 + strlen (oldpath));
		sprintf (newpath, "XFILESEARCHPATH=%s:/afs/athena.mit.edu/astaff/project/pelott-dev/datafiles/%%N",oldpath);
	}
	putenv (newpath);

#endif

#ifdef AIX
	if (!oldpath) {
		newpath = (char *) malloc (100);
		strcpy (newpath, "/afs/athena.mit.edu/astaff/project/pelott-dev/datafiles/%N");
	}
	else {
		newpath = (char *) malloc (100 + strlen (oldpath));
		sprintf (newpath, "%s:/afs/athena.mit.edu/astaff/project/pelott-dev/datafiles/%%N",oldpath);
	}
	setenv ("XFILESEARCHPATH",newpath,1);

#endif

/* add sgi to here. 8/7/95 */
#ifdef sgi
        if (!oldpath) {
                newpath = (char *) malloc (100);
                strcpy (newpath, "XFILESEARCHPATH=/afs/athena.mit.edu/astaff/project/pelott-dev/datafiles/sgi/%N");
        }
        else {
                newpath = (char *) malloc (100 + strlen (oldpath));
                sprintf (newpath, "XFILESEARCHPATH=%s:/afs/athena.mit.edu/astaff/project/pelott-dev/datafiles/sgi/%%N",oldpath);
        }
        putenv (newpath);
#endif

#ifdef sparc
	if (!oldpath) {
		newpath = (char *) malloc (100);
		strcpy (newpath, "/afs/athena.mit.edu/astaff/project/hass-d/data/%N");
	}
	else {
		newpath = (char *) malloc (100 + strlen (oldpath));
		sprintf (newpath, "%s:/afs/athena.mit.edu/astaff/project/hass-d/data/%%N",oldpath);
	}
	setenv ("XFILESEARCHPATH",newpath,1);

#endif
}



