/* 
 *  @OPENGROUP_COPYRIGHT@
 *  COPYRIGHT NOTICE
 *  Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
 *  Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
 *  ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
 *  the full copyright text.
 *  
 *  This software is subject to an open license. It may only be
 *  used on, with or for operating systems which are themselves open
 *  source systems. You must contact The Open Group for a license
 *  allowing distribution and sublicensing of this software on, with,
 *  or for operating systems which are not Open Source programs.
 *  
 *  See http://www.opengroup.org/openmotif/license for full
 *  details of the license agreement. Any use, reproduction, or
 *  distribution of the program constitutes recipient's acceptance of
 *  this agreement.
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
 *  PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 *  WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
 *  OR FITNESS FOR A PARTICULAR PURPOSE
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
 *  NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
 *  EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGES.
*/ 
/* 
 * HISTORY
*/ 
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: Pause.c /main/10 1995/07/14 11:59:23 drk $"
#endif
#endif
#include "testlib.h"
#include <stdlib.h>
#include <math.h>
#include <stdio.h>


#define MAX_LINE        4096

#ifdef REGRESSION
#define CONTINUE_STRING " Press either Pass or Fail to continue testing."
#define END_STRING      \
		" If no instructions press Exit, else Pass/Fail and Exit"
#else
#define CONTINUE_STRING " Press the Continue Button for more testing."
#define END_STRING      " Test Finished -- Exit Please."
#endif /* REGRESSION */

extern Boolean startup_time;
extern char		*CompleteTestName;


Widget        	InstructionBox; 	/* Global Widget for Instruction 
					   MessageBox */
FILE     	*output_file;  		/* Global summary output file */
static Widget	ContinuePopup = NULL;   /* Shell to contain MessageBox */
static Boolean 	continue_created = False;
static char     line[MAX_LINE];		/* Pre-Formatted Input */
static char     line_format[MAX_LINE];  /* Formmated Input */
static Boolean	event_wait;		/* wait for events in queue */


extern void   CommonCheckEvent(XEvent *event, Widget shell);
void   FlushEvents();
static char   *GetInstructionsFromFile();
static Widget CreateContinuePopup();
static void   ContinueCB(Widget w, caddr_t client_data,
			 caddr_t call_data);
static void   ContinueCBWrapper(Widget w, caddr_t client_data,
			 caddr_t call_data);
static void   QuitCB(Widget w, caddr_t client_data,
		     caddr_t call_data);


#ifdef REGRESSION
/* some global symbols */
static int num_panels = 0;
static int num_panels_passed = 0;
static int num_panels_failed = 0;
#endif /* REGRESSION */

static int last_panel_flag = 0;

/*************************************************************************
*									 *
*	Name:		CommonPause()					 *
*									 *
* 	Purpose:	To Allow the writer and the user of the test to  *
*			stop at designed points in the test and give     *
*		        directions on what the users should minipulate   *
*			and what the expected action are going to be.    *
*			The Pause configuration will be a Message Box    *
*			located at (500, 0) which will contain 		 *
*			instructions on what the tester wants the user   *
*			to do.						 *
*									 *
*	Returns:	Nothing						 *
*									 *
*************************************************************************/

void  CommonPause()
{
     XmString 	message_XmString;
     Arg	args[1];
     int 	n = 0;
     int	i, size;
     static int panel_num = 1;
     char	buf[32];

	 purify_newleaks();
     purify_clear_newleaks();

     if (instructions) {

         if (!continue_created)
     	    ContinuePopup = CreateContinuePopup();

         GetInstructionsFromFile();
         message_XmString = XmStringCreateLtoR(line_format,
     					   XmFONTLIST_DEFAULT_TAG);

         n = 0;
         XtSetArg(args[n], XmNmessageString, message_XmString); 	n++;
         XtSetValues(ContinuePopup, args, n);

         XtPopup(XtParent(ContinuePopup), XtGrabNone);

	 /* number the panels for easier reporting of bugs */
	 sprintf (buf, "Instruction Panel %d", panel_num++);

	 n = 0;
	 XtSetArg (args[n], XmNtitle, buf); n++;
	 XtSetValues (XtParent(ContinuePopup), args, n);

         XmStringFree(message_XmString);

	 size = strlen(line_format);
	 for (i = 0 ; i < size ; i++)
		line_format[i] = '\0';

	 size = strlen(line);
	 for (i = 0 ; i < size ; i++)
		 line[i] = '\0';

         event_wait = True;

         while (event_wait)
     	    FlushEvents();
     }


}


