/* routines to manipulate frames */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <ctype.h>
#include <parse.h>
#include "dm.h"
#include "dm_globals.h"

#define MAX_FRAME_STACK 100

// Internal functions
int child_filled(aFrame *frame, aFrameDef *fd, int *node);
char *fill_child(aFrame *frame, aFrameDef *fd, int *node);
char *prompt_for_node( aFrame *frame, aFrameDef *fd, int node);

// method: createFrame
//
// arguments:
//  char *name: (input) name of a frame to create
//
// return: pointer to a created frame
//
// uses no global variables
// this function makes a frame with the specified name (from the definition
// of the name) and returns the only reference to it. must clean up after this
// function.
//
aFrame *createFrame(char *name) {
  aFrame	*new_frame;
  aFrameDef	*fd;
  
  // find frame definition
  if( !(fd = getFrameDef(name)) ) {
    printf("frame: %s not found\n", name);
    exit(-1);
  }
  
  // malloc space for frame
  if( !(new_frame = (aFrame *) calloc(1, sizeof(aFrame)) )){
    fprintf(stderr, "ERROR: can't allocate space for frame\n");
    exit(-1);
  }
  
  /* copy frame name */
  cp_str( &(new_frame->name), name);
  
  /* malloc space for values */
  if( !(new_frame->value = (char **) calloc(fd->n_slot, sizeof(char *)) )){
    fprintf(stderr, "ERROR: can't allocate space for slots\n");
    exit(-1);
  }
  
  /* malloc space for confirmed flags */
  if( !(new_frame->confirmed = (char *) calloc(fd->n_slot, sizeof(char)) )){
    fprintf(stderr, "ERROR: can't allocate space for slots\n");
    exit(-1);
  }
  
  /* malloc space for consistent flags */
  if( !(new_frame->consistent = (char *) calloc(fd->n_slot, sizeof(char)) )){
    fprintf(stderr, "ERROR: can't allocate space for slots\n");
    exit(-1);
  }
  
  /* malloc space for prompt_count */
  if( !(new_frame-> prompt_count =
	(char *) calloc(fd->n_slot, sizeof(char)) )){
    fprintf(stderr, "ERROR: can't allocate space for prompt counts\n");
    exit(-1);
  }
  
  return new_frame;
  
}

// method: activateFrame
//
// arguments:
//  aFrame *frame: (input) reference to frame to activiate
//
// return: none.
//
// uses no global variables
// This function simply does a single call to set the [Active] slot to "yes"
//
void activateFrame(aFrame *frame) {
  setKey(frame, "[Active]", "yes");
}

// method: setKey
//
// arguments:
//  aFrame *frame: (input) reference to frame to activiate
//  char *key: (input) the name of the slot to change the value
//  char *val: (input) the new value of the slot
//
// return: none.
//
// uses no global variables
// This function first finds a Slot Pointer in the frame. Deallocates it if
// it is not null. Reallocates it to be the same size as val and copies it.
//
void setKey(aFrame *frame, char *key, char *val) {
  char **slot_ptr;
  
  // This function is pointer safe here since no
  // deferences occur here
  //
  if( !(slot_ptr=getKeyPtr(frame, key)) )
    return;
  
  // free any existing value
  if (*slot_ptr)
    free(*slot_ptr);
  *slot_ptr= 0;
  
  if (!val)
    return;
  
  if( !(*slot_ptr=(char *)malloc(strlen(val)+1) )){
    fprintf(stderr, "ERROR: can't allocate space for slots\n");
    exit(-1);
  }
  strcpy(*slot_ptr, val);
}


// method: cmpVal
//
// arguments:
//  aFrame *frame: (input) reference to a defined frame
//  char *key: (input) the name of a slot to compare
//  char *val: (input) the value to compare the slot to
//
// return: int; 0  if key value matches val
//              !0 if it doesn't match (most likely 1, or -1).
//
// uses no global variables
// This function first finds a Slot Pointer in the frame. It compares the
// value of "val" to this slot. If the slot doesn't exist, it automatically
// does not matches. If the slot exists but is null, it only matches
// if "val" is also null.
//
int cmpVal(aFrame *frame, char *key, char *val) {
  char **slot_ptr;
  
  // if not a slot in the frame, then it does not match
  if( !(slot_ptr=getKeyPtr(frame, key)) )
    return(1);
  
  // an unallocated slot is the same as a null reference'
  // an unallocated slot matches "val" only if "val == NULL"
  if (!*slot_ptr) {
    if (!val)
      return(0);
    else
      return(1);
  }
  if (!val)
    return(1);
  
  return( strcmp( *slot_ptr, val) );
  
}

// method: cmpVal
//
// arguments:
//  aFrame *frame: (input) reference to a defined frame
//  char *key: (input) the name of a slot to find
//
// return: char* pointer to value string
//
// uses no global variables
// This function first finds a Slot Pointer in the frame. It then returns this
// slot value. If slot not found, returns null.
//
char *getVal(aFrame *frame, char *key) {
  
  char **slot_ptr;
  
  if( !(slot_ptr=getKeyPtr(frame, key)) )
    return( (char *)0 );
  
  /* null slot matches val=0 */
  return( *slot_ptr );
  
}

// method: addFrame
//
// arguments:
//  aFrame **list: (input) Dynamic array of aFrames.Max size of MAX_FRAME_STACK
//  aFrame *frame: (input) slot of the name to add to the end through pointers
//
// return: none.
//
// uses global define of MAX_FRAME_STACK
// This function will add "frame" to the last element of the list given. 
//
void addFrame(aFrame **list, aFrame *frame) {
  int		i = 0;
  aFrame	**f = list;
  
  for(; i < MAX_FRAME_STACK; i++, f++) {
    if( !*f ) break;
  }
  if( i >=  MAX_FRAME_STACK ) {
    printf("Too many frames\n");
    return;
  }
  *f= frame;
}

// method: freeFrame
//
// arguments:
//  aFrame *frame: (input) An allocated frame to deallocate
//
// return: none.
//
// uses no global variables.
// This function will clear all allocations of the frame.
// char **value is a two dim array and it will not clear those values, just
// the pointer to the array.
//
void freeFrame(aFrame *frame) {
  if (frame->name) free(frame->name);
  if (frame->value) free(frame->value);
  if (frame->confirmed) free(frame->confirmed);
  if (frame->consistent) free(frame->consistent);
  if (frame->prompt_count) free(frame->prompt_count);
  free(frame);
}


// method: clear_all_frames
//
// arguments:
//  none.
//
// return: none.
//
// uses global variables (extracts, context, history)
// This function will call "free_list()" on the global variables
// "extracts, context, history"
//
void clear_all_frames() {
  free_list(extracts);
  free_list(context);
  free_list(history);
}


