#include <stdio.h>
#include "hes.h"

/* debugging flags:

DEBUG_PARSER    ---- aids in debugging the parser in load_windows() 



*/

/* help window editing system  ---- HES

   written && designed by UChin Kim  

   prototype of things to come.  

   the basic idea behind this is to allow help documentation to be
   incrementally modified on-line as needed.  if a user finds the current
   help documentation to be inadequate, he can always add his own 
   (provided that he knows how to use the system).  the core of this
   idea is that of extensibility.  use the tools to make more tools.  
   granted, in this case, it is limited to making more documentation.
   
   another idea related to this is that of deferment.  one of the 
   great ideas in computer science.  defer arbitrary decisions as much
   as possible and/or allow the user to choose if possible.  (emacs is
   a great example of this concept --- emacs is very customizable)  
   the only thing this particular system allows deferment of is of
   the contents of the help windows themselves.  

   Possible future refinements include:  allow a tree-like hierarchy 
   of help windows -- so that the user can browse through at his 
   leisure.  not sure how to incorporate this so that it is easy to
   use and intuitive and elegant.  one possibility would be to 
   display the tree structure and allow the user to click on whatever
   topics he wants to see.  good thing about this is that user can
   click on any sublevel of help immediately without having to go 
   through intermediate help levels.  (maybe it's not a tree structure
   but a web-structure ?)  we could have something like a rogue
   maze --- no, not a rogue maze, but a special type of web.  

   a special type of web that is limited to four (or eight?) connections.
   think this out later.  it might be possible to set up a type of
   browser --- a revolutionary type of browser  

   anyways, this is HES.  currently, it will allow a user to do the 
   following: 
   1. display a list of current help windows.  
   2. edit a help window.
   3. save a help window.  (or save all help windows?)
   4. display a help window.


   format of help window file:
   
   help windows can either be edited online or through the use 
   of an editor.  

   EBNF Syntax:
   {} encloses 0 or more
   [] encloses 0 or 1.

   help-file: {help-window}
              EOF-delimiter

   help-window: '|' help-name '|'
                help-text

   EOF-delimiter:  '|EOF|'



   example:

|Filtering|

       filtering is ....
       ...
       ...
       ...


|Tracing|

       tracing involves the assigning of ...
       ...
       ...
       ...


|EOF|



   functions/operations:

   - edit a window.
   - display a window.
   - display a windows list
   - save the windows.
   - load windows.

*/

/*
{
  char name[HELPNAMSIZE];
  char buffer[HELPBUFSIZE];
}; */

struct {
  char name[HELPNAMSIZE];
  char buffer[HELPBUFSIZE];
  } hw[MAXWIND]; /* hw[MAXWIND]; */ /* allow up to MAXWIND help screens */

int num_windows = 0;  /* initially, no windows are loaded */


void init_hw()

/* initialize the contents of the hw[] array. 
   specifically, for each element of the array, set 
   its name to the empty string, and set its buffer 
   to the empty string */
{
  int i;
   
  for (i = 0; i < MAXWIND; i++) {
    hw[i].name[0] = '\0';
    hw[i].buffer[0] = '\0';
  }
}


char *get_keyword(line)
char *line;

/* get_keyword parses a line of the following format:
   '|' keyword '|'
   example:  |hello|    
   |goodbye|  and so on.  the keyword must be 
   separated by '|' (or whatever DELIMITER is set to).  
   get_keyword will read up to the next '|' character.
   no spaces should be between the delimiters.  */
/* seems to work */


{
  static char key[HELPNAMSIZE];
  int index = 0;
  int exit_flag = FALSE;

  key[0] = '\0'; /* initialize char array */
  if (*line == '\0') {
#ifdef DEBUG_PARSER
    fprintf(stderr, "get_keyword(): being passed a null string.\n");
#endif  
    
    
    return(key);
  }

  line++; /* have line point past the first delimiter */
  while ( (*line != '\0') && (*line != DELIMITER) && (exit_flag == FALSE) ) {
    key[index] = *line;
    line++;
    index++;
    if (index >= (HELPNAMSIZE - 1)) exit_flag = TRUE; /* no more space */
  }

  key[index] = '\0'; /* make the string a proper C string */
  return(key);
}
    
int int_scanf()