/*************************************************************************
*									 *
*	Name:		FlushEvents()					 *
*									 *
*	Purpose:	Flush all events from the Pending Queue		 *
*									 *
*	Returns:	Nothing						 *
*									 *
*************************************************************************/

void  FlushEvents()
{
     XEvent event;

     /* change for Pir 2478 - blocking select() */

if (startup_time) 
     do {
        XtAppNextEvent(app_context, &event);
        CommonCheckEvent(&event, Shell1);
        XtDispatchEvent(&event);
    } while (XtAppPending(app_context));

  else 
     do {
        XtAppNextEvent(app_context, &event);
        XtDispatchEvent(&event);
    } while (XtAppPending(app_context));

}


/*************************************************************************
*									 *
* 	Name: 		CreateContinuePopup()				 *
*									 *
*	Purpose:	To intially create a MessageBox Dialog for use   *
*                       in Motif test suite. This routine will create    *
*			a MessageDialog and set callbacks to exit the    *
*			test or continue with the next phase in the test *
*			It also loads test_font for the font of the      *
*			Dialog (if avaiable).				 *
*									 *
*	Returns:	This function will return the MessageDialog to   *
*			be used in the rest of the test.		 *
*								  	 *
*************************************************************************/

static Widget CreateContinuePopup()
{
    Arg           args[10];
    int           n;
    Widget        exit_button,
    	      	  help_button,
                  continue_button;
    XmString      message_string,
                  continue_string,
                  exit_string,
		  fail_string;
    XmFontList    fontlist;
    Widget	  PopupShell;
#ifdef REGRESSION
    Widget	  FailButton;
#endif /* REGRESSION */

    fontlist = CommonGetFontList(test_font);

#ifdef REGRESSION
    continue_string = XmStringCreateLtoR("Pass",
    				   XmFONTLIST_DEFAULT_TAG);
#else
    continue_string = XmStringCreateLtoR("Continue", 
    				   XmFONTLIST_DEFAULT_TAG);
#endif /* REGRESSION */

    exit_string = XmStringCreateLtoR("Exit", 
    				   XmFONTLIST_DEFAULT_TAG);

    n = 0;
    XtSetArg(args[n], XtNgeometry, "+500+0"); 			n++;
    XtSetArg(args[n], XtNallowShellResize, True);		n++;
    PopupShell = XtCreatePopupShell("Test Instructions", 
				    topLevelShellWidgetClass, Shell1, 
				    args, n);

    n = 0;
    XtSetArg(args[n], XmNokLabelString, continue_string); 	n++;
    XtSetArg(args[n], XmNcancelLabelString, exit_string); 	n++;
    XtSetArg(args[n], XmNbuttonFontList, fontlist); 		n++;
    XtSetArg(args[n], XmNlabelFontList, fontlist);		n++;
    XtSetArg(args[n], XmNshadowThickness, 10);			n++;
    XtSetArg(args[n], XmNmarginWidth, 15);			n++;
    XtSetArg(args[n], XmNmarginHeight, 15);			n++;
    XtSetArg(args[n], XmNdialogType, XmDIALOG_INFORMATION);	n++;
    InstructionBox = XmCreateMessageBox(PopupShell, "InstructionBox", args, n);

#ifdef REGRESSION
    fail_string = XmStringGenerate("Fail", XmFONTLIST_DEFAULT_TAG,
                    XmCHARSET_TEXT, NULL );

    n = 0;
    XtSetArg (args[n], XmNlabelString, fail_string);		n++;
    XtSetArg (args[n], XmNuserData, 0); 			n++;
    FailButton = XmCreatePushButton (InstructionBox, "FailButton", args, n);
    XtManageChild (FailButton);

    XmStringFree(fail_string);
#endif /* REGRESSION */

    XtManageChild(InstructionBox);

    XmStringFree(continue_string);
    XmStringFree(exit_string);

    continue_button = XmMessageBoxGetChild(InstructionBox, XmDIALOG_OK_BUTTON);
    exit_button = XmMessageBoxGetChild(InstructionBox, XmDIALOG_CANCEL_BUTTON);
    help_button = XmMessageBoxGetChild(InstructionBox, XmDIALOG_HELP_BUTTON);

#ifdef REGRESSION
    n = 0;
    XtSetArg (args[n], XmNuserData, 1); n++;
    XtSetValues (continue_button, args, n);

    XtAddCallback(continue_button, XmNactivateCallback, 
 		  (XtCallbackProc)ContinueCBWrapper, InstructionBox);
    XtAddCallback(FailButton, XmNactivateCallback, 
 		  (XtCallbackProc)ContinueCBWrapper, InstructionBox);
#else
    XtAddCallback(continue_button, XmNactivateCallback, 
 		  (XtCallbackProc)ContinueCB, InstructionBox);
#endif /* REGRESSION */

    XtAddCallback(exit_button, XmNactivateCallback, 
 		  (XtCallbackProc)QuitCB, InstructionBox);

    n = 0;
    XtSetArg(args[n], XmNsensitive, False);			n++;
    XtSetValues(help_button, args, n);

    continue_created = True;

    return(InstructionBox);
}