// method: free_list
//
// arguments:
//  aFrame **list: a dynamic array of aFrame* pointers.
//
// return: none.
//
// uses global define of MAX_FRAME_STACK
// this function will go through each frame from and calls "freeFrame()"
// on each frame, then set to NULL. Function frees through at an
// unallocated frame or MAX_FRAME_STACK.
//
void free_list(aFrame **list) {
  int		i = 0;
  aFrame	**f = list;
  
  for(; i < MAX_FRAME_STACK; i++, f++) {
    if( !*f)
      break;
    freeFrame(*f);
    *f = (aFrame *)0;
  }
}

// method: cp_to_hist
//
// arguments:
//  aFrame **from: a dynamic array of aFrame* pointers.
//
// return: none.
//
// uses global define of MAX_FRAME_STACK
// this copies all the elements of "from" to the end of the history array.
// it is circular in the sense that when hist_idx reaches the end, it will
// loop back around to the beginning of the history array.
//
// Note: The frames that are overwritten are not deallocated this is a
// potential memory leak.
//
void cp_to_hist(aFrame **from) {
  int		i = 0;
  aFrame	**f = from;
  
  for(; i < MAX_FRAME_STACK; i++, f++) {
    if (!*f) break;
    if (hist_idx == MAX_FRAME_STACK) hist_idx = 0;
    history[hist_idx++]= *f;
    *f = (aFrame *)0;
  }
}

// method: get_current_frame
//
// arguments:
//  char *name: a name of a frame
//
// return: a reference to an aFrame struct, null if not found
//
// uses global define of MAX_FRAME_STACK
// uses global variables of (focus, prompted, context)
//
// This function looks for the frame that is the same as the name given.
// This function looks in (a) frame in focus, (b) frame in prompt,
// (c) backwards through the context frame stack
//
aFrame *get_current_frame(char *name) {
  int i;

  // Error check
  if (!name) return ( (aFrame *)0 );
  
  // if same as current focus frame
  if( focus && !strcmp(focus->name, name) ) return(focus);
  
  // if same as prompted frame
  if( prompted.frame && !strcmp(prompted.frame->name, name) )
    return(prompted.frame);
  
  // find end of stack
  for(i = 0; i < MAX_FRAME_STACK; i++) {
    if( !context[i])
      break;
  }
  
  if (i == 0)
    return( (aFrame *)0 );
  
  for(i--; i >= 0; i--) {
    //if name matches context aFrame name
    if (!strcmp(name, context[i]->name))
      return( context[i] );
  }
  return( (aFrame *)0 );
}


// method: node_done
//
// arguments:
//  aFrame *frame: (input) Frame to determine if complete
//  aFrameDef *fd: (input) Definition of this frame as per 'task'
//  int *node: (input\output) Tells function which part of this frame to
//                            start on since it is recursive
//
// return: int; 1 if node complete
//              0 otherwise
//
// This function takes a frame and a starting node count. From that node
// while it has no more children it will determine if this frame is
// done enough to be done. (See difference between +\*)
//
int node_done( aFrame *frame, aFrameDef *fd, int *node) {
  int	   or2;
  int      done;
  char	  *key;
  Slot	  *s;
  
  // if this slot doesn't exist, return false
  //
  if( *node >= fd->n_slot ) return(0);
  
  // if value for node exists, return true
  //
  if( *(frame->value + *node) ) return(1);
  
  // if cannot find a key in the definition, return true
  //
  if( !(key = (fd->slot+*node)->key) ) return(1);
  
  // if no children, return false
  // if the node is the last slot or if the first strlen() of letters
  // do not match, then it has no children.
  // scmp(a, b) will call a strnlen(a, b, strlen(a));
  //
  if( (*node == (fd->n_slot-1)) || scmp(key, (fd->slot + *node+1)->key) != 0)
    return(0);
  
  // for each child of node 
  or2 = 0;
  for( (*node)++; *node < fd->n_slot; ) {
    s = fd->slot + *node;
    // if the key does not match the prefix of this slot, break
    //
    if (scmp(key, s->key) != 0)
      break;
    
    // if the key matches, and it is does not have a required symbol (+,*)
    // skip all of this child's slots
    //
    if( !s->required ) {
      skip(frame, fd, node);
      continue;
    }
    else if( s->required == '*' )
      or2 = 1;
    
    // now check to see if this child is done from here down
    done = node_done( frame, fd, node);
    // the + says that "this must be completed to the frame to be complete"
    // the * says that "this is one of the possiblities for this frame to be
    // complete"
    if( (s->required == '+') && !done ) return(0);
    if( (s->required == '*') && done ) return(1);
    
    // This is down here because it only needs to goto next child if skip()
    // is NOT called
    //
    (*node)++;
  }
  
  //short hand for
  // if (or2 == 0) return 1;
  // if (or2 == 1) return 0;
  //
  return (1 - or2);
}

// method: missing_info
//
// arguments:
//  aFrame *frame: (input) Frame to determine if complete
//  aFrameDef *fd: (input) Definition of this frame as per 'task'
//  int *node: (input\output) Tells function which part of this frame to
//                            start on since it is recursive
//
// return: char* prompt for missing info
//               0\null if none
//
// Uses no global variables
// This function will use "node_done()" and if not done, returns the prompt
// associated with one of missing slots of this frame definition.
//
char *missing_info (aFrame *frame, aFrameDef *fd, int *node) {
  char	*key;
  Slot	*s;
  char	*p;
  int		i;
  int		done;
  int		pre_node;
  
  if( *node >= fd->n_slot ) return( (char *)0);
  
  // if node complete, nothing needed
  i= *node;
  done = node_done( frame, fd, &i);
  // If node_done is != 0, then it is done so null char*
  //
  if (done) {
    skip(frame, fd, node);
    return( (char *)0);
  }
  
  // if node not required
  s= fd->slot + *node;
  if (!s->required) {
    // skip all children and return
    //
    skip(frame, fd, node);
    return( (char *)0);
  }
  
  // if prompt for node
  if ((p = (fd->slot + *node)->prompt)) {
    // prompt_for_node expands the prompt out and then returns it
    // Since it returns null on error, we can just return it.
    // Previous code left in for explanation
    //
    return prompt_for_node(frame, fd, *node);
    //if( (p = prompt_for_node(frame, fd, *node)) ) return(p);
    //else return( (char *)0 );
  }
  
  // if no key value for node, goto next node and return null
  if( !(key = (fd->slot+ *node)->key) ) {
    (*node)++;
    return( (char *)0 );
  }
  
  // for each child of node
  for((*node)++ ; *node < fd->n_slot;  ) {
    
    s= fd->slot + *node;
    if( scmp(key, s->key) ) break; // end of kids
    
    // skip node if not required
    if (!s->required) {
      skip(frame, fd, node);
      continue;
    }
    
    // skip node if already filled
    i = *node;
    done = node_done(frame, fd, &i);
    if (done) {
      skip(frame, fd, node);
      continue;
    }
    
    // check prompt for child
    if( (p = (fd->slot + *node)->prompt) ) {
      // Since it returns null on error, we can just return it.
      // Previous code left in for explanation
      //
      return prompt_for_node(frame, fd, *node);
      //if( (p= prompt_for_node(frame, fd, *node)) ) return(p);
      //else return( (char *)0 );
    }
    pre_node = *node;
    // starting at this node, recursive call to this function
    if ((p = missing_info(frame, fd, node)) )
      return(p);
    // if this is still the same as the call before "missing_info"
    // then a skip() function in the recursion didnt occur so go
    // ahead and increase the node
    //
    if (*node == pre_node)
      (*node)++;
  }
  return( (char *)0);
  
}


