/*****************************************************************************
*   A motif interface to the animation controller.			     *
*									     *
* Written by:  Haggay Dagan, Zvika Zilberman and Gershon Elber		     *
*							Ver 0.1, Feb. 1995.  *
*****************************************************************************/

#ifdef __hpux
typedef char *caddr_t;	       /* Awful kludge. Let me know of a better way. */
#endif /* __hpux */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/Xresource.h>

#include <Xm/MainW.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/SelectioB.h>
#include <Xm/RowColumn.h>
#include <Xm/MessageB.h>
#include <Xm/Label.h>
#include <Xm/Scale.h>
#include <Xm/Form.h>

#include <string.h>
#include <math.h>

#include "irit_sm.h"
#include "iritgrap.h"
#include "xmtdrvs.h"

typedef enum {
    FILE_NAME,
    START_TIME,
    FINAL_TIME
} PromptOkTypes;

typedef enum {
    DISMISS,
    BACKWARD,
    FORWARD
} SingleOkTypes;

static Widget AnimationForm, Bounce, Origin, IntervalSlide,
	StartButton, EndButton, Single, FileForm[3], StopMe,
	SingleForm, IntervalLabel;

static void CreateSingle(Widget w);
static void CreatePromptMessage(Widget w, int State);
static void StopAnimationCB(void);
static void SingleOkCB(Widget w, int State);
static void PromptOkCB(Widget w, int State, XmSelectionBoxCallbackStruct *Cbs);
static void TgButtonsCB(Widget w,int State);
static void IntervalCB(Widget w);
static void BeginCB(Widget w);
static void CancelCB(void);
static void StartTimeCB(void);
static void FinishTimeCB(void);

