 /*
  * Khoros: $Id: autorun.c,v 1.4 1992/03/20 22:42:19 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: autorun.c,v 1.4 1992/03/20 22:42:19 dkhoros Exp $";
#endif

 /*
  * $Log: autorun.c,v $
 * Revision 1.4  1992/03/20  22:42:19  dkhoros
 * VirtualPatch5
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 *
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "cantata.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    file name:  autorun.c                             <<<<
   >>>>                                                       <<<<
   >>>>   description:                                        <<<<
   >>>>                                                       <<<<
   >>>>      routines:  xvl_autorun()                         <<<<
   >>>>                 xvl_autorun_scheduler()               <<<<
   >>>>                 xvl_autorun_dispatcher()              <<<<
   >>>>                 xvl_demand_scheduler()                <<<<
   >>>>                 xvl_dispatch_glyph()                  <<<<
   >>>>                 xvl_schedule_glyph()                  <<<<
   >>>>                                                       <<<<
   >>>> modifications:					      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


static GlyphList *disable_glyph();

/************************************************************
*
* Routine Name:  xvl_autorun
*
*      Purpose:  This routine is used to initiate the automatic
*		 scheduling and dispatch of a workspace.  The
*		 workspace contains a list of glyphs which are
*		 to be scheduled for dispatching.
*
*        Input:  workspace - the workspace to be dispatched
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_autorun(workspace)

Workspace	*workspace;
{
	Widget       stop_button;


	/*
	 *  See if the workspace has a stop autorun button.
	 */
	stop_button = xvl_find_stop_button(workspace->menuform);

	/*
	 *  Make sure auto run is not currently running.  If so then
	 *  stop autorun by deleting the work procedure and setting
	 *  the autorun flag to be false;
	 */
	if (workspace->autorun == True)
	{
	   xvl_abort_autorun(workspace);
	   return;
	}

	/*
	 *  Otherwise we want to start the automatic dispatch mechanism.
	 */
	if (stop_button != NULL)
	{
	   XtMapWidget(stop_button);
	   XRaiseWindow(XtDisplay(stop_button), XtWindow(stop_button));
	   xvf_reverse_colors(stop_button);
	}

	/*
	 *  Build frontier list of the all the modified source nodes.
	 */
	xvl_autorun_scheduler(workspace);
	if (workspace->frontlist != NULL)
	{
	   workspace->autorun = True;
	   xvl_autorun_dispatcher(workspace);
	}
	else
	   xvl_abort_autorun(workspace);
}



xvl_abort_autorun(workspace)

Workspace *workspace;
{
	Widget    stop_button;
	GlyphList *running;
	Glyph     *glyph;
	Workspace *macro;


	/*
	 *  See if the workspace has a stop autorun button.
	 */
	stop_button = xvl_find_stop_button(workspace->menuform);
	if (stop_button != NULL)
	{
	   xvf_reverse_colors(stop_button);
	   XtUnmapWidget(stop_button);
	}
	xvl_restore_glyph(workspace->parent);

	running = workspace->running;
	while (running != NULL)
	{
	   glyph = running->glyph;
	   running = running->next;
	   if (glyph->run_type != SINK && glyph->output_list != NULL)
	      xvl_stop_glyph(glyph);

	   if (glyph->type == PROCEDURE)
	   {
	      macro = glyph->val.macro;
	      xvl_abort_autorun(macro);
	   }
	}
	xvl_destroy_glyphlist(workspace->frontlist);
	xvl_destroy_glyphlist(workspace->dispatched);

	workspace->autorun    = False;
	workspace->frontlist  = NULL;
	workspace->dispatched = NULL;
}


GlyphList *xvl_disable_glyph(glyph, scheduled)

Glyph	   *glyph;
GlyphList  *scheduled;
{
	GlyphList *disabled = NULL;

	disabled = disable_glyph(glyph, &scheduled, disabled);
	xvl_destroy_glyphlist(disabled);
	return(scheduled);
}

static GlyphList *disable_glyph(glyph, scheduled, disabled)

