#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "robodoc.h"
#include "headers.h"
#include "items.h"
#include "util.h"
#include "folds.h"
#include "links.h"
#include "analyser.h"


/****** ROBODoc/RB_Analyse_Document [3.0i]
 * NAME
 *   RB_Analyse_Document -- scan document for headers and store them
 * SYNOPSIS
 *   RB_Analyse_Document (document)
 *   RB_Analyse_Document (FILE *)
 * FUNCTION
 *   Searches the document for headers. Stores information about
 *   any headers that are found in a linked list. Information
 *   that is stored includes, the name of the header, its version
 *   number, and its contents.
 * INPUTS
 *   document - a pointer to a file with the document to be
 *              analysed
 *   the gobal buffer line_buffer.
 * RESULT
 *   1)   A linked list pointed to by the global variable
 *        first_header that contains information about each
 *        header.
 * NOTES
 *   Using fseek and ftell because gcc doesn't know fgetpos and fsetpos,
 *   on the sun unix system that I use.
 * SEE ALSO
 *   RB_Find_Marker
 * SOURCE
 */

void
RB_Analyse_Document (FILE * document)
{
  int header_type;
  int real_size;
  char *name;

  for (;
       (header_type = RB_Find_Marker (document)) != NO_HEADER;
       )
    {
      struct RB_header *new_header;
      
      if (!
	  (
	   ((header_type == INTERNAL_HEADER) &&
	    !(course_of_action & (DO_INCLUDE_INTERNAL | DO_INTERNAL_ONLY)))
	   ||
	   ((header_type != INTERNAL_HEADER) &&
	    (course_of_action & DO_INTERNAL_ONLY))
	   ||
	   (header_type == BLANK_HEADER)
	   )
	  )
	{
	  long cur_file_pos;
	  
	  new_header = RB_Alloc_Header ();
	  RB_Insert_In_List (&first_header, new_header);
	  new_header->type = header_type;
	  if ((new_header->name = RB_Find_Header_Name ()) != NULL)
	    {
	      RB_Say ("found header [line %5d]: \"%s\"\n",
		      line_number, new_header->name);
#if 0
	      if ((new_header->function_name
		   = RB_Function_Name (new_header->name)) == NULL)
#endif
	      if ((new_header->function_name
		   = RB_Function_Name (line_buffer)) == NULL)
		{
		  RB_Panic ("Can't determine the \"function\" name.\n");
		}
	      cur_file_pos = (long) ftell (document);
	      if ((real_size = RB_Find_End_Marker (document,
						   &new_header->size))
		  != 0)
		{
		  char *contents;
		  
		  fseek (document, cur_file_pos, 0);
		  if ((contents = malloc ((new_header->size +
					   2) * sizeof (char)))
		      != NULL)
		    {
		      fread (contents, new_header->size, sizeof (char), document);
		      
		      contents[real_size] = '\0';
		      new_header->contents = contents;
		      new_header->size = real_size;
		    }
		  else
		    RB_Panic ("out of memory! [Alloc Header Contents]\n");
		}
	      else
		{
		  RB_Panic ("found header with no end marker \"%s\"\n",
			    new_header->name);
		}
	    }
	  else
	    {
	      RB_Panic ("found header marker but no name [line %d]\n",
			line_number);
	    }
	}
      else
	{
	  if (header_type != BLANK_HEADER)
	    {
	      if ((name = RB_Find_Header_Name ()) != NULL)
		{
		  new_header = RB_Alloc_Header ();
		  if ((real_size =
		       RB_Find_End_Marker (document, &new_header->size))
		      == 0)
		    {
		      RB_Free_Header (new_header);
		      RB_Panic ("found header with no end marker \"%s\"\n", name);
		    }
		  else
		    {
		      RB_Free_Header (new_header);
		    }
		}
	      else
		{
		  RB_Panic ("found header marker but no name [line %d]\n",
			    line_number);
		}
	    }
	}
    }
}

/****** END RB_Analyse_Document *******/




/****f* ROBODoc/RB_Function_Name [2.0x]
 * NAME
 *   RB_Function_Name -- get pointer to the function name.
 * SYNOPSIS
 *   char *RB_NamePart(char *header_name)
 * FUNCTION
 *   A header name is consists of two parts. The module name and
 *   the function name. This returns a pointer to the function name.
 *   The name "function name" is a bit obsolete. It is really the name
 *   of any of objects that can be documented; classes, methods,
 *   variables, functions, projects, etc.
 * SOURCE
 */