/*****************************************************************************
* DESCRIPTION:								     M
*   creates the main animation window				   	     M
*									     *
* PARAMETERS:								     M
*   The shell Widget (top level shell) 					     M
*									     *
* RETURN VALUE:								     M
*   void								     M
*****************************************************************************/
void CreateAnimation(Widget w)
{
    static char
	*Name[4] = { "Origin", "Bounce", "Single", "ToFile" };
    Arg arg[10];
    Widget Button, Form, SubForm, label1;
    int i;
    char String[10], NewLabel[20]; 
    XmString MotifString;

    AnimFindAnimationTime(&IGAnimation, IGGlblDisplayList);

    XtSetArg(arg[0], XmNfractionBase, 11);
    XtSetArg(arg[1], XmNdefaultPosition, FALSE);
    XtSetArg(arg[2], XmNwidth, 250);
    XtSetArg(arg[3], XmNheight, 400);
    AnimationForm = XmCreateFormDialog(w, "Animation", arg, 4);

    for(i = 0; i < 4; i++) {
        Form = CreateSubForm(AnimationForm, 1 + i);
        SubForm = XtVaCreateManagedWidget("submarin",
					  xmFormWidgetClass,  	Form,
					  XmNleftAttachment,  	XmATTACH_FORM,
					  XmNrightAttachment, 	XmATTACH_FORM,
					  XmNfractionBase,   	5,
					  NULL);
	Button = XtVaCreateManagedWidget(Name[i],     
					 xmToggleButtonWidgetClass, SubForm, 
					 XmNrightAttachment, XmATTACH_POSITION,
					 XmNrightPosition,   3,
					 XmNleftAttachment,  XmATTACH_POSITION,
					 XmNleftPosition,    1,
					 NULL);
	if (i == 0)
	    Origin = Button;
	else if (i == 1)
	    Bounce = Button;
	else if (i == 2)
	    Single = Button;
	XtAddCallback(Button, XmNarmCallback, (XTC) TgButtonsCB, (XTP) i);
    }

    sprintf(NewLabel, "Interval (ms) %d", (int) (IGAnimation.Dt * 1000));
    IntervalLabel = AddLabel(AnimationForm, NewLabel, 9); 
    Form = CreateSubForm(AnimationForm, 8);  
    SubForm = XtVaCreateManagedWidget("submarin",
				      xmFormWidgetClass,  Form,
				      XmNleftAttachment,  XmATTACH_FORM,
				      XmNrightAttachment, XmATTACH_FORM,
				      XmNfractionBase,    10,
				      NULL);
    IntervalSlide = XtVaCreateManagedWidget("slide",
					    xmScaleWidgetClass, SubForm,
					    XmNvalue,		333,
					    XmNshowValue,	FALSE,
					    XmNorientation,	XmHORIZONTAL,
					    XmNminimum,  	0,
					    XmNrightAttachment, XmATTACH_POSITION,
					    XmNrightPosition,	9,
					    XmNleftAttachment,  XmATTACH_POSITION,
					    XmNleftPosition,	1,
					    XmNmaximum,		1000,
					    NULL); 
    XtAddCallback(IntervalSlide, XmNvalueChangedCallback,
		  (XTC) IntervalCB, (XTP)NULL);
    Form = CreateSubForm(AnimationForm, 5);
    SubForm = XtVaCreateManagedWidget("matrixOpFoundation",
				      xmFormWidgetClass,  Form,
				      XmNleftAttachment,  XmATTACH_FORM,
				      XmNrightAttachment, XmATTACH_FORM,
				      XmNfractionBase,    11,
				      NULL);
    MotifString = XmStringCreate("Start", XmSTRING_DEFAULT_CHARSET);    
    label1 =  XtVaCreateManagedWidget("submarin1",
				      xmLabelWidgetClass, SubForm,
				      XmNlabelString,	  MotifString,
				      XmNleftAttachment,  XmATTACH_POSITION,
				      XmNleftPosition,    1,
				      XmNrightAttachment, XmATTACH_POSITION,
				      XmNrightPosition,   5,
				      NULL);
    XmStringFree(MotifString);
    MotifString = XmStringCreate("End", XmSTRING_DEFAULT_CHARSET);    
    label1 =  XtVaCreateManagedWidget("submarin1",
				      xmLabelWidgetClass, SubForm,
				      XmNlabelString,	  MotifString,
				      XmNleftAttachment,  XmATTACH_POSITION,
				      XmNleftPosition,    6,
				      XmNrightAttachment, XmATTACH_POSITION,
				      XmNrightPosition,   10,
				      NULL);
    XmStringFree(MotifString);  
    Form = CreateSubForm(AnimationForm, 6);
    SubForm = XtVaCreateManagedWidget("matrixFoundation",
				      xmFormWidgetClass,  Form,
				      XmNleftAttachment,  XmATTACH_FORM,
				      XmNrightAttachment, XmATTACH_FORM,
				      XmNfractionBase,    11,
				      NULL);
    StartButton = XtVaCreateManagedWidget("Start",
				      xmPushButtonWidgetClass, SubForm, 
				      XmNleftAttachment,  XmATTACH_POSITION,
				      XmNleftPosition,    1,
				      XmNrightAttachment, XmATTACH_POSITION,
				      XmNrightPosition,   5,
				      NULL);
    sprintf(String, "%4.2f", IGAnimation.StartT);
    ReplaceLabel(StartButton, String);
    XtAddCallback(StartButton, XmNactivateCallback,
		  (XTC) StartTimeCB, (XTP) NULL);

    EndButton = XtVaCreateManagedWidget("End", 
				      xmPushButtonWidgetClass,	SubForm, 
				      XmNleftAttachment,   XmATTACH_POSITION,
				      XmNleftPosition,     6,
				      XmNrightAttachment,  XmATTACH_POSITION,
				      XmNrightPosition,    10,
				      NULL);
    XtAddCallback(EndButton, XmNactivateCallback,
		  (XTC) FinishTimeCB, (XTP) NULL);
    sprintf(String, "%4.2f", IGAnimation.FinalT);
    ReplaceLabel(EndButton, String);
    Form = CreateSubForm(AnimationForm, 10); 
    SubForm = XtVaCreateManagedWidget("matrixOpFoundation",
				      xmFormWidgetClass,   Form,
				      XmNleftAttachment,   XmATTACH_FORM,
				      XmNrightAttachment,  XmATTACH_FORM,
				      XmNfractionBase,     11,
				      NULL);
    Button = XtVaCreateManagedWidget("Begin",
				     xmPushButtonWidgetClass, 	SubForm, 
				     XmNleftAttachment,    XmATTACH_POSITION,
				     XmNleftPosition,      1,
				     XmNrightAttachment,   XmATTACH_POSITION,
				     XmNrightPosition,     5,
				     NULL);
    XtAddCallback(Button, XmNactivateCallback,
		  (XTC) BeginCB, (XTP) NULL);

    Button = XtVaCreateManagedWidget("Dismiss",        /* Save Matrix button */
				     xmPushButtonWidgetClass, 	SubForm, 
				     XmNleftAttachment,    XmATTACH_POSITION,
				     XmNleftPosition,      6,
				     XmNrightAttachment,   XmATTACH_POSITION,
				     XmNrightPosition,     10,
				     NULL);
    XtAddCallback(Button, XmNactivateCallback,
		  (XTC) CancelCB, (XTP) NULL);

    /* Create the other sub windows. */
    CreateSingle(w);
    CreatePromptMessage(w, 0);
    CreatePromptMessage(w, 1);
    CreatePromptMessage(w, 2);
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Create the single step window					     *
*									     *
* PARAMETERS:								     *
*   w:   The shell Widget (top level shell) 			 	     *
*									     *
* RETURN VALUE:								     *
*   void.								     *
*****************************************************************************/
static void CreateSingle(Widget w)
{
    static char 
	*Name[3] = { "Dismiss", "Backward", "Forward" };
    int i;
    Arg arg[20];
    Widget Button, SubForm;
    XmString MotifString;
 
    MotifString = XmStringCreate("Single Step", XmSTRING_DEFAULT_CHARSET);
    XtSetArg(arg[0], XmNdialogTitle, MotifString);
    XtSetArg(arg[1], XmNfractionBase,1);
    XtSetArg(arg[2], XmNdefaultPosition,FALSE);
    XtSetArg(arg[3], XmNwidth,300 );
    XtSetArg(arg[4], XmNheight,50 );
    SingleForm = XmCreateFormDialog(w, "single", arg, 5);
    SubForm = XtVaCreateManagedWidget("matrixOpFoundation",
				      xmFormWidgetClass,  SingleForm,
				      XmNleftAttachment,  XmATTACH_FORM,
				      XmNrightAttachment, XmATTACH_FORM,
				      XmNfractionBase,    3,
				      NULL);

    for (i = 0; i < 3; i++) {
	Button = XtVaCreateManagedWidget(Name[i],        
			xmPushButtonWidgetClass, 	SubForm,
			XmNtopAttachment,       	XmATTACH_POSITION,
			XmNtopPosition,        		0,
			XmNbottomAttachment,       	XmATTACH_POSITION, 
			XmNbottomPosition,        	2,
			XmNleftAttachment,       	XmATTACH_POSITION,
			XmNleftPosition,        	i,
			XmNrightAttachment,      	XmATTACH_POSITION,
			XmNrightPosition,        	i + 1,
			NULL);
	XtAddCallback(Button, XmNactivateCallback, (XTC) SingleOkCB, (XTP) i);
    }
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Creates a prompt dialog with message.				     *
*									     *
* PARAMETERS:								     *
*   w:      The shell Widget (top level shell) 			             *
*   State:  Determines which one of the prompt dialogs 			     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void CreatePromptMessage(Widget w, int State)
{
    static char *
	Title[3] ={ "Enter file name",
		    "Enter start time",
		    "Enter finish time" };
    Arg args[3];
    XmString MotifString;

    MotifString = XmStringCreate(Title[State], XmSTRING_DEFAULT_CHARSET);
    XtSetArg(args[0], XmNselectionLabelString, MotifString);
    FileForm[State] = (Widget) XmCreatePromptDialog(w, "File Form", args, 1);

    XmStringFree(MotifString);
    XtAddCallback(FileForm[State], XmNokCallback,
		  (XTC) PromptOkCB, (XTP) State);
    if (!State) {
	MotifString = XmStringCreateSimple("Stop it");
	XtSetArg(args[0], XmNcancelLabelString, MotifString);
	StopMe = (Widget) XmCreateWorkingDialog(w, "Stop it", args, 1);
	XtUnmanageChild(XmMessageBoxGetChild(StopMe, XmDIALOG_OK_BUTTON));
	XtUnmanageChild(XmMessageBoxGetChild(StopMe, XmDIALOG_HELP_BUTTON));
	XtAddCallback(StopMe, XmNcancelCallback,
		      (XTC) StopAnimationCB, (XTP) NULL);
	XmStringFree(MotifString);
    }
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Stops the animation process using an async. event.			     *
*									     *
* PARAMETERS:								     *
*   None								     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void StopAnimationCB(void)
{
    IGAnimation.StopAnim = TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Should we stop this animation? Senses the event queue of X.              M
*                                                                            *
* PARAMETERS:                                                                M
*   Anim:     The animation to abort.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      TRUE if we needs to abort, FALSE otherwise.                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   AnimCheckInterrupt                                                       M
*****************************************************************************/
int AnimCheckInterrupt(AnimationStruct *Anim)
{
    XEvent Event;
    Display
	*XDpy = XtDisplay(IGTopLevel);

    XFlush(XDpy);
    XmUpdateDisplay(IGTopLevel);
    while (XCheckMaskEvent(XDpy,
			   ButtonPressMask |
			   ButtonReleaseMask |
			   ButtonMotionMask |
			   PointerMotionMask |
			   KeyPressMask |
			   KeyReleaseMask,
			   &Event))
	XtDispatchEvent(&Event);

    if (Anim ->StopAnim)
	fprintf(stderr, "\nAnimation was interrupted by the user.\n");

    return Anim -> StopAnim;
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Callback that deals with the single window Buttons			     *
*									     *
* PARAMETERS:								     *
*   w:      The single widget		 			             *
*   State:  Determines whic Button was pressed.		 		     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void SingleOkCB(Widget w, int State)
{
    switch (State) {
        case DISMISS:
	    XtUnmanageChild(SingleForm);
	    break;
	case BACKWARD:
	    if (IGAnimation.RunTime - IGAnimation.Dt >=
					IGAnimation.StartT)
		IGAnimation.RunTime -= IGAnimation.Dt;
	    else
		IGAnimation.RunTime = IGAnimation.StartT;

	    AnimDoSingleStep(&IGAnimation, IGGlblDisplayList);
	    break;
	case FORWARD:
	    if (IGAnimation.RunTime + IGAnimation.Dt <=
					IGAnimation.FinalT)
		IGAnimation.RunTime += IGAnimation.Dt;
	    else
		IGAnimation.RunTime = IGAnimation.FinalT;

	    AnimDoSingleStep(&IGAnimation, IGGlblDisplayList);
	    break;
        default:
	    fprintf(stderr, "Mistake at SingleOkCB\n");
    }
} 

/*****************************************************************************
* DESCRIPTION:								     *
*   Handler for the OK Button on a prompt dialog.			     *
*									     *
* PARAMETERS:								     *
*   w:      The prompt dialog		 			             *
*   State:  Determines which one of the prompt dialogs 			     *
*   Cbs:    Event info.							     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void PromptOkCB(Widget w, int State, XmSelectionBoxCallbackStruct *Cbs)
{
    char *String;
    XmStringGetLtoR(Cbs -> value, XmSTRING_DEFAULT_CHARSET, &String); 

    switch (State) { 
        case FILE_NAME: 
	    strcpy(IGAnimation.BaseFileName, String);
	    break;
	case START_TIME: 
	    IGAnimation.StartT = atof(String);
	    ReplaceLabel(StartButton, String);
	    break; 
	case FINAL_TIME: 
	    IGAnimation.FinalT = atof(String);
	    ReplaceLabel(EndButton, String);
	    break;
    }
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Handler for the toggle Button on a prompt dialog.			     *
*									     *
* PARAMETERS:								     *
*   w:      the animation window	 			             *
*   State:  Determines which one of the toggle Buttons has changed	     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void TgButtonsCB(Widget w,int State)
{
    switch (State) {
	case 0:
	    XmToggleButtonSetState(Bounce, FALSE, FALSE);
	    XmToggleButtonSetState(Single, FALSE, FALSE);
	    break;
	case 1:
	    XmToggleButtonSetState(Origin, FALSE, FALSE);
	    XmToggleButtonSetState(Single, FALSE, FALSE);
	    break;
	case 2:
	    XmToggleButtonSetState(Bounce, FALSE, FALSE);
	    XmToggleButtonSetState(Origin, FALSE, FALSE);
	    break;
	case 3:
	    IGAnimation.SaveAnimation = !IGAnimation.SaveAnimation;
	    break;
    }  
} 

/*****************************************************************************
* DESCRIPTION:								     *
*   Handler for the interval slide.					     *
*									     *
* PARAMETERS:								     *
*   w:	the interval slide Widget					     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void IntervalCB(Widget w)
{
    int Interval;
    char String[20];

    XmScaleGetValue(w, &Interval);
    IGAnimation.Dt = pow(10.0, 0.003 * Interval);
    IGAnimation.Dt = ((int) IGAnimation.Dt) / 1000.0;
    sprintf(String, "Interval (ms) %d", (int) (IGAnimation.Dt * 1000.0));
    ReplaceLabel(IntervalLabel, String);
} 

/*****************************************************************************
* DESCRIPTION:								     *
*   Handler for the Begin button.					     *
* Invokes saving window and/or single step window. If required, starts the   *
* animation.								     *
*									     *
* PARAMETERS:								     *
*   None								     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void BeginCB(Widget w)
{
    Position AnimationWindowX, AnimationWindowY;
    Dimension AnimationWindowW;
    int Interval;
    char String[20];

    IGAnimation.StopAnim = FALSE;

    XmScaleGetValue(IntervalSlide, &Interval);
    IGAnimation.Dt = pow(10.0, 0.003 * Interval);
    IGAnimation.Dt = ((int) IGAnimation.Dt) / 1000.0;
    sprintf(String, "Interval (ms) %d", (int) (IGAnimation.Dt * 1000.0));
    ReplaceLabel(IntervalLabel, String);

    IGAnimation.BackToOrigin = XmToggleButtonGetState(Origin);
    IGAnimation.TwoWaysAnimation = XmToggleButtonGetState(Bounce);
    IGAnimation.SingleStep = XmToggleButtonGetState(Single);
    XtVaGetValues(AnimationForm,
		  XmNwidth,	&AnimationWindowW,
		  XmNx,		&AnimationWindowX,
		  XmNy,		&AnimationWindowY,
		  NULL);

    if (IGAnimation.SaveAnimation) {
	XtVaSetValues(FileForm[0],
		      XmNdefaultPosition, FALSE,
		      XmNx,		  AnimationWindowX +
					  AnimationWindowW,
		      XmNy,		  AnimationWindowY + 100,
		      NULL);
	XtManageChild(FileForm[0]);
    }

    if (IGAnimation.SingleStep) {
	XtVaSetValues(SingleForm,
		      XmNdefaultPosition, FALSE,
		      XmNx,		  AnimationWindowX +
					  AnimationWindowW,
		      XmNy,		  AnimationWindowY + 50,
		      NULL);
	XtManageChild(SingleForm);
    }
    else {
	XtVaSetValues(StopMe,
		      XmNdefaultPosition, FALSE,
		      XmNx,		  AnimationWindowX +
					  AnimationWindowW,
		      XmNy,		  AnimationWindowY + 100,
		      NULL);
	XtManageChild(StopMe);
	AnimDoAnimation(&IGAnimation, IGGlblDisplayList);
	XtUnmanageChild(StopMe);
    }
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Close the animation window						     *
*									     *
* PARAMETERS:								     *
*   None								     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void CancelCB(void)
{
    XtUnmanageChild(AnimationForm);
    if (XtIsManaged(SingleForm))
        XtUnmanageChild(SingleForm);
}

/*****************************************************************************
* DESCRIPTION:								     M
*   Getting input values from the user.					     M
*									     *
* PARAMETERS:								     M
*   None                                 				     M
*									     *
* RETURN VALUE:								     M
*   void 								     
*****************************************************************************/
void AnimationCB(void)
{  
    Position MainWindowX, MainWindowY;
    Dimension MainWindowW;
  
    XtVaGetValues(IGTopLevel,
		  XmNwidth,	&MainWindowW,
		  XmNx,		&MainWindowX,
		  XmNy,		&MainWindowY,
		  NULL);

    XtVaSetValues(AnimationForm,
		  XmNdefaultPosition, FALSE,
		  XmNx,		      MainWindowX + MainWindowW + 16,
		  XmNy,		      MainWindowY,
		  NULL);
    XtManageChild(AnimationForm);
}
									
/*****************************************************************************
* DESCRIPTION:								     *
*   Manages the "change start time " window.				     *
*									     *
* PARAMETERS:								     *
*   None								     *
*									     *
* RETURN VALUE:								     *
*   void 								     *
*****************************************************************************/
static void StartTimeCB(void)
{
    Position MainWindowX, MainWindowY;
    Dimension MainWindowW;
    
    XtVaGetValues(AnimationForm,
		  XmNwidth,	&MainWindowW,
		  XmNx,		&MainWindowX,
		  XmNy,		&MainWindowY,
		  NULL);

    XtVaSetValues(FileForm[START_TIME],
		  XmNdefaultPosition, FALSE,
		  XmNx,		      MainWindowX + 40,
		  XmNy,		      MainWindowY + 320,
		  NULL);
    XtManageChild(FileForm[START_TIME]);
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Manages the "change finish time" window.				     *
*									     *
* PARAMETERS:								     *
*   None								     *
*									     *
* RETURN VALUE:								     *
*   void								     *
*****************************************************************************/
static void FinishTimeCB(void)
{
    Position MainWindowX,MainWindowY;
    Dimension MainWindowW;
  
    XtVaGetValues(AnimationForm,
		  XmNwidth,	&MainWindowW,
		  XmNx,		&MainWindowX,
		  XmNy,		&MainWindowY,
		  NULL);

    XtVaSetValues(FileForm[2],
		  XmNdefaultPosition, FALSE,
		  XmNx,		      MainWindowX + 80,
		  XmNy,		      MainWindowY + 350,
		  NULL);
    XtManageChild(FileForm[2]);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Adds a new label to an old widget.  				     M
*                                                                            *
* PARAMETERS:                                                                M
*   w:      Calling widget.      		                             M
*   Label:  New label.							     M
*                                                                            *
* RETURN VALUE:                                                              M
*   none			                                             M
*                                                                            *
* KEYWORDS:                                                                  M
*   ReplaceLabel				       			     M
*****************************************************************************/
void ReplaceLabel(Widget  w, char *NewLabel)
{ 
    XmString LabelText;
    Arg args[10];

    LabelText = XmStringCreate(NewLabel, "CharSet1");
    XtSetArg(args[0], XmNlabelString, LabelText);
    XtSetValues(w, args, 1);
    XmStringFree(LabelText);
}