Glyph	   *glyph;
GlyphList  **scheduled, *disabled;
{
	Node	  *node;
	NodeList  *output, *links;
	Workspace *workspace;


	/*
	 *  In case we have scheduled a glyph on the frontier list which is a
	 *  descendant of a glyph that will also be running, then we must delete
	 *  it otherwise it may get run before the child's parent does.
	 */
	if (xvl_check_if_scheduled(glyph) == True)
	{
	   workspace = glyph->workspace;
	   workspace->frontlist = xvl_delete_from_glyphlist(glyph,
			workspace->frontlist);
	   return(disabled);
	}

	output = glyph->output_list;
	while (output != NULL)
	{
	   links = output->node->links;
	   while (links != NULL)
	   {
	      node = links->node;
	      if (xvl_check_if_glyphlist(node->glyph, disabled) == False)
	      {
	         disabled = xvl_add_to_glyphlist(node->glyph, disabled);
	         *scheduled = xvl_add_to_glyphlist(node->glyph, *scheduled);
	         disabled = disable_glyph(node->glyph, scheduled, disabled);
	      }
	      links = links->next;
	   }
	   output = output->next;
	}
	return(disabled);
}



/************************************************************
*
* Routine Name:  xvl_autorun_scheduler
*
*      Purpose:  This routine is used to initiate the automatic
*		 scheduling and dispatch of a workspace.  The
*		 workspace contains a list of glyphs which are
*		 to be scheduled for dispatching.
*
*        Input:  workspace - the workspace to be dispatched
*
*
*   Written By: Mark Young & Scott Wilson
*
*************************************************************/


xvl_autorun_scheduler(workspace)

Workspace *workspace;
{
	Glyph	   *glyph;
	GlyphList  *glyphlist, *scheduled, *autorun_scheduler();


	scheduled = NULL;
	glyphlist = workspace->glyphs;


	while (glyphlist != NULL)
	{
	   glyph = glyphlist->glyph;
	   if (glyph->run_type == SOURCE)
	      scheduled = autorun_scheduler(workspace, glyph, scheduled);

	   glyphlist = glyphlist->next;
	}

	/*
	 *  If no glyphs were found to be scheduled but there are glyphs
	 *  on the workspace, that means there is no SOURCE glyphs to
	 *  schedule from.
	 */
	if (scheduled == NULL && workspace->glyphs != NULL)
	{
	   xvf_error_wait("Warning!  You have glyphs in the workspace but none \
can be executed because there are incomplete connections.  Click on the 'check'\
 button to see all glyphs with unspecified connections.",
			  "xvl_autorun_scheduler", NULL);
	}
	else
	   xvl_destroy_glyphlist(scheduled);
}

GlyphList *autorun_scheduler(workspace, glyph, scheduled)

Workspace *workspace;
Glyph	  *glyph;
GlyphList *scheduled;
{
	Glyph	  *child;
	NodeList  *output, *links;
	GlyphList *xvl_disable_glyph();


	/*
	 *  Check to see if this glyph has been previously scheduled.  If
	 *  so then we need not continue, otherwise add it onto the scheduled
	 *  list so that we won't schedule it again.
	 */
	if (xvl_check_if_glyphlist(glyph, scheduled) == True)
	   return(scheduled);
	else
	   scheduled = xvl_add_to_glyphlist(glyph, scheduled);

	/*
	 *  Check to see if the current glyph is ready to be scheduled for
	 *  dispatching.  Otherwise we go thru it's output list looking to
	 *  see if any of it's children need to be scheduled.
	 */
	if (glyph->modified == True || glyph->run_type == SINK ||
	    (glyph->type == CONTROL && xvl_control_modified(glyph)))
        {
	   glyph->modified = True;
	   xvl_update_modified(glyph);

	   scheduled = xvl_disable_glyph(glyph, scheduled);
	   workspace->frontlist = xvl_add_to_glyphlist(glyph,
			   workspace->frontlist);
	}
	else
	{
	   output = glyph->output_list;
	   while (output != NULL)
	   {
	      links = output->node->links;
	      while (links != NULL)
	      {
	         child = links->node->glyph;
		 if (child->run_type != SOURCE &&
		     xvl_check_if_external(glyph, child) == False)
		 {
		    scheduled = autorun_scheduler(workspace, child, scheduled);
		 }
		 links = links->next;
	      }
	      output = output->next;
	   }
	}
	return(scheduled);
}