/*************************************************************************
*									 *
*	Name:		ContinueCB()					 *
*									 *
*	Purpose:	A Callback routine which will Unmanage the 	 *
*			Message Dialog so that it can read the next test *
*			case associated with this test.			 *
*									 *
*	Returns:	Nothing						 *
*									 *
*************************************************************************/

static void ContinueCB(Widget w, caddr_t client_data, 
		       caddr_t call_data)

{
     event_wait = False;
     XtPopdown(XtParent((Widget)client_data));
}

/*************************************************************************
*									 *
*	Name:		ContinueCBWrapper()				 *
*									 *
*	Purpose:	Replace normal ContinueCB(), keep records of     *
*			pass/fail per panel for regression testing.      *
*			Also contains functionality of ContinueCB().     *
*									 *
*	Returns:	Nothing						 *
*									 *
*************************************************************************/

static void ContinueCBWrapper (Widget w, caddr_t client_data, 
		       caddr_t call_data)

{
     /* keeping track stuff */
     int id;
     Arg args[1];

     /* we're here, so a new panel */
#ifdef REGRESSION
     num_panels++;

     /* the userData of w tells which button it was */

     XtSetArg (args[0], XmNuserData, &id); 
     XtGetValues (w, args, 1);

     if (id) 	/* then we passed */
	num_panels_passed++;
     else num_panels_failed++;
#endif /* REGRESSION */
     
     /* regular ContinueCB stuff */
     if (!last_panel_flag) {
     	event_wait = False;
     	XtPopdown(XtParent((Widget)client_data));
     }
}

/*************************************************************************
*									 *
*	Name:		QuitCB()					 *
*									 *
*	Purpose:	A Callback routine which will exit the test. It  *
*			does this with simple exit(0).			 *
*									 *
*	Returns:	Nothing						 *
*									 *
*************************************************************************/

static void QuitCB(Widget w, caddr_t client_data,
		   caddr_t call_data)
{
  float a,b;

  XmAnyCallbackStruct *cbs;
  XEvent *xevent;
  
  if (call_data != NULL) {
	cbs = (XmAnyCallbackStruct *) call_data;
  	if (cbs) xevent = (XEvent *)cbs->event;
  	if (xevent->xbutton.state & ControlMask) {
    		CommonDumpHierarchy (Shell1, stdout);
    		return;
        }
  }
  printf("Exiting Test...Please Standby...\n");

     /*
      * Terminate malloc processing.  Will do a malloc_dump
      *  if enabled.
      */

#ifdef REGRESSION
  if (num_panels) {
	

        /* dump summary */
        fprintf (output_file, "------------------------------------\n");
        fprintf (output_file, "   Test results: %s             \n", CompleteTestName);
        fprintf (output_file, "   Passed        Failed      Total  \n");
        fprintf (output_file, "     %d            %d          %d   \n", 
						       num_panels_passed,
      						       num_panels_failed,
	                                               num_panels);
        a = (float)num_panels_passed/num_panels;
        b = (float)num_panels_failed/num_panels;
        fprintf (output_file,"%9.2f%%%11.2f%%            \n", a*100, b*100);
        fprintf (output_file, "------------------------------------\n\n");


        fflush (stdout);
	fclose (output_file);
     }
#endif /* REGRESSION */

     MallocExit(0);   /*  0 indicates normal exit */

	 purify_newleaks();
     purify_clear_newleaks();

     exit(0);
}