char *
RB_Function_Name (char *header_name)
{
  char *cur_char;
  char c;
  char *name;

  name = NULL;
  if ((cur_char = header_name) != NULL)
    {
      for (; (c = *cur_char) != '\0'; ++cur_char)
	{
	  if ('/' == *cur_char)
	    {
	      ++cur_char;
	      if (*cur_char)
		name = cur_char;
	    }
	}
    }
  if (name) {
     char *temp;
     temp = malloc((strlen(name) + 1) * sizeof(char));
     strcpy(temp, name);
     return temp; 
  } else {
     return (name);
  }
}

/*** RB_Name_Part ***/



/****** ROBODoc/RB_Find_Marker [3.0h]
 * NAME
 *   RB_Find_Marker -- Search for header marker in document.
 * SYNOPSIS
 *   header_type = RB_Find_Marker (document)
 *             int RB_Find_Marker (FILE *)
 * FUNCTION
 *   Read document file line by line, and search each line for the
 *   any of the headers defined in the array  header_markers
 * INPUTS
 *   document - pointer to the file to be searched.
 *   the gobal buffer line_buffer.
 * RESULT
 *   header type
 *   can be:
 *    (1) NO_HEADER - no header found, end of file reached
 *    (2) MAIN_HEADER
 *    (3) GENERIC_HEADER
 *    (4) INTERNAL_HEADER
 * BUGS
 *   Bad use of feof(), fgets().
 * SEE ALSO
 *   RB_Find_End_Marker
 * SOURCE
 */

int
RB_Find_Marker (FILE * document)
{
  int found;
  int marker, marker_type;
  char *cur_char, *cur_mchar;

  marker_type = NO_HEADER;
  cur_char = NULL;
  found = FALSE;
  while (!feof (document) && !found)
    {
      *line_buffer = '\0';
      fgets (line_buffer, MAX_LINE_LEN, document);
      if (!feof (document))
	{
	  line_number++;
	  for (marker = 0;
	       ((cur_mchar = header_markers[marker]) != NULL) && !found;
	       marker++)
	    {
	      for (found = TRUE, cur_char = line_buffer;
		   *cur_mchar && *cur_char && found;
		   cur_mchar++, cur_char++)
		{
		  if (tolower(*cur_mchar) != tolower(*cur_char))
		    found = FALSE;
		}
	    }
	  if (found)
	    {
	      switch (*cur_char)
		{
		case 'h':
		  marker_type = MAIN_HEADER;
		  break;
		case '*':
		  marker_type = GENERIC_HEADER;
		  break;
		case 'i':
		  marker_type = INTERNAL_HEADER;
		  break;
		case 'f':
		  marker_type = FUNCTION_HEADER;
		  break;
		case 's':
		  marker_type = STRUCT_HEADER;
		  break;
		case 'c':
		  marker_type = CLASS_HEADER;
		  break;
		case 'm':
		  marker_type = METHOD_HEADER;
		  break;
		case 'd':
		  marker_type = CONSTANT_HEADER;
		  break;
		case 'v':
		  marker_type = VARIABLE_HEADER;
		  break;
		default:
		  RB_Say ("%s: WARNING, [line %d] undefined headertype,"
			  " using GENERIC\n", whoami, line_number);
		  marker_type = GENERIC_HEADER;
		}
	    }
	}
    }
  if (!found || feof (document))
    {
      marker_type = NO_HEADER;
    }
  else if (marker_type == GENERIC_HEADER)
    {
      skip_while (*cur_char == '*');
      if (*cur_char == '/')
	{
	  marker_type = BLANK_HEADER;
	}
    }
  return marker_type;
}

/******** END RB_Find_Marker ******/


/****** ROBODoc/RB_Find_End_Marker [3.0h]
 * NAME
 *   RB_Find_End_Marker -- Search for end marker in document.
 * SYNOPSIS
 *   result = RB_Find_End_Marker (document)
 *        int RB_Find_End_Marker (FILE *)
 * FUNCTION
 *   Searches line by line till any of the markers in the
 *   array: end_markers is found.
 * INPUTS
 *   document   - pointer to the file to be searched.
 *   int *total_size - external size
 *   the gobal buffer line_buffer.
 * RESULT
 *                real_size if end marker was found
 *   0          - no end marker was found
 * SEE ALSO
 *   RB_Find_Marker
 * SOURCE
 */