{
/* int_scanf is an improvement on the regular scanf.  for 
   some reason that i cannot fathom, scanf bombs if you tell
   it to expect an integer input and the user types in a letter
   as a reply.  int_scanf works only for integer input 

   int_scanf reads the input in as a string.  then tries to 
   convert it to an integer.  if this is not possible, it returns
   a default value of 0.  */

  char str[40];
  int num;
  int  scan_flag;

  scan_flag = scanf("%s", str);
  if ( (scan_flag == 0) || (scan_flag == EOF) ) {
    return(0);
  }

  scan_flag = sscanf(str, "%d", &num);
  if (scan_flag == 1) {
    return(num);
  }
  else {
    return(0);
  }
}

  

  


void load_windows(filename)
char *filename;

/* debugged */

/* load_windows loads a set of windows from a file 
   format is as follows:

   (there should be no leading spaces before the keywords)

|Filtering|

   Text follows
   .
   .
   .

|Tracing|
   
   Text follows
   .
   .
   .
   

|EOF|      



   basic strategy is as follows:

   load_windows keeps reading lines until it finds a line
   starting with '|'.  It assumes a keyword is enclosed with 
   '|' on either side (no spaces allowed).  Lines following
   the keyword are assumed to be the help text associated with
   the keyword.  The appearance of the next keyword delimits
   the text of the previous keyword help text.  And so on 
   until the end, when the reserved keyword '|EOF|' tells 
   load_windows to stop reading any more lines.  

*/



{

  FILE *fp;

  char *lineptr, line[LINESIZE]; 

  char current_help[HELPNAMSIZE]; /* name of current help text */
  int current_len = 0; /* length of current help buffer */
  

/* possible states for the parser to be in */

/* #define IN_LIMBO  1
   #define IN_TEXT   2
*/

/* algorithm: starts out IN_LIMBO.  reads in a line at a
   time until it finds a DELIMITER.  if DELIMITER is EOF
   then closes file and returns.  otherwise we are IN_TEXT
   and we set current_help to the name of the keyword, and then
   enter IN_TEXT.  if, while in IN_TEXT, we run out of buffer
   space for the particular keyword, we enter IN_LIMBO again.  
   if we encounter a DELIMITER, but we have no more room for
   keywords, we enter IN_LIMBO (or close up?)  */

  int state = IN_LIMBO;
  int exit_flag = FALSE;
  int defer_read = FALSE; 
  int help_no = -1; /* keeps track of which help message to
		      read in */

  init_hw();  /* need to reset hw[] */


  fp = fopen(filename, "r");
  if (fp == NULL) {
    perror("load_windows(): Can't open the file.");
    return;
  }

  /* process a line at a time 
     using fgets().  

     */

  lineptr = fgets(line, LINESIZE, fp); 

  while ( (lineptr != NULL)  && (exit_flag == FALSE) ) {  /* lev 1 */
    if (state == IN_LIMBO) {  /* lev 2 */
      if (line[0] == DELIMITER) {   /* lev 3 */
	strcpy(current_help,get_keyword(line));
#ifdef DEBUG_PARSER
	fprintf(stderr, "parser: added delimiter %s \n", current_help);
#endif
	if (strncmp(line, EOF_END, strlen(EOF_END)) == 0) 
	  exit_flag = TRUE; 
	else {  /* lev 4 */
	  state = IN_TEXT;
	  current_len = 0; /* reset the current length of the help buffer */
	  help_no = help_no + 1;  /* point to next help buffer */
#ifdef DEBUG_PARSER
	  fprintf(stderr, "new help buffer # is %d \n", help_no);
#endif
	  if (help_no > MAXWIND) {
	    exit_flag = TRUE;
	    help_no = help_no - 1;
	  }
	  else {  /* lev 5 */
	    strcpy(hw[help_no].name, current_help);
#ifdef DEBUG_PARSER
	    fprintf(stderr, "assigned delimiter to buffer %d \n", help_no);
#endif
	  }  /* end lev 5 */
	}  /* end lev 4 */

      }  /* end lev 3 */
      /* otherwise do nothing */
    }   /* end lev 2 */

    else if (state == IN_TEXT) {    /* lev 2 */
      /* checks that the total length of the help text 
	 will not be more than the buffer size for it.
	 if total length is less, then add it, otherwise
	 enter IN_LIMBO */
      if (line[0] == DELIMITER) {   /* lev 3 */
	defer_read = TRUE; /* reprocess this line */
	state = IN_LIMBO;
      }  /* end lev 3 */

      else {    /* lev 3 */
	current_len = current_len + strlen(line);
#ifdef DEBUG_PARSER
	fprintf(stderr, "new text length is %d \n", current_len);
#endif
	if (current_len < HELPBUFSIZE) {  /* lev 4 */
	  strcat(hw[help_no].buffer, line);
	} /* end else */  /* end lev 4 */
	else {  /* we've run out of space for that particular buffer (lev 4)*/
	  defer_read = TRUE; /* reprocess this line, for no good reason */
	  state = IN_LIMBO; 
#ifdef DEBUG_PARSER
	  fprintf(stderr, "just entered limbo.\n");
#endif
	} /* end lev 4 */
      } /* end lev 3 */
    } /* end lev 2 */

      
    /* now get the next line */
    if (defer_read == TRUE)
      defer_read = FALSE;
    else {
      lineptr = fgets(line, LINESIZE, fp);
    }

  } /* end while loop */  /* end lev 1 */

/* by now, we've finished reading in everything we needed to 
   read in.  close the file, set some global variables and
   return */

  fclose(fp);

  num_windows = help_no + 1;

}