// method: getKeyPtr
//
// arguments:
//  aFrame *frame: (input) Frame to find this key in
//  char *key: (input) Key to find inside this frame
//
// return: char**- pointer to the char* value of this slot
//                 0\null if none
//
// Uses no global variables
// Looks in the given frame for the slot value matching key
// Right now it uses scmp() so if will match the first 'n' letters
// of the *key value.
// 
char **getKeyPtr(aFrame *frame, char *key) {
  int		i;
  aFrameDef	*fd;
  Slot	        *s;

  // if either are undefined, cannot continue
  //
  if( !frame || !key ) return( (char **)0);
  
  // find frame definition
  if( !(fd = getFrameDef(frame->name)) ) return( (char **)0);

  // For each slot, compare passed in key and the current slot's key
  //
  i = 0;
  s = fd->slot;
  for (; i < fd->n_slot; i++, s++) {
    // if (strlen(key, s->key, strlen(key) matches)
    // Note: This perhaps should be strcmp() ?
    //
    if( !scmp(key, s->key) )
      break;
  }
  
  // if still inside the array of slots, return true
  if( i < fd->n_slot )
    return ( (frame->value)+i );

  // else its a problem
  return( (char **)0 );
  
}

// method: getNodeKey
//
// arguments:
//  aFrame *frame: (input) Frame to find this key in
//  int node: (input)
//
// return: char*- pointer to the char* key of this slot
//                0\null if none
//
// Uses no global variables
// In the given frame, returns the associated key number's value
//
char *getNodeKey(aFrame *frame, int node) {
  aFrameDef	*fd;
  
  if( !frame ) return( (char *)0);
  
  // find frame definition
  if( !(fd = getFrameDef(frame->name)) ) return( (char *)0);
  
  // get key associated with node
  if( node < fd->n_slot ) return ( ((fd->slot)+node)->key );
  return( (char *)0 );
  
}


// method: setPrompted
//
// arguments:
//  aFrame *frame: (input) Frame of prompted text
//  char *key: (input) Key of prompted text
//  char *type: (input) Type of prompted text
//
// return: none
//
// Uses global variable (prompted)
// Sets the global variable (prompted) to be the values of the passed in.
// No error checking is done for nulls, etc.
//
void set_prompted(aFrame *frame, char *key, char *type) {
  prompted.frame = frame;
  prompted.key = key;
  prompted.type = type;
}


// method: getFrame
//
// arguments:
//  aFrame **list: (input) an array of aFrame pointers
//  char *name: (input) name of the frame that you are looking for in the list
//  char *ctl: (input) either "*" or "last" or "next" or dontcare
//  int *fidx: (input\output) index that only matters for input on ctl="next"
//                            always is the output of index frame was found
//
// return: aFrame pointer to the frame in the list; 0\null if none
//
// Uses global definition of MAX_FRAME_STACK
// Given a list of frames and a name of a frame, it will start at "fidx"
// and return the first frame with the same name.
// Notes: possible values for 'ctl' are
// "*" which mean "match any frame",
// "last" which means find the last one matching this name
// "next" which means to start at the current 'fidx' looking for it
//
aFrame *getFrame(aFrame **list, char *name, char *ctl, int *fidx) {
  int		i;
  int           found;
  aFrame	**f;
  
  // Error check all incoming pointers
  //
  if (!list || !fidx || !name || !ctl)  return( (aFrame *)0 );
  
  // if 'ctl' matches "next" then set the fidx to -1 (then changes to 0)
  // so that it starts at the beginning
  // this gets entered on "first", "last", anything BUT "next"
  //
  if( strcmp("next", ctl) != 0)
    *fidx= -1;

  // Either reset or goto the next frame index
  if( *fidx < 0 ) i= 0;
  else i = (*fidx) +1;
  
  found = -1;
  for( f = list+i ; i < MAX_FRAME_STACK; i++, f++) {
    if( !*f) break;
    // if name matches "*", eg, "anything", found it
    // unless it is asking for the "last" one. Then keep going.
    // name[1] is save because it will shortcircuit
    //
    if (name[0] == '*' && name[1] == 0) { //if (!strcmp("*", name)) {
      found = i;
      //changed scmp to strcmp and added "!= 0"
      //
      if( strcmp("last", ctl) != 0)
	break;
    }
    
    //leaving scmp for null protection
    if (scmp((*f)->name, name)) continue;
    
    // found next frame with name!
    found = i;
    //changed scmp to strcmp and added "!= 0"
    //
    if( strcmp("last", ctl) != 0)
      break;
  }
  if( found < 0 ){
    return( (aFrame *)0 );
  }

  // Set fidx as the outgoing index also so that the next search
  // can start from this index. this function is often used in a loop
  //
  *fidx = found;
  
  return( *(list+found) );
}

// method: findFrame
//
// arguments:
//  char *name: (input) name of the frame to find
//  char *key: (input) key to match the value to
//  char *val: (input) value to match
//
// return: aFrame*- pointer to the aFrame in the context array that matches
//                0\null if none
//
// Uses global variable (context)
// looks through the context for the first frame that has the same name
// as passed in and the key of that frame equals to the value passed in
//
aFrame *findFrame(char *name, char *key, char *val) {
  int i;

  // Error check all input
  if (!name || !key || !val)
    return( (aFrame *)0 );

  // go through the context until end of list
  for( i=0 ; i < MAX_FRAME_STACK; i++) {
    if( !context[i]) break;

    // when the current frame match matches the name
    if (strcmp(context[i]->name, name) != 0) continue;
    
    // see if that value is equal to the key in the found frame
    // if so, return the frame from the context list
    //
    if (cmpVal(context[i], key, val) == 0)
      return(context[i]);
  }
  // else null for frame not found
  return( (aFrame *)0 );
}

// method: print_frame
//
// arguments:
//  aFrame *frame: (input) reference to pointer to print out
//
// return: none.
//
// Uses no global variables
// Prints out the input frame's name and values if not null.
//
void print_frame (aFrame *frame) {
  int		i;
  aFrameDef	*fd;
  char	        **val;
  Slot	        *s;
  
  if( !frame ) return;

  // Find the frame definition
  if( !(fd= getFrameDef(frame->name)) ) return;

  // print name of frame
  printf("Frame: %s\n", frame->name);
  // for each slot in frame print the key and current value
  // only print if val has something besides null
  s= fd->slot;
  i = 0;
  val = frame->value;
  for(; i < fd->n_slot; i++, val++, s++) {
    if( !*val ) continue;
    printf("    %s:%s\n", s->key, *val);    
  }
  fflush(stdout);
}