int
RB_Find_End_Marker (FILE * document, int *total_size)
{
  int real_size = 0;
  int found = FALSE;
  int marker;
  int line_len = 0;
  char *cur_char, *cur_mchar;

  while (!feof (document) && !found)
    {
      cur_char = line_buffer;
      *cur_char = '\0';
      fgets (cur_char, MAX_LINE_LEN, document);
      ++line_number;		/* global linecounter *koessi */

      line_len = strlen (cur_char);
      real_size += line_len;

      if (!feof (document))
	{
	  for (marker = 0;
	       ((cur_mchar = end_markers[marker]) != NULL) && !found;
	       marker++)
	    {
	      for (found = TRUE, cur_char = line_buffer;
		   *cur_mchar && *cur_char && found;
		   cur_mchar++, cur_char++)
		{
		  if (tolower(*cur_mchar) != tolower(*cur_char))
		    found = FALSE;
		}
	    }
	}
    }
  if (total_size)
    *total_size = real_size;
  if (found)
    return real_size - line_len;
  else
    return 0;
}

/*****  RB_Find_End_Marker   *****/


/****** ROBODoc/RB_Find_Header_Name   [3.0b]
 * NAME
 *   RB_Find_Header_Name -- search for header name
 * SYNOPSIS
 *   result = RB_Find_Header_Name ()
 *      char *RB_Find_Header_Name ()
 * FUNCTION
 *   Searches the line buffer for the header name.
 *   It assumes that the header name follows after the
 *   header marker, seperated by one or more spaces, and terminated
 *   by one or more spaces or a '\n'.
 *   It allocates an array of chars and copies the name to this array.
 * INPUTS
 *   the gobal buffer line_buffer.
 * RESULT
 *   pointer to the allocated array of chars that contains the name,
 *   terminated with a '\0'.
 *   NULL if no header name was found.
 * MODIFICATION HISTORY
 *   8. August 1995      --  optimized by koessi
 * SEE ALSO
 *   RB_Find_Function_Name(), RB_WordLen(), RB_StrDup()
 * SOURCE
 */

char *
RB_Find_Header_Name (void)
{
  char *cur_char;

  cur_char = line_buffer;
  skip_while (*cur_char != '*');
  skip_while (!isspace (*cur_char));
  skip_while (isspace (*cur_char));
  if (*cur_char)
    {
      char *n;
      int len;

      if (strchr(cur_char, '\n'))
	*strchr(cur_char, '\n') = '\0';

      len = RB_WordLen(cur_char);

      n = calloc(len + 1, sizeof(*cur_char));
      strncpy(n, cur_char, len);
      return n;
    }
  return (NULL);
}

/*****  RB_Find_Header_Name  *****/


/****** ROBODoc/RB_Find_Item [3.0b]
 * NAME
 *   RB_Find_Item -- find item in header contents.
 * SYNOPSIS
 *   item_type = RB_Find_Item (next_line,item_line)
 *
 *           int RB_Find_Item (char **,  char **)
 * FUNCTION
 *   Searches the header contents line by line, looking
 *   for an item Indicator.
 * INPUTS
 *   next_line  - pointer to a pointer that points to line
 *                at which the search will start.
 * SIDE-EFFECTS
 *   next_line  - pointer to a pointer that points to begin of the line
 *                after the line the item was found on.
 *   item_line  - pointer to a pointer that points to the line the item
 *                was found on.
 * RESULT
 *   item_type  - one of possible items indicators.
 * SOURCE
 */

int
RB_Find_Item (char **next_line, char **item_line)
{
  char *cur_char = *next_line;
  int item_type;

  for (item_type = NO_ITEM;
       *cur_char && (item_type == NO_ITEM);
    )
    {
      *item_line = cur_char;
      cur_char = RB_Skip_Remark_Marker (cur_char);

      skip_while (isspace (*cur_char) && *cur_char != '\n');
      if (isupper (*cur_char))
	{
	  char *item_begin = cur_char;
	  char *item_end;

	  skip_while (isupper (*cur_char));
	  item_end = cur_char;
	  if (isspace (*cur_char) && *cur_char)
	    {
	      skip_while (isspace (*cur_char) && *cur_char != '\n');

	      /* Item consists of two words ? */
	      if (isupper (*cur_char) && *cur_char)
		{
		  skip_while (isupper (*cur_char));
		  item_end = cur_char;
		  skip_while (isspace (*cur_char) && *cur_char != '\n');
		}
	      if (*cur_char == '\n')
		{
		  char old_char = *item_end;

		  *item_end = '\0';
		  item_type = RB_Get_Item_Type (item_begin);
		  *item_end = old_char;
		  cur_char++;
		}
	    }
	}
      if (item_type == NO_ITEM)
	{
	  find_eol;
	  if (*cur_char)
	    cur_char++;
	}
    }

  /* advance item_line to end of comment block when we have no more items */
  if (item_type == NO_ITEM)
    {
      *item_line = cur_char;
    }
  *next_line = cur_char;
  return item_type;
}