/************************************************************
*
* Routine Name:  xvl_autorun_dispatcher
*
*      Purpose:  This routine is used as the work procedure
*		 for dispatching the glyphs belonging to a workspace.
*		 The workspace contains a list of glyphs which are
*		 put on to a freontier list.  The dispatcher dispatches
*		 the next glyph on that list until there is no glyphs
*		 left to be dispatched.  The dispatcher is done when
*		 the frontier list is empty (NULL).
*
*        Input:  workspace - the workspace to be dispatched
*
*
*   Written By: Mark Young & Scott Wilson
*
*************************************************************/


xvl_autorun_dispatcher(workspace)

Workspace *workspace;
{
	int	  running, status;
	Workspace *attributes;

	Glyph	  *glyph;
	GlyphList *frontlist, *glyphlist;


	/*
	 *  Need to look thru the frontier list to see if there is any glyphs
	 *  ready to be dispatched.
	 */
	attributes = xvl_get_attributes(workspace);

	while (workspace->frontlist != NULL)
	{
	   /*
	    *  See if any of the glyphs on the frontier list need to be
	    *  dispatched.  Upon the first one break the loop so that it
	    *  gets dispatched.
	    */
	   frontlist = workspace->frontlist;
	   do
	   {
	      glyph = frontlist->glyph;
	      if (xvl_check_if_runnable(glyph) == True &&
		  xvl_check_if_enabled(glyph) == True &&
		  xvl_check_if_glyphlist(glyph, workspace->dispatched) == NULL)
	      {
		 break;
	      }
	      glyph = NULL;
	      frontlist = frontlist->next;
	   } while (frontlist != NULL);

	   if (glyph == NULL)
	      break;

	   /*
	    *  Otherwise dispatch the glyph and delete it from the frontier
	    *  list.  Record the status so that we can know to continue.
	    */
#ifdef DEBUG
fprintf(stderr,"dispatcher: dispatching glyph %s.\n", glyph->label_str);
#endif
	   workspace->frontlist = xvl_delete_from_glyphlist(glyph,
                                workspace->frontlist);
	   status = xvl_dispatch_glyph(glyph);

	   /*
	    *  If the glyph is successfully dispatched and running and parallel
	    *  execution is set to no then break out of this loop.
	    */
	   if (attributes->parallel == False &&
	       status == DISPATCH_GLYPH_RUNNING)
	   {
	      break;
	   }
	}

	running = False;
	glyphlist = workspace->running;
	while (glyphlist != NULL)
	{
	   glyph = glyphlist->glyph;
	   if (!(glyph->run_type == SINK && glyph->exec_type == ONERUN) &&
		glyph->output_list != NULL || glyph->type == PROCEDURE)
	   {
	      running = True;
	      break;
	   }
	   glyphlist = glyphlist->next;
	}

	if (running == False)
	{
	   frontlist = NULL;
	   glyphlist = workspace->frontlist;
	   while (glyphlist != NULL)
	   {
	      glyph = glyphlist->glyph;
	      if (glyph->run_type != SINK  && glyph->output_list != NULL)
	         frontlist = xvl_add_to_glyphlist(glyph, frontlist);

	      glyphlist = glyphlist->next;
	   }

	   xvl_abort_autorun(workspace);
	   if (workspace->parent != NULL)
	   {
	      xvl_schedule_glyph(workspace->parent);
	      xvl_autorun_dispatcher(workspace->parent->workspace);
	   }

	   if (frontlist != NULL)
	   {
	      glyphlist = frontlist;
	      while (glyphlist != NULL)
	      {
	         glyph = glyphlist->glyph;
	         XtMapWidget(glyph->error);
	         xvl_blink_glyph(glyph);
	         glyphlist = glyphlist->next;
	      }

	      xvf_error_wait("Warning!  The following blinking glyphs were \
scheduled to be executed but were never dispatched.  This implies that the \
blinking  glyphs never had all of their input data available connections (DAV) \
in order for cantata to dispatch the glyph.  Although this is not an error \
it may represent a problem in your current data flow execution or cantata \
graph.", "xvl_autorun_dispatcher", NULL);

	      glyphlist = frontlist;
	      while (glyphlist != NULL)
	      {
		 glyph = glyphlist->glyph;
		 XtUnmapWidget(glyph->error);
		 xvl_unblink_glyph(glyph);
		 glyphlist = glyphlist->next;
	      }
	      xvl_destroy_glyphlist(frontlist);
	   }
	}
}