int index_window(name)
char *name;

/* index_window finds name in the hw array, and returns the
   indice corresponding to that name. uses a linear search.
   better search is not needed because there are usually 
   less than 20 items to search anyways.  returns -1 if 
   not found */
{
  int i = 0;
  int found = FALSE;
  int index;
  
  while ( (i < num_windows) && (found == FALSE) ) {
#ifdef DEBUG_PARSER
    fprintf(stderr, "index(): comparing <%s> with <%s> \n", name, hw[i].name);
#endif
    if (strcmp(name, hw[i].name) == 0) {
      index = i;
      found = TRUE;
#ifdef DEBUG_PARSER      
      fputs("index(): comparison was true.");
#endif      
    }
    i++;  /* point to next item */
  }

  if (found == TRUE) 
    return(index);
  else return(-1);
}

  

void display_window(name)
char *name;

/* displays the windows in hw[], 
   displays the windows up to num_windows.  */
     
{

  int i;
  
  i = index_window(name);
#ifdef DEBUG_PARSER
  fprintf(stderr,"display_window(): index of window is %d \n", i);
#endif  
  
  
  if (i == -1) {
#ifdef DEBUG_PARSER
    fprintf(stderr, "display_window(): couldn't find your window.\n");
#endif  
    
  }
#ifdef DEBUG_PARSER

  
  fprintf(stderr, "Window : %s \n", hw[i].name);
  fprintf(stderr, "Text: %s", hw[i].buffer);
#endif  
}


void list_window_names()
/* list_window_names lists the names of all the windows it knows */

{
  int i;
#ifdef DEBUG_PARSER

  
  fprintf(stderr, "\n\n");
  fprintf(stderr, "--- List of Windows ---\n");
  for (i = 0; i < num_windows; i++) {
    fprintf(stderr, "%s\n", hw[i].name);
  }
  fprintf(stderr, "\n\n");
#endif    
}


void simple_menu()
/* simple_menu displays a list of choices, lets the user
select from them. and executes the appropriate routines */

{

  int choice;
  int exit_flag = FALSE;
  int scan_flag;

  void handle_list(), handle_edit(), handle_load();
  void handle_display();

  do {
#ifdef DEBUG_PARSER
    printf("\n\n\n");
    printf("--- Le Menu Simplistique ---\n");
    printf("1. list window names. \n");
    printf("2. edit a window. \n");
    printf("3. load a windows file. \n");
    printf("4. display a window screen. \n");
    printf("5. exit this menu.\n");
    printf("\n\nEnter the number corresponding to your choice.\n");
#endif
    choice = int_scanf(); 

    if ( (choice > 0) && (choice < 6) ) {
      switch(choice) {
      case 1: handle_list(); break;
      case 2: handle_edit(); break;
      case 3: handle_load(); break;
      case 4: handle_display(); break;
      case 5: exit_flag = TRUE; break;
      }
    }

  } while (exit_flag == FALSE);
}


void handle_list()
{
  list_window_names();
}

void handle_edit()
{
  printf("editor: Not implemented yet. Tough luck.\n");
}

void handle_load()
{
  char filename[40];
  int scan_flag;

  printf("Enter file name to load: ");
  scan_flag = scanf("%s", filename);
  if (scan_flag != 1) {
    filename[0] = '\0'; 
  }

  load_windows(filename);
  list_window_names();
}

void handle_display()
{

  char name[HELPNAMSIZE];
  int scan_flag;

  printf("Enter name of window to display: ");
  
  scan_flag = scanf("%s", name);
  if (scan_flag != 1) {
    name[0] = '\0';
  }

  display_window(name);

}