// method: getFrameDef
//
// arguments:
//  char *name: (input) name of frame to find def for
//
// return: aFrameDef pointer to definition struct for frame
//
// Uses global variables (ontology)
// Returns a pointer to the aFrameDef associated with this frame name.
//
aFrameDef *getFrameDef( char *name ) {
  aFrameDef	*fd;
  int		i;
  
  if (!name) return( (aFrameDef *)0 );

  i = 0;
  fd = ontology;
  for(; i < num_forms; i++, fd++) {
    // When the names match, return this frame
    // changed from scmp() to strcmp()
    //
    if (strcmp(name, fd->name) == 0)
      return( fd );
  }
  // Error condition is null frame
  return( (aFrameDef *)0 );
}

// method: skip
//
// arguments:
//  aFrame *frame: (input)
//  aFrameDef *fd: (input)
//  int *node: (input\output)
//
// return: none
//
// Uses no global variables
// Starting an slot index "node", will go through all the children of this
// node by determining whether or not the slot has the same prefix as the
// given one
//
void skip( aFrame *frame, aFrameDef *fd, int *node) {
  char *key;
  Slot *s;
  
  // Error check
  if (!frame || !fd || !node) return;
  
  key = (fd->slot + *node)->key;

  // Check to make sure this key exists
  if (!key) return;

  // While the value of key is the prefix of other slots,
  // continue past them
  for( (*node)++; *node < fd->n_slot; (*node)++ ) {
    s = fd->slot + *node;
    // Note: Do not use strcmp() in this function
    // because this function was one of the main reasons
    // the scmp() was written. It specifically needs the
    // strncmp(a, b, strlen(a)) functionality.
    if( scmp(key, s->key) ) break;
  }
}

// method: frame_done
//
// arguments:
//  aFrame *frame: (input)
//  int *node_idx: (input\output)
//
// return: char* pointer - either null or a prompt for the user to complete
//                         this frame
//
// Uses no global variables
// Look through all the slots of the given frame and return a prompt.
// The prompt is null if missing_info() returns null.
// This prompt only returns the prompt phrase and still needs to be expanded.
//
char *frame_done( aFrame *frame, int *node_idx ) {
  aFrameDef	*fd;
  char	*p;

  // Attempt to find the frame definition
  //
  if( !(fd = getFrameDef(frame->name)) ) return( (char *)0 );

  // for each slot in the frame, find if any have missing info
  // and return that info
  //
  for(*node_idx= 0; *node_idx < fd->n_slot; ) {
    if( (p = missing_info( frame, fd, node_idx)) )
      return(p);
  }
  return( (char *)0 );
}

// method: frame_complete
//
// arguments:
//  aFrame *frame: (input) frame to examine for complete-ness
//
// return: char* pointer returns prompt for missing info if not complete
//
// Uses global variables (focus, prompt, prompt_text)
// Frame must be active and then uses frame_done() to find a prompt phrase.
// Then takes this phrase and
// (a) expands it (b) sets expanded prompt as "prompted" (c) sets in focus
// (d) sets in global prompt_text
//
char *frame_complete( aFrame *frame ) {
  int	node_idx;
  aFrameDef	*fd;
  char *internal_prompt;

  // Error check
  if (!frame) return ( (char*)0);
  
  // Attempt to find the frame definition
  if( !(fd= getFrameDef(frame->name)) ) return( (char*)0);

  // if frame is not active, return null prompt
  if( !cmpVal(frame, "[Active]", "no") ) return( (char*)0);
  
  node_idx= 0;
  // Starting at slot 0, find the prompt phrase
  if( (internal_prompt = frame_done(frame, &node_idx)) ) {
    // expand this prompt phrase
    //
    expand_prompt(frame, internal_prompt, prompt_text);

    // set this frame and prompt to be the prompted frame
    //
    set_prompted( frame, (fd->slot + node_idx)->key, "value");

    // set this frame in focus
    //
    focus = frame;

    // set global prompt to global prompt_text
    // prompt = char*; prompt_text = char[]
    //
    prompt = prompt_text;

    return(prompt);
  }
  return( (char*)0 );
}

// method: frame_confirm
//
// arguments:
//  aFrame *frame: (input) check this frame for confirm-ness
//
// return: char* of a prompt to confirm the frame
//
// Uses global variables (focus, prompt, prompt_text)
// Checks to see if this frame is complete, and then confirmed.
// If not complete, returns prompt for confirmed prompt.
// If 
// 
char *frame_confirm( aFrame *frame ) {
  char  *prmpt;

  // Error check
  if (!frame) return( (char*)0 );
  
  // if complete but not confirmed, then confirm
  if( !(prmpt = getVal(frame,"[confirmed]")) ) {
    if( !(prmpt = prompt_for_key(frame,"[confirmed]", "yes")) ) {
      return( (char*)0 );
    }
    expand_prompt(frame, prmpt, prompt_text);
    focus = frame;
    return(prompt_text);
  }
  // else if confirmed, return
  else if( !scmp("yes", prmpt) ) {
    return( (char*)0 );
  }
  // else if disconfirmed, confirm each required slot
  else {
    if ((prmpt = confirm_values(frame))) {
      expand_prompt(frame, prmpt, prompt_text);
      return(prompt_text);
    }
    
    focus = frame;
    
    // set confirmed slot
    setKey(frame,"[confirmed]", "yes");
  }
  return( (char*)0 );
}

// method: complete_frame
//
// arguments:
//  char *frame_name: (input) 
//
// return: char* of a prompt to either complete or confirm the frame
//         returns 0\null if the frame is complete and confirmed, or
//         if there is a problem. (Strange mix there huh?)
//
// Uses no global variables
// Finds the frame using get_current_frame() and if not found, adds to context.
// Takes this frame and makes sure it is complete and then confirmed. If it is
// not one of these, then it returns a prompt to send to user. 
// 
char *complete_frame( char *frame_name ) {
  aFrame *frame;
  char  *prmpt;

  // Look for current frame pointer
  if( !(frame = get_current_frame(frame_name)) ) {
    // create new frame if no existing one and add to context
    if( !(frame = createFrame(frame_name)) ) {
      fprintf(stderr, "Can't create frame %s\n", frame_name);
      return( (char *)0 );
    }
    addFrame(context, frame);
  }
  
  // Check this frame for completeness. If not complete, return prompt
  //
  if( (prmpt= frame_complete(frame)) ) return(prmpt);
  
  // Check this frame for confirmness. If not confirmed, return prompt
  //
  if( (prmpt= frame_confirm(frame)) ) return(prmpt);
  
  return( (char *)0 );
}