/*************************************************************************
*									 *
*	Name:		GetInstructionsFromFile()			 *
*									 *
*	Purpose:	A routine that will open a file that contains    *
*			instructions for the given test. The name of     *
*			this file will be determined by the name of the  *
*			test with a .dat extension. Once this file is    *
*			opened, instructions will be extracted. Data     *
*			will be extracted be reading the input file      *
*			character by character and checking for special  *
*			characters. Once these characters have been      *
*			processed a formatted string of chars will be    *
*			created to be converted to an XmString and to be *
*			displayed in the Message Dialog.		 *
*									 *
*	Special Chars:	\   : escape next character			 *
*			\t  : remove all tabs subsitute with 1 space     *
*			\n  : strip all newlines let code reformat       *
*			' ' : remove multiple spaces let code reformat   *
*			#)  : beginning of test case. # subsituted to    *
*			      test case number.				 *
*			C)  : Add standard continue statment to data     *
*		    	      stream. End current screen		 *
*			@)  : Indented format. Format without a test     *
*			      case number.				 *
*			!)  : No format. No formatting except width 	 *
*			      formatting will be performed.		 *
*									 *	
*    	Returns:	One "Frame" of instructions.			 *
*									 *
*	Note:		One "Frame" of instructions must not exceed more *
*			than 9 cases.					 *
*									 *
*************************************************************************/

static char *GetInstructionsFromFile()