/*****  RB_Find_Item    *****/


/****** ROBODoc/RRB_Number_Duplicate_Headers 
 * NAME
 *    RB_Number_Duplicate_Headers -- number duplicate headers
 * SYNOPSIS
 *    RB_Number_Duplicate_Headers (void)
 * FUNCTION
 *    Extends the function name with an additional number if there 
 *    are several components with the same name.
 *    Otherwise there will be labels with the same name in HTML
 *    which confuses the browser.
 * SOURCE
 */

void 
RB_Number_Duplicate_Headers (void)
{
  struct RB_header *cur_header;
  struct RB_header *dup_header;
  for (cur_header = first_header; 
       cur_header;
       cur_header = cur_header->next_header)
  {  
    char number[20];
    int  nr = 0;
    for (dup_header = cur_header->next_header; 
         dup_header;
         dup_header = dup_header->next_header)
    {
       if (strcmp(cur_header->function_name,
                  dup_header->function_name) == 0) {
          char *new_name;
          nr++;
          sprintf(number, "(%d)", nr);
          new_name = malloc ((strlen(number) + 1 + 
              strlen(dup_header->function_name) + 1 ) * sizeof(char));
          if (new_name == NULL) 
             RB_Panic ("out of memory! [Number Duplicates]\n");
          sprintf(new_name, "%s%s", dup_header->function_name,
                  number);
          free(dup_header->function_name);
          dup_header->function_name = new_name;
       }
    }
  }
}

/******/


/****** ROBODoc/RB_Make_Index_Tables [3.0b]
 * NAME
 *    RB_Make_Index_Tables
 * SYNOPSIS
 *    void RB_Make_Index_Tables (void)
 * FUNCTION
 *    Creates sorted index tables of headers and links to speed up
 *    matching links later on.
 * INPUTS
 *    none
 * SIDE EFFECTS
 *    Modifies header_index & link_index
 * RESULT
 *    none
 * SOURCE
 */

void
RB_Make_Index_Tables ()
{
  int nr_of_headers, header;
  int nr_of_links, link;
  struct RB_link *cur_link;
  struct RB_header *cur_header;

  for (cur_header = first_header, nr_of_headers = 0;
       cur_header;
       cur_header = cur_header->next_header)
    nr_of_headers++;

  for (cur_link = first_link, nr_of_links = 0;
       cur_link;
       cur_link = cur_link->next_link)
    nr_of_links++;

  if (nr_of_headers)
    {
      int sort1, sort2;

      RB_Say ("Allocating Header Index Table\n");
      header_index = malloc (nr_of_headers * sizeof (struct RB_header **));

      header_index_size = nr_of_headers;
      if (!header_index)
	RB_Panic ("out of memory! [Make Index Tables]\n");

      /* Fill Index Table */
      for (cur_header = first_header, header = 0;
	   cur_header;
	   cur_header = cur_header->next_header, header++)
	header_index[header] = cur_header;

      /* Sort Index Table */
      RB_Say ("Sorting Header Index Table\n");
      for (sort1 = 0; sort1 < nr_of_headers; sort1++)
	{
	  struct RB_header *temp;

	  for (sort2 = sort1; sort2 < nr_of_headers; sort2++)
	    {
	      if (strcmp (header_index[sort1]->function_name,
			  header_index[sort2]->function_name) > 0)
		{
		  temp = header_index[sort1];
		  header_index[sort1] = header_index[sort2];
		  header_index[sort2] = temp;
		}
	    }
	}
    }
  if (nr_of_links)
    {
      int sort1, sort2;

      RB_Say ("Allocating Link Index Table\n");
      link_index = malloc (nr_of_links * sizeof (struct RB_link **));

      link_index_size = nr_of_links;
      if (!link_index)
	RB_Panic ("out of memory! [Make Index Tables]\n");

      /* Fill Index Table */
      for (cur_link = first_link, link = 0;
	   cur_link;
	   cur_link = cur_link->next_link, link++)
	{
	  link_index[link] = cur_link;
	}

      /* Sort Index Table */
      RB_Say ("Sorting Link Index Table\n");
      for (sort1 = 0; sort1 < nr_of_links; sort1++)
	{
	  struct RB_link *temp;

	  for (sort2 = sort1; sort2 < nr_of_links; sort2++)
	    {
	      if (strcmp (link_index[sort1]->label_name,
			  link_index[sort2]->label_name) > 0)
		{
		  temp = link_index[sort1];
		  link_index[sort1] = link_index[sort2];
		  link_index[sort2] = temp;
		}
	    }
	}
    }
}

/****** RB_Make_Index_Tables  *****/