// method: expand_prompt
//
// arguments:
//  aFrame *frame: (input)
//  char *prompt: (input)
//  char *buf: (output)
//
// return: none.
//
// Uses no global variables
// Easy to explain, difficult to follow code that will replace all the
// $([Depart_Loc].[State]) stuff in the prompts to be the value of the slots
//
void expand_prompt(aFrame *frame, char *prompt, char *buf) {
  char *out; // out is the out-buffer pointer
  char *in; // in is the input-buffer pointer
  char *s; // s is the value pointer of the value from 'getVal'
  char name[LINE_LEN];
  int is_more;
    
  // copy template string until substitute char
  out = buf;
  for(in = prompt; *in; in++) {
    switch(*in) {
      // Case $
    case '$':
      if( *(in+1) == '[' ) {
	if( sscanf(in+1, "%s", name) < 1 ) break;
	if( !(s= getVal(frame, name)) ) break;
	sprintf(out, "%s", s);
	out += strlen(out);
	in += strlen(name);
	break;
      }
      else if( *(in+1) == '(' ) {
	while (1) {
	  if( !(s = index(in+1, (int) ']')) ) break;
	  if( !(s = index(in+1, (int) '[')) ) break;
	  if( sscanf(s, "%s", name) < 1 ) break;
	  for(s = name+ strlen(name)-1; *s && (*s != ']'); s--)
	    *s = (char)0;
	  
	  // if no content, skip
	  if( !(s = getVal(frame, name)) ) {
	    if( (s = index(in+1, (int) ')')) ) in  = s;
	    break;
	  }
	  
	  // if it is "*", skip
	  if (strcmp(s, "*") == 0) {
	    if( (s = index(in+1, (int) ')')) ) in = s;
	    break;		      
	  }
	  
	  strcpy(name,s);
	  for( in = in+2; *in && (*in != '['); in++) *out++ = *in;
	  if( !*in ) break;
	  strcpy(out, name);
	  out += strlen(out);
	  for( ; *in && (*in != ' ') && (*in != ')'); in++);
	  for( is_more = 0; *in && (*in != ')'); in++) {
	    
	    // try to search for another [*].[*]
	    if (*(in+1) == '$') {
	      *out++ = ' ';
	      is_more = 1;
	      break;
	    }
	    *out++= *in;
	  }
	  
	  // more [*].[*] to go
	  if (is_more == 1) continue;
	  
	  break;  // from while
	}
	break;
      }
      break;
      //case DEFAULT
    default:
      *out++ = *in;
    }
  }
  *out = 0;
}


// method: is_relevant
//
// arguments:
//  aFrame *frame: (input) reference to frame to look in
//  char *slot: (input) name of slot that should contain this key
//  char *child: (input) the key that should be found inside the slot
//
// return: char* of the key that strstr() matches to the slot->child
//         null if none
//
// Uses no global variables
// Finds the slot that matches input slot. Then finds the child\key
// that is inside this slot. See below for extra cases.
// //check if [Child] is a child of [Parent].[Child]
//
char *is_child( aFrame *frame, char *slot, char *child ) {
  aFrameDef	*fd;
  char	*key;
  int		node;

  // Error security check
  if (!frame || !slot || !child) return( (char *)0 );

  // Find the frame definition
  if( !(fd= getFrameDef(frame->name)) ) return( (char *)0 );
  
  // Go through each slot and look for the slot
  //
  for(node= 0; node < fd->n_slot; node++ ) {
    // if no key for node, error so exit
    if( !(key= (fd->slot+ node)->key) )  return( (char *)0 );
    if( !scmp(slot, key ) ) break;
  }
  // If fell out of end of loop, return error
  if (node >= fd->n_slot) return( (char *)0 );

  // Now go through all the keys of this slot and
  // 1) break if a key is not found
  // 2) if 'slot' matches in 'key' then return null
  // 3) if 'child' is found in 'key' then return the key
  //
  for( ; node < fd->n_slot; node++ ) {
    if( !(key= (fd->slot+ node)->key) )  return( (char *)0 );
    if( scmp(slot, key ) ) break;
    if( strstr(key, child) ) return(key);
  }
  
  return( (char *)0 );
}

// method: is_relevant
//
// arguments:
//  aFrame *frame: (input) frame to look through for this child
//  char *slot: (input) slot name
//  char *child: (input) name of child key
//
// return: char* of the key that scmp() matches to the slot+child, null if none
//
// Uses no global variables
// Takes the slot and takes only the first part of it and appends
// child on the end of it.
// Ex: slot = [One].[Two].[Three] ---- child = [Four].[Five]
// It will create ref_key which is [One].[Four].[Five]
//
char *is_relevant( aFrame *frame, char *slot, char *child ) {
  aFrameDef	*fd;
  char	*key;
  int	node;
  char  ref_key[100];

  // Error security check
  if (!frame || !slot || !child) return( (char *)0 );

  // Find frame definition
  if( !(fd= getFrameDef(frame->name)) ) return( (char *)0 );
  
  // copy the [Parent] of slot to ref_key
  strcpy(ref_key, slot);
  // Note: strtok() turns the first occurence of token to 0,
  // so [Parent].[Another] turns into [Parent0.[Another] and so
  // the next concat will appent it and look "[Parent]." like that
  //
  strtok(ref_key, "]");
  if ((int)strlen(ref_key) + (int)strlen(child) > (int)sizeof(ref_key)) {
    printf("buffer is not big enough in is_relevant()! exiting program!\n");
    exit(-1);
  }
  
  // concate [Child] to form a full key
  strcat(ref_key, "].");
  strcat(ref_key, child);
  printf("ref_key = %s\n", ref_key);
  
  // now look through all slots and use scmp() to compare
  // using strncmp(a,b,strlen(a));
  //
  for(node= 0; node < fd->n_slot; node++ ) {
    // if this key is undefined, break and return null
    if( !(key= (fd->slot+ node)->key) )  
      return( (char *)0 );
    if( !scmp(ref_key, key ) ) 
      return(key);
  }
  
  // didn't find the ref_key
  return( (char *)0 );
}


// method: prompt_for_node
//
// arguments:
//  aFrame *frame: (input) check this frame for confirmation
//
// return: char* of the prompt for this confirmation. null if none to confirm
//
// Uses no global variables
// Goes through all slots in this frame and if it has a confirmation prompt
// and has not been confirmed, returns the prompt for the confirmation.
//
char *confirm_values( aFrame *frame) {
  aFrameDef	*fd;
  Slot	*s;
  char	*p;
  int	i;
  char	**val;
  
  // Error security check
  if (!frame) return( (char *)0 );

  // Get the frame definition
  if( !(fd= getFrameDef(frame->name)) ) return( (char *)0 );
  
  // for each node in frame
  s = fd->slot;
  i = 0;
  val = frame->value;
  
  for(; i < fd->n_slot; i++, val++, s++) {

    // if no value for node, continue
    if( !*val ) continue;
    
    // if key value is "*", no need of confirmation
    if (*val[0] == '*' && *val[1] == 0) continue;
    
    // if already confirmed, no need for confirmation
    if( (int) *(frame->confirmed + i) != 0 ) continue;
    
    // if no key value for node (which shouldn't happen...)
    if( !s->key ) continue;
    
    // if confirm_prompt exists for this slot, set it to a slot
    // named "confirm" and return the prompt
    if( (p = (fd->slot + i)->conf_prompt) ) {
      set_prompted(frame, s->key, "confirm");
      return( p );
    }
  }
  return( (char *)0 );
}