/************************************************************
*
* Routine Name:  xvl_demand_scheduler
*
*      Purpose:  This routine is used to initiate the demand
*		 scheduling and dispatch of a glyph.  If the
*		 workspace demand driven paramter is on and a
*		 glyph is either modified or explicitly run
*		 by the user the then we need to start a demand
*		 driven scheduling of the workspace, with respect
*		 to the glyph.
*
*        Input:  glyph - the glyph to be scheduled
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_demand_scheduler(glyph)

Glyph *glyph;
{
	Widget    stop_button;
	Workspace *workspace;
	GlyphList *scheduled, *demand_scheduler();


	if (xvl_check_if_scheduled(glyph) == False)
	{
	   scheduled = demand_scheduler(glyph, NULL);
	   xvl_destroy_glyphlist(scheduled);
	}

	workspace = glyph->workspace;
	if (workspace->autorun == False && workspace->frontlist != NULL)
	{
	   /*
	    *  See if the workspace has a stop autorun button.
	    */
	   stop_button = xvl_find_stop_button(workspace->menuform);
	   if (stop_button != NULL)
	   {
	      XtMapWidget(stop_button);
	      XRaiseWindow(XtDisplay(stop_button), XtWindow(stop_button));
	      xvf_reverse_colors(stop_button);
	   }

	   /*
	    *  want to start the automatic dispatch mechanism.
	    */
	   workspace->autorun = True;
	   xvl_autorun_dispatcher(workspace);
	}
}

GlyphList *demand_scheduler(glyph, scheduled)

Glyph	  *glyph;
GlyphList *scheduled;
{
	Glyph	  *parent;
	Workspace *workspace;
	NodeList  *input, *links;


	/*
	 *  Check to see if this glyph has been previously scheduled.  If
	 *  so then we need not continue, otherwise add it onto the scheduled
	 *  list so that we won't schedule it again.
	 */
	if (xvl_check_if_glyphlist(glyph, scheduled) == True ||
	    xvl_check_if_scheduled(glyph) == True)
	   return(scheduled);
	else
	   scheduled = xvl_add_to_glyphlist(glyph, scheduled);


	/*
	 *  Add the glyph onto the frontier list so that it will be scheduled,
	 *  but also mark the glyph as whether it was demand_scheduled.  If
	 *  so then this will prevent this glyph from scheduling any of it's
	 *  children since we are already scheduling the children necessary for
	 *  the demand scheduling of the original glyph.
	 */
	if (glyph->type != CONTROL && glyph->run_type != SINK)
	   glyph->demand_scheduled = True;

	workspace = glyph->workspace;
	workspace->frontlist = xvl_add_to_glyphlist(glyph,workspace->frontlist);


	if (xvl_check_if_enabled(glyph) == False)
	{
	   input = glyph->input_list;
	   while (input != NULL)
	   {
	      /*
	       *  If the dav on this input connection is false then
	       *  up the parent to see if the parent should be scheduled.
	       */
	      if (input->node->dav == False)
	      {
		 /*
		  *  Go through the input links (parents) scheduling the
		  *  parent if it is not an external link.
		  *
		  *  Note:   we act like an input can have more than one
		  *	     link which is currently not allowed but maybe
		  *	     in the future.
		  */
	         links = input->node->links;
	         while (links != NULL)
	         {
	            parent = links->node->glyph;
		    if (xvl_check_if_external(glyph, parent) == False)
		    {
		       scheduled = demand_scheduler(parent, scheduled);

		       /*
		        *  Schedule this glyph to be since it's needs to be run
		        *  before the child can run.
		        */
		       if (glyph->type != CONTROL  && glyph->run_type != SINK)
		          glyph->demand_scheduled = True;

		       workspace->frontlist = xvl_add_to_glyphlist(parent,
				workspace->frontlist);

		    }
		    links = links->next;
		 }
	      }
	      input = input->next;
	   }
	}
	return(scheduled);
}