{
     static FILE	*instruct_file;
     static Boolean	instruction_opened = False;
     static Boolean	end_data = False;
     static Boolean	no_format;
     int		array_index = 0;
     int		array_index_format = 0;
     int 		test_number = 1;
     int		count, i, len;
     Boolean		end_line;
     Boolean		char_read = False;
     Boolean		Bextended;
     char		c, c2;
     char		temp_s[10];
     int                line_length;

     /* Open file to read instructions */

     if (!instruction_opened) {
         if (!(instruct_file = fopen(instruct_file_name, "r"))) {
            if (!(instruct_file = fopen(instruct_file_name_orig, "r"))) {
                printf("Failure Opening: %s\n", instruct_file_name);
                exit(0);
            }
            else {
                fprintf(stderr,
                        "Using %s, please create a %s file\n",
                        instruct_file_name_orig, instruct_file_name);
                instruction_opened = True;
            }
          }
          else
            instruction_opened = True;
     }


     /* Start first pass through the input data. Identify special
	characters and get the input string into one flat stream of
	data */

     if (((c  =  (char) fgetc(instruct_file)) == EOF) || end_data) {
	fprintf(stderr, "Pause information exhausted\n");
	exit(-1); 
     }
  
     end_line = False;
     Bextended = False;
     no_format = False;
     array_index = 0;

     while (!end_line && c != EOF) {
     	switch (c) {
		/* End of Instructions */
		case 'E' :	c2 =  (char) fgetc(instruct_file);
                                if (c2 == ')') {
					if (Bextended)
						line[array_index++] = '\n';
					end_line = True;
					end_data = True;
					line[array_index++] = '\n';
					line[array_index++] = '\n';
					strcat(line, END_STRING);
					last_panel_flag = 1;
				}
				else {
                                         line[array_index++] = c;
                                         line[array_index++] = c2;
                                }
                                break;
		/* End of one Panel of Instructons */
		case 'C' :	c2 =  (char) fgetc(instruct_file);
				if (c2 == ')') {
                                        end_line = True;
					line[array_index++] = '\n';
					line[array_index++] = '\n';
					strcat(line, CONTINUE_STRING);
				}
				else {
					 line[array_index++] = c;
					 line[array_index++] = c2;
				}
				break;
		/* Beginning of a line of instructions within a panel */
		case '#' :	c2 =  (char) fgetc(instruct_file);
				if (c2 == ')') {
					if (Bextended)
						line[array_index++] = '\n';
					if (!no_format)
						line[array_index++] = '\n';
					line[array_index++] = '#';
					line[array_index++] = ')';
				}
				else {
					line[array_index++] = c;
					line[array_index++] = c2;
				}
				Bextended = False;
				no_format = False;
				break;
		/* Beginning of Bextended data case - indent lines */
		case '@':	c2 =  (char) fgetc(instruct_file);
				if (c2 == ')') {
					Bextended = True;
					line[array_index++] = '\n';
					if (!no_format)
						line[array_index++] = '\n';
					for (i = 0; i < 2 ; i++)
						line[array_index++] = ' ';
				}
				else {
					line[array_index++] = c;
					line[array_index++] = c2;
				}
				no_format = False;
				break;
		/* Start a no formatting block */
		case '!':	c2 =  (char) fgetc(instruct_file);
				if (c2 == ')')  {
					line[array_index++] = '\n';
					no_format = True;
				}
				else {
					line[array_index++] = c;
					line[array_index++] = c2;
				}
				break;
		/* Strip newlines, let second pass format */
		case '\n':	if (no_format) 
					line[array_index++] = '\n';
				else {
					c2 = ' ';
					char_read = True;
				}
				break;
		/* Strip tabs, let second pass format */
	   	case '\t':	line[array_index++] = ' ';
				break;
		/* Escape next character */
		case '\\':	c2 =  (char) fgetc(instruct_file);
				if (c2 != 'n')
					line[array_index++] = c2;
				if (c2 == 'n' && no_format)
					line[array_index++] = '\n';
				break;
		/* Remove all spaces, let second pass format */
		case ' ':	c2 =  (char) fgetc(instruct_file);
				if (!no_format) {
					while (c2 == ' ')
						c2 = (char)fgetc(instruct_file);
					if (c2 != '\n')
						line[array_index++] = ' ';
				}
				else {
					line[array_index++] = ' ';
					while (c2 == ' ') {
						c2 = (char)fgetc(instruct_file);
						line[array_index++] = ' ';
					}
				}
				char_read = True;
				break;
		/* Normal characters */
		default:	if (isdigit(c)) {
					sprintf(temp_s, "%c", c);
					line[array_index++] = temp_s[0];
				}
				else 
					line[array_index++] = c;
				break;
	}
	if (char_read) {
		c = c2;
		char_read = False;
	}
	else
        	c  =  (char) fgetc(instruct_file);
     }

     /* Begin second pass. Format the first data string and put
	formatted information into a second string */

     array_index = 0;
     array_index_format = 0;
     count = 1;
     line_length = strlen(line);
     while (array_index < line_length) {
	if (line[array_index++] == '#') {
		if (line[array_index] == ')') {
			sprintf(temp_s, "%d", test_number++);
			line_format[array_index_format++] = temp_s[0];
			line_format[array_index_format++] = ')';
			array_index++;
			count = 1;
		}
		else {
			count += 2;
			line_format[array_index_format++] =
						line[array_index - 1];
			line_format[array_index_format++] = 
						line[array_index++];
		}
	}
	else  {
		if (line[array_index - 1] == '\n') {
			line_format[array_index_format++] = 
						line[array_index - 1];
			count = 1;
		}
		else {
			line_format[array_index_format++] = 
						line[array_index - 1];
			count++;
		}
	}
	if ((count % pause_len) == 0) {
	        while (array_index < line_length &&
		        line[array_index] != ' ' &&
			line[array_index] != '\n') {
			line_format[array_index_format++] = 
						line[array_index++];
		}
		if ((line[array_index + 1] != '\n' ) &&
		    (line[array_index + 1] != '#')) {
			line_format[array_index_format++] = '\n';
			line_format[array_index_format++] = ' ';
			line_format[array_index_format++] = ' ';
			line_format[array_index_format++] = ' ';
			array_index++;
		}
       	}		
     }

     line_format[array_index_format] = '\n';

     return(line_format);
}