// method: prompt_for_node
//
// arguments:
//  aFrame *frame: (input) find the prompt in this frame
//  aFrameDef *fd: (input) defintion for this frame
//  int node: (input) node to start on looking for prompt
//
// return: char* of the prompt for this key
//
// Uses no global variables
// If this slot has a prompt, one of four things. Either (1) it also has
// children and some of them have been filled in or (2) it has children and
// none of them have been filled up or (3) it has no children or (4) this
// frame has been prompted at least twice
// In the case of 1 or 1 and 4, attempt to fill in the children. In the case
// of 2 and 4, attempt to fill in the children. In the case of 3, just return
// the prompt of the current slot. The prompt returned has not been expanded.
//
char *prompt_for_node( aFrame *frame, aFrameDef *fd, int node) {
  Slot	*s;
  char	*p; //p is the slot-prompt
  char  *np;
  char  *kv;
  int	i;
  int   cnt;
  
  // if prompt for node and error checking
  s = fd->slot + node;
  if (!s) return ( (char*)0);
  
  if( (p = s->prompt) ) {
    
    /* if some children already filled, fill other children */
    /* if more than second time, fill children */
    i = node;
    cnt = (int) *(frame->prompt_count + node);
    if( child_filled(frame, fd, &i) || (cnt >= 2 )) {
      if( (np = fill_child(frame, fd, &i)) ) {
	kv = getNodeKey(frame, i);
	set_prompted(frame, kv, "value");
	(*(frame->prompt_count + i))++;
	return(np);
      }
    }
    
    /* if less than max count go ahead and prompt */
    if( cnt < MaxPrompt ) {
      kv= getNodeKey(frame, node);
      set_prompted(frame, kv, "value");
      (*(frame->prompt_count + node))++;
      return(p);
    }
  }
  return( (char *)0 );
}

// method: prompt_for_key
//
// arguments:
//  aFrame *frame: (input) find the prompt in this frame
//  char *key: (input) this is the key to find in this frame
//  char *type: (input) a value to set to prompted.type if a prompt is found
//
// return: char* of the prompt for this key
//
// Uses global variables (prompted)
// The the slot we need in this frame. If there is an associated prompt,
// return the un-expanded prompt through callstack of:
//   prompt_for_key() ->  prompt_for_node()
// If a prompt is returned, the global prompted.type is set to 'type'
//
char *prompt_for_key( aFrame *frame, char *key, char *type) {
  aFrameDef	*fd;
  Slot	*s;
  char	*p;
  char  *kv;
  int	i;
  int   node;
  
  // Error check input
  if (!frame || !key || !type) return( (char *)0 );
  
  // Find the aFrameDef structure
  if( !(fd= getFrameDef(frame->name)) ) return( (char *)0 );
  
  // Go through all the slots and compare the key to the slots
  // break when it is found
  i = 0;
  s = fd->slot;
  for(; i < fd->n_slot; i++, s++) {
    
    // if no key value for node
    if (!(kv= s->key) ) continue;
    
    // if slot key matches input key
    if (scmp(key, kv) == 0) break;
  }
  // Check to make sure we broke on purpose, else error out
  if( i >= fd->n_slot) return( (char *)0 );

  // Save current slot number
  node = i;
  
  // if prompt for node exists
  if( (p = s->prompt) ) {
    // Find a prompt from this frame down. This will either look at this slot
    // or one of the children's slots
    if( (p = prompt_for_node(frame, fd, node)) ) {
      prompted.type = type;
      return(p);
    }
  }
  return( (char *)0 );
}

// method: setConfirm
//
// arguments:
//  aFrame *frame: (input) Frame to find this slot and set it as confirmed
//  char *key: (input) Slot name to set confirmed
//  int val: (input) Confirmed value to set
//
// return: None.
//
// Uses no global variables
// In this frame, set's the parallel array of "confirmed" to the value of "val"
//
void setConfirm(aFrame *frame, char *key, int val) {
  int		i;
  aFrameDef	*fd;
  Slot	        *s;

  // Error check input
  if( !frame || !key ) return;
  
  // find frame definition
  if( !(fd= getFrameDef(frame->name)) ) return;

  // For each slot in the frame
  i = 0;
  s = fd->slot;
  for(; i < fd->n_slot; i++, s++) {
    //if key name matches this input name
    if( !scmp(key, s->key) ) break;
  }
  // If exited by flowing over, return null
  if( i >= fd->n_slot ) return;

  // in the frame's "confirmed" parallel array, set this input value
  *( (frame->confirmed)+i )= (char) val;
}

// method: cp_value
//
// arguments:
//  aFrame *to: (input\output) Frame to copy TO
//  aFrame *from: (input) Frame to copy FROM
//
// return: None.
//
// Uses no global variables
// Given two of the same frame (same frame name) this function will copy
// all the values from FROM to TO. If there are some values in TO not present
// in FROM, they will not be erased.
//
void cp_values(aFrame *to, aFrame *from) {
  int		i;
  aFrameDef	*fd;
  char	        **f;
  char          **t;

  // Both must be valid pointers and have the same frame name
  if( !to || !from ) return;
  if( strcmp(from->name, to->name) != 0) return;
  
  // find frame definition
  if( !(fd= getFrameDef(from->name)) ) return;

  // For each value slot, if *f value exists, copy to t
  f = from->value;
  t = to->value;
  for (i=0; i < fd->n_slot; i++, f++, t++ ) {
    if (*f)
      cp_str(t, *f);
  }
  
}

// method: cp_value
//
// arguments:
//  aFrame *to: (input\output) Frame to copy TO
//  aFrame *from: (input) Frame to copy FROM
//
// return: None.
//
// Uses no global variables
// Given two of the ANY-TYPES frame this function will copy all the values
// from FROM to TO for each slot\key name.
// If there are some values in TO not present in FROM, they will not be erased.
//
void cp_key_values(aFrame *to, aFrame *from) {
  int		i;
  aFrameDef	*fd;
  Slot	        *s;
  char	        **f;

  // Error checking
  if( !to || !from ) return;
  
  // Find from frame definition
  if( !(fd= getFrameDef(from->name)) ) return;

  // For each frame in FROM, set the key in TO to be the same value
  //
  f = from->value;
  i = 0;
  s = fd->slot;
  for (; i < fd->n_slot; f++, i++, s++) {
    // Added "!f" because needed to make sure f has a value. It should
    // but its better to have the check than null pointer reference.
    if (!f || !*f) continue;
    setKey(to, s->key, *f);
  }  
}