/************************************************************
*
* Routine Name:  xvl_dispatch_glyph
*
*      Purpose:  This routine is used to dispatch or execute
*		 a glyph.  The routine is really a wrapper for
*		 calling the different glyph dispatch routines 
*		 such as:
*
*		 "xvl_run_glyph" or "xvl_run_macro".
*
*
*        Input:  glyph -	glyph to run.
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_dispatch_glyph(glyph)

Glyph		*glyph;
{
	int	  status;
	Workspace *attributes, *workspace;


	/*
	 *  Make the glyph modified and disable all it's children.
	 */
	glyph->modified = False;
	xvl_update_modified(glyph);

	/*
	 *  Need to set ech on or off depending on whether the glyph's
	 *  workspace has "echo execution" set to true.  And check to
	 *  see if a local phantomd should be running.
	 */
	attributes = xvl_get_attributes(glyph->workspace);
	if (attributes->remote_execution == True && khoros_phantomd_pid == 0)
	   xvl_start_phantomd();

	/*
	 *  Set whether we are to perform local echo or not
	 */
	XVF_ECHO_CMD = attributes->echo_exec;

	if (glyph->type == GLYPH)
	   status = xvl_run_glyph(glyph);
	else if (glyph->type == PROCEDURE)
	   status = xvl_run_macro(glyph);
	else if (glyph->type == CONTROL)
	   status = xvl_run_control(glyph);
	else if (glyph->type == COMMAND)
	   status = xvl_run_command(glyph);
	else
	{
	   glyph->pid = 0;
	   status = DISPATCH_GLYPH_FAILED;
	}

	/*
	 *  Make sure the glyph is a not control "loop" glyph, which means
	 *  either a count or while loop.
	 */
	if (((strcmp(glyph->label_str, "count_loop") != 0) &&
	     (strcmp(glyph->label_str, "while_loop") != 0) &&
	     (strcmp(glyph->label_str, "merge") != 0)) ||
	    glyph->type != CONTROL)
	{
	    /*
	     *  Add the glyph to the dispatched glyphlist
	     */
	    workspace = glyph->workspace;
	    workspace->dispatched = xvl_add_to_glyphlist(glyph,
				workspace->dispatched);
	}
	return(status);
}



/************************************************************
*
* Routine Name:  xvl_schedule_glyph
*
*      Purpose:  This routine is used to schedule the glyph's
*		 children for autorun.
*
*        Input:  glyph - the glyph for which we will schedule the
*			 children for dispatching.
*
*
*   Written By: Mark Young & Scott Wilson
*
*************************************************************/


xvl_schedule_glyph(glyph)

Glyph *glyph;
{
	NodeList  *output;


	if (glyph == NULL)
	   return;

	/*
	 *  There is two things we now need to do so that autorun
	 *  will dispatch our children.  The first is that we need
	 *  to update the dav (data available) flags for the children.
	 *  The other thing is we need to is add any children to the
	 *  frontier list if all their dav are true and if autorun
	 *  is currently running.
	 */
	output = glyph->output_list;
	while (output != NULL)
	{
	   if (output->node->selected == True)
	   {
	      xvl_schedule_links(glyph, output->node);
	   }
	   output = output->next;
	}
}

xvl_schedule_links(glyph, node)

Glyph	 *glyph;
Node	 *node;
{
	Glyph	  *child;
	NodeList  *links;
	Workspace *workspace = glyph->workspace;

	links = node->links;
	while (links != NULL)
	{
	   child = links->node->glyph;
	   child->modified = True;
	   xvl_update_modified(child);

	   if (workspace->autorun == True && glyph->demand_scheduled == False)
	   {
	      if (!xvl_check_if_external(glyph, child) &&
		  !xvl_check_if_glyphlist(child, workspace->dispatched))
	      {
#ifdef DEBUG
printf("scheduler: scheduling glyph '%s'\n", child->label_str);
#endif
	         workspace->frontlist = xvl_add_to_glyphlist(child,
				workspace->frontlist);
	      }
	   }
	   links = links->next;
	}
	glyph->demand_scheduled = False;
	xvl_update_input_node(node);
}