// method: frame_empty
//
// arguments:
//  aFrame *frame: (input) Frame to check if it is empty
//
// return: int; 1 if empty, 0 if not empty
//
// Uses no global variables
// Goes through the value array and if there is something stored in one,
// it is NOT empty. If no values found, it is empty
//
int frame_empty(aFrame *frame) {
  int		i;
  aFrameDef	*fd;
  char	**f;

  // If the frame doesn't exist, it is empty
  if( !frame ) return(1);
  
  // find frame definition
  if( !(fd= getFrameDef(frame->name)) ) return(1);

  // For each slot, if there is a value, return 0 (== not empty)
  f = frame->value;
  i = 0;
  for( ; i < fd->n_slot; i++, f++ ){
    if( *f ) return(0);
  }

  // If you get here, there were no values so return 1 (== empty)
  return (1);
}


// method: cp_frame
//
// arguments:
//  aFrame *to: (input\output) Frame to copy to
//  aFrame *from: (input) Frame to copy from
//
// return: none.
//
// Uses no global variables
// Copies the name of the From frame to the To frame. Copies all values from
// From to To.
//
void cp_frame(aFrame *to, aFrame *from) {

  // Error check
  if( !from || !to ) return;
  
  cp_str(&(to->name), from->name);
  cp_values(to, from);
}

// method: key_confirmed
//
// arguments:
//  aFrame *frame: (input) Frame to find the key in and determine if confirmed
//  char *key: (input) name of slot to determine if confirmed
//
// return: int. 0 is not-confirmed, non-0 is confirmed
//
// Uses no global variables
// Given a frame and a key, tells whether or not this value is confirmed.
// Does not check whether or not this key is suppose to be confirmed or not.
//
int key_confirmed(aFrame *frame, char *key) {
  int		i;
  aFrameDef	*fd;
  Slot	*s;

  // Error check
  if( !frame || !key ) return(0);
  
  // find frame definition
  if( !(fd= getFrameDef(frame->name)) ) return(0);

  // Go through each slot and find this key of this frame
  //
  i = 0;
  s = fd->slot;
  for(; i < fd->n_slot; i++, s++ ){
    if( !scmp(key, s->key) ) break;
  }
  // If exited loop on purpose, return the value of the parallel
  // confirmed array
  if( i < fd->n_slot ) return ( (int) *((frame->confirmed)+i) );
  
  return(0);
  
}

// method: key_consistent
//
// arguments:
//  aFrame *frame: (input) Frame to find the key in
//  char *key: (input) name of slot to determine if consistent
//
// return: int. 0 is not-consistent, non-0 is consistent
//
// Uses no global variables
// Given a frame and a key, tells whether or not this value is consistent.
// 
//
int key_consistent(aFrame *frame, char *key) {
  int		i;
  aFrameDef	*fd;
  Slot	        *s;

  // Error check
  if( !frame || !key ) return(0);
  
  // find frame definition
  if( !(fd= getFrameDef(frame->name)) ) return(0);

  // Go through each slot and find this key of this frame
  //
  i = 0;
  s = fd->slot;
  for(; i < fd->n_slot; i++, s++ ){
    if( !scmp(key, s->key) ) break;
  }
  // If exited loop on purpose, return the value of the parallel
  // consistent array
  if( i < fd->n_slot ) return ( (int) *((frame->consistent)+i) );
  
  return(0);
  
}

// method: child_filled
//
// arguments:
//  aFrame *frame: (input) look inside this frame
//  aFrameDef *fd: (input) and here is the definition for this frame
//  int *node: (input\ouput) starting at this slot
//
// return: int. 1 if there is a child filled, 0 if no children filled
//
// Uses no global variables
// Given a frame and a start node for children, will return 1
// if it has any children that are node_done()'d or 0 if it has no children
// that are node_done()
// 
int child_filled(aFrame *frame, aFrameDef *fd, int *node) {
  char	*key;
  Slot	*s;
  int		nd;
  int		i;
  int		done;
  
  // Error check the pointers
  if (!frame || !fd || !node) return ( 0 );
  
  // if no key value for node return error
  if( !(key = (fd->slot+ *node)->key) )  return( 0 );
  
  // for each child starting at this node
  for(nd = (*node)+1 ; nd < fd->n_slot;  ) {
    
    s = fd->slot + nd;
    if (scmp(key, s->key) != 0) return(0); // end of kids
    
    // if already filled
    i = nd;
    done = node_done( frame, fd, &i);
    if (done) return(1);
    nd++;
  }
  return(0);
}

// method: fill_child
//
// arguments:
//  aFrame *frame: (input) look inside this frame
//  aFrameDef *fd: (input) and here is the definition for this frame
//  int *node: (input\output) starting at this slot
//
// return: char*, the prompt of a slot that is required
//
// Uses no global variables
// Given a frame and a starting node, go through until it finds a slot
// that is both required and has not been filled yet. Does recursive
// calls to the same function.
// 
char *fill_child(aFrame *frame, aFrameDef *fd, int *node) {
  char	*key;
  char	*p;
  Slot	*s;
  int		i;
  int		done;
  int		pre_node;

  // Error check
  if (!frame || !fd || !node) return( (char *)0 );
  
  // if no key value for node
  if( !(key = (fd->slot+ *node)->key) )  return( (char *)0 );
  
  // for each child of node
  for((*node)++ ; *node < fd->n_slot;  ) {
    
    s= fd->slot + *node;
    if( scmp(key, s->key) ) break; // end of kids
    
    // skip node if not required
    if( !s->required ) {
      skip(frame, fd, node);
      continue;
    }
    
    // skip node if already filled
    i = *node;
    done = node_done( frame, fd, &i);
    if (done) {
      skip(frame, fd, node);
      continue;
    }
    
    // check prompt for child
    // If this slot has a prompt, return it
    // (since it is required and not filled)
    //
    if( (p= (fd->slot + *node)->prompt) ) return( p );
    // Save where we are
    pre_node= *node;
    // Call recursively
    //
    if( (p= fill_child( frame, fd, node)) ) return(p);
    // If the recursive call didn't move up at all, move to next slot
    //
    if( *node == pre_node)
      (*node)++;
  }
  return((char *)0);
}


// method: complete_active_frames
//
// arguments:
//  aFrame **frame_p: (output) pointer to a aFrame in the 'context' array
//
// return: char* with prompt [reply from frame_complete()]
//
// Uses global variable (context, prompt_text)
// Checks all the active frame in the "context" array and if not completed
// returns prompt with the expanded string. If frame_complete() does not
// return a string prompt (eg, it is prompted already) then make sure it is
// confirmed.
//
char *complete_active_frames(aFrame **frame_p) {
  char        *prmpt;
  aFrame      *frame;
  aFrameDef   *fd_frame;
  int         xx;
  
  // Error Security check, otherwise next *-line would null-pointer
  if (!frame_p) return( (char *)0 );

  // Clear out this frame
  *frame_p = (aFrame *)0;
  
  // For each frame in context
  for( frame = getFrame(context, "*", "first", &xx); frame;
       frame = getFrame(context, "*", "next", &xx) ) {

    // Find the definition of this frame
    if( !(fd_frame= getFrameDef(frame->name)) ) return( (char *)0 );
    
    // if not an active frame, continue
    if( !cmpVal(frame, "[Active]", "no") ) continue;
    
    // if prompt generated for missing info, set the frame and return it.
    // it will be expanded already
    if( (prmpt = frame_complete(frame)) ) {
      *frame_p = frame;
      return(prompt_text);
    }   
    
    // if complete but not accepted, set the frame but do not return the prompt
    if( (getKeyPtr(frame, "[accept]")) &&
	cmpVal(frame, "[accept]", "yes") != 0) {
      *frame_p= frame;
      return( (char *)0);
    }
  }
  return( (char *) 0 );
}

// this function allows to copy across different types of frames
//
void xcopy_frame(aFrame *to, aFrame *from) {
  
  int		i;
  aFrameDef	*fd_from;
  aFrameDef     *fd_to;
  char	        **f;
  Slot          *slot;

  // Simple error check
  //
  if( !to || !from ) return;
  
  // find frame definition
  //
  //mwe TODO-not sure if this should be a && or a ||, left as a && for now
  if( !(fd_from = getFrameDef(from->name)) &&
      !(fd_to = getFrameDef(to->name))) 
    return;
  
  // look through all slots in "from" frame
  //
  //initial 'for' values, for read-ablility
  slot = fd_from->slot;
  f= from->value;
  
  for( i = 0 ; i < fd_from->n_slot; i++, f++, slot++) {
    
    // only copy those slots with upper-case slot keys
    //
    //mwe added [0] == '['
    if ( *f && (slot->key[0] == '[') && isupper((int)slot->key[1]) ) {
      setKey(to, slot->key, *f);
    }
  }
}

/* return pointer to first active frame */
aFrame *getActiveFrame() {
  aFrame      *frame;
  int         xx;
  
  /* for each frame in context */
  for( frame= getFrame(context, "*", "first", &xx); frame;
       frame= getFrame(context, "*", "next", &xx) ) {
    
    /* if active, return */
    if (!cmpVal(frame, "[Active]", "yes"))
      return(frame);
    
  }
  return( (aFrame *) 0 );
}

/* find frame in context which is best match to extracted frame */
aFrame *findContext(aFrame *extract) {
  aFrame *frame;
  aFrame *best_frame;
  int xx;
  int count;
  int best_count;
  
  /* if same as current focus frame */
  if (focus && !strcmp(focus->name, extract->name))
    return(focus);
  
  if( prompted.frame && !scmp(prompted.frame->name, extract->name) )
    return(prompted.frame);
  
  best_frame= (aFrame *)0;
  best_count= 0;
  
  for(frame= getFrame(context, extract->name, "first", &xx); frame;
      frame= getFrame(context, extract->name, "next", &xx)) {
    
    if( (count= overlap(frame, extract)) >= best_count ) {
      best_frame= frame;
      best_count= count;
    }
  }
  return(best_frame);
}


/* find frame in context which is best match to given frame name */
aFrame *getContext(char *name_a) {
  
  aFrame *frame;
  aFrame *best_frame = (aFrame *)0;
  aFrame *best_perfect_frame = (aFrame *)0;
  int xx;
  
  // These first two checks are in here for speed. They will check
  // the 'in focus' ones and the 'prompted' ones because it is faster
  // that going through all the frames
  //
  /* if same as current focus frame */
  if (focus && !strcmp(focus->name, name_a))
    return(focus);
  
  if (prompted.frame && !scmp(prompted.frame->name, name_a))
    return(prompted.frame);
  
  // TODO
  // Note: This will find and save the last of the found ones since it
  // does not have a "not if already found" line. I am *pretty* sure
  // that you can only have one of each type of Frame in the context though
  // but need to research this more
  //
  // For all frames, if
  for(frame = getFrame(context, name_a, "first", &xx); frame;
      frame = getFrame(context, name_a, "next", &xx)){
    
    // Match perfectly
    if (!strcmp(frame->name, name_a)) {
      best_perfect_frame = frame;
    }
    // Match using scmp
    if (!scmp(frame->name, name_a)) {
      best_frame = frame;
    }
  }
  
  if (best_perfect_frame)
    return best_perfect_frame;
  
  return(best_frame);
}

/* compute context slot overlap between frame */
int overlap(aFrame *f1, aFrame *f2) {
  int         i;
  int         count = 0;
  aFrameDef   *fd;
  Slot        *s;
  char *c;
  
  // If one is null, its 0
  //
  if (!f1 || !f2)
    return 0;
  
  // If their names are NOT the same, it's 0
  //
  if (scmp(f1->name, f2->name) != 0)
    return(0);
  
  // Find frame definition
  // Since they are the same, only need f1's
  // If cannot find, then it's 0
  if( !(fd= getFrameDef(f1->name)) )
    return(0);
  
  for(i=0, s = fd->slot; i < fd->n_slot; i++, s++ ){
    
    // If the key does not have a [, then it is not a slot itself
    // note: index is the same as strchr()
    if( !(c= index(s->key, (int) '[')) )
      continue;
    
    // If the first letter past the [ is not upper, it's not a major
    // parse tag so I dont care about it.
    // example: It could be like [the] or some useless word and not like
    // [Store] or [Location]
    //
    if( !isupper( (int) *(++c)) )
      continue;
    
    // If the slot matches [Active], I dont care about it because they
    // should all have that
    //
    if( !scmp("[Active]", s->key) )
      continue;
    
    // Let me explain 'value' a second here.
    // 'value' is a char**
    // If for some reason it was messed up then it would really
    // easy to get a null pointer down below so these lines are added
    // here to check for values
    
    if( !((f1->value)+i) || !((f2->value)+i) ) continue;
    if( !*((f1->value)+i) || !*((f2->value)+i) ) continue;
    
    if( !scmp(*((f1->value)+i), *((f2->value)+i) ) )
      count++;
  }
  return(count);
}

void print_context(aFrame **context) {
  aFrame	*cf;
  int		xx;
  
  printf("Context frames:\n");
  
  for(cf= getFrame(context, "*", "first", &xx); cf;
      cf= getFrame(context, "*", "next", &xx)) {
    print_frame(cf);
  }
  printf("\n");
  
  fflush(stdout);
}
