/* Time-stamp: <97/01/23 15:35:45 john> */

/*
# Purpose: The main program for outputting a streetplan or other entry.
 */

#include <stdio.h>
#include <ctype.h>
#include "attr.h"
#include "grid.h"
#include "sizes.h"
#include "spread.h"
#include "style.h"
#include "readattr.h"
#include "ascii.h"
#include "cgi.h"
#include "table.h"
#include "misc.h"
#include "save.h"
#include "edit.h"
#include "graphics.h"
#include "vrml.h"
#include "substitution.h"

static void output_as_data(struct entry *entries)
{
  struct entry *entry;

  for (entry = entries;
       entry != NULL;
       entry = entry->next)
    {
      struct attribute *attr;


      for (attr = entry->attributes;
	   attr != NULL;
	   attr = attr->next)
	{
	  char *v;
	  char c;
	  char pc = ' ';
	  int needs_quotes = 0;

	  if (attr->key[0] == '\0')
	    {
	      continue;
	    }

	  if (attr->value[0] == '\0')
	    needs_quotes = 1;

	  for (v = attr->value;
	       (c = *v) != '\0';
	       v++)
	    {
	      switch (c)
		{
		case '\n':
		case '\r':
		  needs_quotes = 1;
		  break;
		default:
		  break;
		}
	    }

	  fputs(attr->key , stdout);
	  putchar('=');
	  if (needs_quotes) putchar('\"');
	  for (v = attr->value;
	       (c = *v) != '\0';
	       v++)
	    {
	      switch (c)
		{
		case'\"':
		  fputs("\\\"", stdout);
		  break;
		case '\r':
		case '\n':
		  if ((pc != '\r') && (pc != '\n'))
		    {
		      putchar(c);
		    }
		  break;
		default:
		  putchar(c);
		  break;
		}
	      pc = c;
	    }
	  if (needs_quotes) putchar('\"');
	  putchar('\n');
	}
      putchar('\n');
    }
}

/* Output a road, which is given as a linked list of entries (as read
   by routines in attr.c, for example), in one of several formats,
   including
   * regurgitating the entry data (for other programs to use)
   * HTML table
   * <pre>formatted</pre> ASCII graphics
   * special formats as determined by the "special" argument to
     getplan (see ../doc/tech/getplan-args.html#special)
   * VRML
   In general, a road is drawn vertically down the page, with
   buildings and side streets along its sides. The road itself is the
   first entry in the linked list, and the rest are put along the
   sides, according to their "direction" and "rank" attributes.
 */

static void output_road(char *attr_file_type,
			char *attr_file_name,
			struct entry *entries,
			struct style *style)
{
  set_initial_sizes(entries);

  if (style->output_code == OUTPUT_PARSED)
    {
      printf("<h2>Parsed attribute data</h2>\n");
      show_entries(entries);
    } else if (style->special != NULL)
      {
	struct entry *this_entry;

	if (style->special_page_start != NULL)
	  {
	    output_with_substitutions(style->special_page_start,
				      entries, entries,
				      attr_file_type, attr_file_name,
				      style, "{}");
	  }

	for (this_entry = entries;
	     this_entry != NULL;
	     this_entry=this_entry->next)
	  {
	    fetch_inclusions(this_entry, style);

	    if (style->special != NULL)
	      {
		output_with_substitutions(style->special,
					  this_entry, entries,
					  attr_file_type, attr_file_name,
					  style, "{}");
	      }
	  }

	if (style->special_page_end != NULL)
	  {
	    output_with_substitutions(style->special_page_end,
				      entries, entries,
				      attr_file_type, attr_file_name,
				      style, "{}");
	  }
      } else {
	struct grid *grid = entries_to_grid(entries);
	if (grid == NULL)
	  {
	    error_header("Failed to make grid");
	    free_entries(entries);
	    error_trailer();
	    exit(0);
	  }

	widen_roads_to_full_width(grid);

	grid->page_type = attr_file_type;
	grid->page_name = attr_file_name;

#if 1
	printf("<hr><blockquote><strong>This system is under development -- the author apologises for rough edges</strong> and would welcome <a href=\"mailto:streetmaster@cb1.com\">suggestions</a>.</blockquote><hr>\n");
#endif

	{
	  struct entry *might_be_dummy;
      
	  for (might_be_dummy = entries;
	       might_be_dummy != NULL;
	       might_be_dummy = might_be_dummy->next)
	    {
	      if (strcmp(might_be_dummy->type, "dummy") == 0)
		{
		  printf("<p><strong>This page contains dummy entries, ready to be filled in with what is really there. <a name=\"zippy\">Being generated randomly</a> (mostly from the <a href=\"http://www.sccs.swarthmore.edu/org/swil/Misc_Works/yow.lines.txt\">zippy quotes file</a>), they will generally have silly names. No offence is intended, even with dummy names that happen to be appropriate insults to those at the place concerned.</strong></p><hr>\n");
		  break;
		}
	    }
	}

	if (style->parts & HEAD_PART)
	  {
	    char *description = get_attr(entries, "description");

	    if (description != NULL)
	      {
		char c;
		printf("\n<h2>Description</h2>\n");
		while ((c = *description++) != '\0')
		  {
		    putchar(c);
		  }
		printf("\n<!-- end of description -->\n\n");
	      }

	    if ((style->all_edit_links) && !(style->hide_edit))
	      {
		int old_parts = style->parts;
		int old_edit = style->edit;

		style->parts = HEAD_PART;
		style->edit = 1;

		link_in_style("getplan.cgi",
			      attr_file_type,
			      attr_file_name,
			      style,
			      "[edit main entry]",
			      NULL);

		style->edit = old_edit;
		style->parts = old_parts;
	      }
	  }

	if (style->helpful)
	  {
	    if (!style->detail)
	      {
		if (style->graphics)
		  {
		    printf("<p>To get more detail on a feature, select the graphic of the feature; if there is no graphic, select the [zoom] link.</p>\n");
		  } else {
		    printf("<p>To get more detail on a feature, select the [zoom] link.</p>\n");
		  }
	      }

	    if (style->pictures)
	      {
		printf("<p>Pictures of individual features link to home pages where known.</p>\n");
	      }

	    if (style->unvetted)
	      {
		if (!style->hide_unvetted)
		  {
		    printf("<p>This page contains contributions which have not yet been checked and merged by the <a href=\"mailto:streetmaster@cb1.com\">streetmaster</a>. If it looks like someone has broken something, try the [omit unvetted changes] link at the bottom of the page.</p>\n");
		  }
	      } else {
		printf("<p>This page does not contain contributions which have not yet been checked and merged by the <a href=\"mailto:streetmaster@cb1.com\">streetmaster</a>.\n");
		if (style->hide_unvetted)
		  {
		    printf("If something you know someone has added recently is not present, you will have to wait for the streetmaster to merge it..</p>\n");
		  } else {
		    printf("If something you know someone has added recently is not present, try the [include unvetted changes] link at the bottom of the page.</p>\n");
		  }
	      }
	  }
	
	if ((style->edit) && (style->parts & HEAD_PART))
	  {
	    make_edit(entries, style, NULL);
	  }

	if (style->parts & BODY_PART)
	  {
	    if (style->output_code == OUTPUT_GRID)
	      {

		printf("<h2>Grid</h2>\n");
		show_grid(grid);
	      } else {
		if (style->debug)
		  {
#if 0
		    printf("<h2>Entry list(before adjustment)</h2>\n");
		    show_entries(entries);
#endif
		    printf("<h2>Debug grid (before adjustment)</h2>\n");
		    printf("<pre>\n");
		    show_grid(grid);
		    printf("</pre>\n");
		  }

		if (style->output_code == OUTPUT_HTML_PRE)
		  {
		    int span = 1;
		    if (
			(!complete_rectangle(grid)) ||
			(!spread_across(grid)) ||
			(!extend_end_roads(grid)) ||
			(!spread_down(grid)) ||
			(!verticalize_texts(grid)) ||
			(!multi_line_texts(grid)) ||
			(!total_column_widths(grid, span)))
		      {
			error_header("A stage of grid processing failed");
			free_grid(grid);
			free_entries(entries);
			error_trailer();
			exit(0);
		      }

		    if (style->debug)
		      {
#if 0
			printf("<h2>Entry list(after adjustment)</h2>\n");
			show_entries(entries);
#endif
			printf("<h2>Debug grid (after adjustment)</h2>\n");
			show_grid(grid);
		      }

		    printf("<h2>Ascii grid</h2>\n<pre>\n");
		    output_grid_as_ascii(grid, span, style);
		    printf("</pre>\n");
		  } else {
		    if (!spread(grid))
		      {
			error_header("Could not spread grid");
			free_grid(grid);
			free_entries(entries);
			error_trailer();
			exit(0);
		      }

		    if (style->debug)
		      {
#if 0
			printf("<h2>Entry list(after spread)</h2>\n");
			show_entries(entries);
#endif
			printf("<h2>Debug grid (after spread)</h2>\n");
			printf("<pre>\n");
			show_grid(grid);
			printf("</pre>\n");
		      }

		    /* do table kinds here */
		    if (style->output_code == OUTPUT_HTML)
		      {
			output_grid_as_table(grid, style);
		      } else if (style->output_code == OUTPUT_VRML)
			{
#if 1
			  printf("new vrml output to go here\n");
#endif
			} else {
			  printf("<p>Unknown output mode: \"%s\"! Use <a href=\"#styles\">styles</a> menu to select a different one, or choose from ",
				 style->output_mode);
		    
			  link_in_style("further-options.cgi",
					attr_file_type, attr_file_name, style,
					"more styles and options than you can shake a stick at",
					NULL, NULL, NULL);
			  printf(".</p>\n");

			}
		  }
	      }
	  }
	free_grid(grid);
      }
}

/* Output an area, in various formats -- currently, only HTML tables 
   works.
   The area is output as a grid.
   See ../doc/authoring/field-types.html#area-layout-data for how the
   areas are described.
 */

static void output_area(char *attr_file_name,
			struct entry *entry,
			struct entry *page_entries,
			struct style *style)
{
  char *area_layout_data;
  struct grid *area_layout_grid;

  if (style->debug)
    {
      printf("<p>Looking for layout data for %s</p>\n",
	     attr_file_name);
    }

  fetch_inclusions(entry, style);

  area_layout_data = get_attr(entry, "area-layout-data");

  if (area_layout_data == NULL)
    {
      printf("<p>Sorry, no area layout data has been made for this area.</p>\n");
      return;
    }

  area_layout_grid = make_area_layout_grid(entry,
					   page_entries,
					   area_layout_data,
					   style->debug);

  if (area_layout_grid == NULL)
    {
      printf("<p>Error in creating area layout grid from area layout data.</p>\n");
      return;
    }

  if (style->output_code == OUTPUT_HTML)
    {
      if (style->debug)
	{
	  printf("<pre>output_area:\n</pre>\n");
	}
      output_grid_as_table(area_layout_grid, style);
    } else {
      if (style->output_code == OUTPUT_HTML_PRE)
	{
	  int i;
	  int span = 1;
	  printf("<pre>\n");
	  printf("%s: %s\n", entry->type, entry->name);
	  for (i = strlen(entry->type); i > 0; i--) putchar('-');
	  printf("  ");
	  for (i = strlen(entry->name); i > 0; i--) putchar('=');
	  printf("\n\n");

#if 0
	  if ((verticalize_texts(area_layout_grid)) &&
	      (multi_line_texts(area_layout_grid)) &&
	      (total_column_widths(area_layout_grid, span)))
	    {
	      output_grid_as_ascii(area_layout_grid, span, style);
	    } else  {
	      printf("<p>Something went wrong in processing the grid for output as ASCII graphics</p>\n");
	    }
#else
	  printf("<p>Sorry, there is currently a problem with outputting areas to ASCII graphics. Please <a href=\"mailto:streetmaster@cb1.com\">mail the maintainer</a> if you'd like to be notified when this is fixed.</p>\n");
#endif
	  printf("</pre>\n");
	} else {
	  printf("<p>Unknown output mode: \"%s\"!</p>", style->output_mode);
	}
    }
 
}

/* Output a single entry as a whole page -- either something that is
   neither a road nor an area, from a data file of its own, or a
   zoomed entry from a road.
 */

static void output_non_road(char *attr_file_type,
			    char *attr_file_name,
			    struct entry *entry,
			    struct entry *page_entries,
			    struct style *style,
			    char *zoomed_to)
{
  int old_detail = style->detail;

  style->detail = 1;

  fetch_inclusions(entry, style);

  if (style->output_code == OUTPUT_HTML_PRE)
    {
      int i;
      printf("<pre>\n");
      printf("%s: %s\n", entry->type, entry->name);
      for (i = strlen(entry->type); i > 0; i--) putchar('-');
      printf("  ");
      for (i = strlen(entry->name); i > 0; i--) putchar('=');
      printf("\n\n");
      print_attr_if_present(entry, "Description", "description", "%s%s\n");
      print_attr_if_present(entry, "Opening times", "opening-times", "%s%s\n");
      print_attr_if_present(entry, "Provides", "provides", "%s%s\n");
      printf("</pre>\n");
    } else {
      if (style->output_code == OUTPUT_HTML)
	{
	  if (style->debug)
	    {
	      printf("<pre>output_non_road:\n</pre>\n");
	    }
	  output_entry(attr_file_type,
		       attr_file_name,
		       entry,
		       style,
		       page_entries,
		       NULL,
		       zoomed_to);
	} else {
	  printf("<p>Unknown output mode: \"%s\"!</p>", style->output_mode);
	}
    }

  style->detail = old_detail;
}

static void parse_enumerated_field(char *in,
				   char **possibles,
				   int *pResult)
{
  int index;
  char *this;

  for (index = 0, this=possibles[index];
       this[0] != '\0';
       index++, this=possibles[index])
    {
      int i;
      char tc, ic;

      for (i = 0, tc = this[i], ic=in[i];
	   tc != '\0' && ic != '\0';
	   i++,  tc = this[i], ic=in[i])
	{
	  if (tolower(ic) != tc)
	    {
	      break;
	    }
	}
      if (tc == NULL)
	{
	  *pResult = index+1;
	  return;
	}
    }
}

static void parse_style(char *query_string, struct style *my_style)
{
  char *f;

  f = my_style->output_mode;

  my_style->output_code = OUTPUT_UNKNOWN;

  if (f != NULL)
    {
      parse_enumerated_field(f, output_modes, &(my_style->output_code));
    }
  
  my_style->suppress_auto_graphics = 0;
  
  f = get_field(query_string, "debug");
  my_style->debug = ((f != NULL) && (strcmp(f, "on") == 0));
  
  f = get_field(query_string, "verbose");
  my_style->verbose = ((f != NULL) && (strcmp(f, "on") == 0));

  f = get_field(query_string, "edit");
  my_style->edit = ((f != NULL) && (strcmp(f, "on") == 0));

  f = get_field(query_string, "parts");
  if (f != NULL)
    {
      if (strcmp(f, "head") == 0)
	{
	  my_style->parts = HEAD_PART;
	} else if (strcmp(f, "body") == 0)
	  {
	    my_style->parts = BODY_PART;
	  } else if (strcmp(f, "all"))
	    {
	      my_style->parts = HEAD_PART | BODY_PART;
	    }
    } else {
      my_style->parts = HEAD_PART | BODY_PART;
    }

  f = get_field(query_string, "detail");
  my_style->detail = ((f != NULL) && (strcmp(f, "on") == 0));

  f = get_field(query_string, "pictures");
  if (f != NULL)
    {
      if (strcmp(f, "on") == 0)
	{
	  my_style->pictures = 1;
	}

      if (strcmp(f, "off")==0)
	{
	  my_style->suppress_auto_graphics = 1;
	  my_style->pictures = 0;
	}
    }

  my_style->unvetted = 1;

  f = get_field(query_string, "unvetted");
  if ((f != NULL) && (strcmp(f, "off") == 0)) my_style->unvetted = 0;

  f = get_field(query_string, "graphics");
  if (f != NULL)
    {
      if (strcmp(f, "on") == 0)
	{
	  my_style->graphics = 1;
	}

      if (strcmp(f, "off")==0)
	{
	  my_style->suppress_auto_graphics = 1;
	  my_style->graphics = 0;
	}
    }

  f = get_field(query_string, "hide_unvetted");
  my_style->hide_unvetted = ((f != NULL) && (strcmp(f, "on") == 0));

  f = get_field(query_string, "hide_edit");
  my_style->hide_edit = ((f != NULL) && (strcmp(f, "on") == 0));

  f = get_field(query_string, "all_edit_links");
  my_style->all_edit_links = ((f != NULL) && (strcmp(f, "on") == 0));

  my_style->extra = get_field(query_string, "extra");
  if ((my_style->extra != NULL) && (my_style->extra[0] == '\0')) my_style->extra = NULL;

  my_style->special = get_field(query_string, "special");
  if ((my_style->special != NULL) && (my_style->special[0] == '\0')) my_style->special = NULL;

  my_style->special_page_start = get_field(query_string, "special_page_start");
  if ((my_style->special_page_start != NULL) && (my_style->special_page_start[0] == '\0')) my_style->special_page_start = NULL;

  my_style->special_page_end = get_field(query_string, "special_page_end");
  if ((my_style->special_page_end != NULL) && (my_style->special_page_end[0] == '\0')) my_style->special_page_end = NULL;

  my_style->helpful = 1;
  f = get_field(query_string, "helpful");
  if ((f != NULL) && (strcmp(f, "off") == 0)) my_style->helpful = 0;

  if (my_style->hide_edit)
    {
      my_style->edit = 0;
      my_style->all_edit_links = 0;
    }
  if (my_style->hide_unvetted)
    {
      my_style->unvetted = 0;
    }

  fill_in_current_time(my_style);

  f = get_field(query_string, "month");
  if (f != NULL)
    {
      my_style->forced_date = 1;
      parse_enumerated_field(f, months, &(my_style->month));
    }

  f = get_field(query_string, "day");
  if (f != NULL)
    {
      my_style->forced_date = 1;
      parse_enumerated_field(f, weekdays, &(my_style->day));
    }

  f = get_field(query_string, "hour");
  if ((f != NULL) && (sscanf(f, "%d", &(my_style->hour)) == 1))
    {
      my_style->hour %= 24;
      my_style->forced_date = 1;
    }
}

#define Setup_Minimal_Grid(_gr_,_type_,_name_,_entries_,_entry_) \
{ \
	(_gr_).page_type = (_type_); \
	(_gr_).page_name = (_name_); \
	(_gr_).central_entry = (_entries_); \
	(_gr_).row_list = NULL; \
	(_gr_).n_rows = 0; \
	(_gr_).rows = NULL; \
	(_gr_).row_title_widths = NULL; \
	(_gr_).min_rank = 0; \
	(_gr_).max_rank = 0; \
	(_gr_).entries = (_entry_); \
        (_gr_).phys_xs = NULL; \
        (_gr_).phys_top = 0; \
}

typedef struct browser {
  char name[20];
  int namelen;
  int features;
} Browser;

static struct browser browsers[10] = {
  {"ANT", 3,
     BROWSER_ANTI_ALIAS | 
     BROWSER_GRAPHICAL |
     BROWSER_CELL_COLOURS},
  {"IBM-WebExplorer", 15,
     BROWSER_GRAPHICAL},
  {"Lynx", 4,
     0},
  {"Mozilla", 7,
     BROWSER_HAS_ALIGN |
     BROWSER_JUMP_IN_TABLES |
     BROWSER_GRAPHICAL |
     BROWSER_CELL_COLOURS |
     BROWSER_RECURSIVE_TABLES},
  {"NCSA Mosaic", 11,
     BROWSER_GRAPHICAL},
  {"STBWeb", 6,
     BROWSER_GRAPHICAL |
     BROWSER_CELL_COLOURS},
  {"Unknown", 0,
     0}};

static struct browser no_agent = {"Not set", 0, 0};

static struct browser *special_agent = &no_agent;

static void setup_browser_features(struct style *style)
{
  char *user_agent = (char*)getenv("HTTP_USER_AGENT");
  
  if (user_agent == NULL)
    {
      return;
    }

  for (special_agent = &(browsers[0]);
       special_agent->namelen != 0;
       special_agent++)
    {
      if (strncmp(user_agent, &(special_agent->name), special_agent->namelen) == 0)
	{
	  style->browser_features = special_agent->features;
	  break;
	}
    }
  if (style->browser_features & BROWSER_GRAPHICAL)
    {
      if (style->browser_features & BROWSER_CELL_COLOURS)
	{
	  style->graphics = 1;
	}
      style->pictures = 1;
    }
}

static void output_links(char *type, char *name,
			 struct entry *entries,
			 struct entry *zoomed,
			 struct style *my_style)
{
  int menu_by_lines = 1;

  printf("<hr>\n");

  printf("  <a href=\"http://www.cb1.com/cb1/\"><img align=\"right\" alt=\"CB1 home\" border=\"0\" src=\"../../www/small-CB1-home.gif\"></a>\n");

  if (!(my_style->browser_features & BROWSER_HAS_ALIGN))
    {
      printf("<br>\n");
    }

  printf("<p>The Cambridge WebMap is a joint project of <a href=\"http://www.cb1.com/\">CB1</a> and <a href=\"http://www.cam.net.uk/\">CamNet</a>.</p>\n");

  if (menu_by_lines)
    {
      printf("Navigation:\n");
    }

  if (zoomed != NULL)
    {
      char* label=get_attr(zoomed, "link");
      char bracknamebuf[1024];
      sprintf(bracknamebuf, "[%s]", entries->name);
      printf("  ");
      link_in_style("getplan.cgi",
		    type, name,
		    my_style,
		    bracknamebuf,
		    (label!=NULL) ? label : zoomed->name,
		    NULL, NULL);
      printf("\n");
    } else {
      if (my_style->debug)
	{
	  printf("  [No unzoom needed]\n");
	}
    }

  {
    char *area = get_attr(entries, "area");

    if (area != NULL)
      {
	char c;
	char *area_name = get_attr(entries, "area-name");
	char bracknamebuf[1024];
	
	if (area_name == NULL)
	  {
	    area_name = area;
	  }
	sprintf(bracknamebuf, "[%s area]", area_name);
	printf("  ");
	link_in_style("getplan.cgi",
		      "area", area,
		      my_style,
		      bracknamebuf, NULL,
		      NULL, NULL);
	printf("\n");
      } else {
	if (my_style->debug)
	  {
	    printf("  [Unknown area]\n");
	  }
      }
  }

  {
    char *settlement = get_attr(entries, "settlement");

    if (settlement != NULL)
      {
	char c;
	char *settlement_name = get_attr(entries, "settlement-name");
	char bracknamebuf[1024];
	if (settlement_name == NULL)
	  {
	    settlement_name = settlement;
	  }
	sprintf(bracknamebuf, "[%s]", settlement_name);
	printf("  ");
	link_in_style("getplan.cgi",
		      "settlement",
		      settlement,
		      my_style,
		      bracknamebuf, NULL,
		      NULL, NULL);
	printf("\n");
      } else {
	if (my_style->debug)
	  {
	    printf("  [Unknown settlement]\n");
	  }
      }
  }

  if ((my_style->verbose) ||
      (my_style->parts != (HEAD_PART | BODY_PART)))
    {
      Link_In_Other_Style("getplan.cgi", type, name, my_style, int, parts, HEAD_PART, "[page head only]", NULL, NULL);
      Link_In_Other_Style("getplan.cgi", type, name, my_style, int, parts, BODY_PART, "[page body only]", NULL, NULL);
      if (my_style->parts != (HEAD_PART | BODY_PART))
	{
	  Link_In_Other_Style("getplan.cgi", type, name, my_style, int, parts, HEAD_PART | BODY_PART, "[whole page]", NULL, NULL);
	}
    }

  if ((!my_style->hide_edit) && (!my_style->hide_unvetted))
    {
      printf("  <a href=\"../select.html\">[Civi<i>rtua</i>l Engineering Depot]</a>\n");
    }

  if (menu_by_lines)
    {
      printf("<br>\n<a name=\"styles\">Styles</a>:\n");
    }

  if (my_style->output_code == OUTPUT_HTML_PRE)
    {
      printf("  <a href=\"getplan.cgi?output=html&name=%s&type=%s\">[normal HTML]</a>\n",
	     name, type);
    } else {
      printf("  <a href=\"getplan.cgi?output=html-pre&name=%s&type=%s\">[plain text]</a>\n",
	     name, type);
    }

  printf("  <a href=\"getplan.cgi?output=vrml&name=%s&type=%s\">[VRML]</a>\n",
	 name, type);

  Flip_Style("getplan.cgi", type, name, my_style, graphics, "[graphics]", "[no graphics]");
  if (!my_style->hide_edit)
    {
      Flip_Style("getplan.cgi", type, name, my_style, all_edit_links, "[edit links]", "[no edit links]");
      Flip_Style("getplan.cgi", type, name, my_style, edit, "[edit boxes]", "[no edit boxes]");
    }
  Flip_Style("getplan.cgi", type, name, my_style, detail, "[detail]", "[no detail]");
  Flip_Style("getplan.cgi", type, name, my_style, pictures, "[pictures]", "[no pictures]");
#if 0
  Flip_Style("getplan.cgi", type, name, my_style, verbose, "[verbose]", "[non-verbose]");
  Flip_Style("getplan.cgi", type, name, my_style, helpful, "[with help]", "[without help]");
#endif

  if (menu_by_lines && ((!my_style->hide_unvetted) || (!my_style->hide_edit)))
    {
      printf("<br>\nOptions:\n");
    }

  if (!my_style->hide_unvetted)
    {
      Flip_Style("getplan.cgi", type, name, my_style, unvetted, "[include unvetted changes]", "[omit unvetted changes]");
    }

  if ((!my_style->hide_edit) && (!my_style->hide_unvetted))
    {
      if (my_style->verbose || my_style->debug)
	{
	  printf("  ");
	  Link_In_Other_Style("getplan.cgi", type, name,
			      my_style, int, debug, 0, "[no debug]",
			      NULL, NULL);
	  printf("\n");
	} else {
	  printf("  ");
	  Link_In_Other_Style("getplan.cgi", type, name,
			      my_style, int, debug, 1, "[debug]",
			      NULL, NULL);
	  printf("\n");
	}

      printf("  ");
      link_in_style("further-options.cgi", type, name, my_style,
		    "[Further options and styles]", NULL,
		    NULL, NULL);
      printf("\n");
    }

  if (my_style->verbose || my_style->debug)
    {
      if (menu_by_lines)
	{
	  printf("<br>\nDocumentation and internals:\n");
	}

      printf("  <a href=\"../doc/authoring/index.html\">[Authoring]</a>\n");
      printf("  <a href=\"../doc/tech/index.html\">[Technical]</a>\n");
      printf("  <a href=\"../doc/authoring/join-webmap-list.html\">[Join webmap list]</a>\n");
      printf("  <a href=\"mailto:webmap@cam.net.uk\">[Mail webmap list]</a>\n");
      printf("  <a href=\"index.html\">[Program code]</a>\n");

    }

  if (menu_by_lines)
    {
      printf("<br>\nLinks:\n");
    }

  printf("  <a href=\"../doc/user/help.html\">[Help]</a>\n  ");
  link_in_style("searchform.cgi", type, name, my_style, "[Search]", NULL, NULL, NULL);
  printf("\n  <a href=\"../doc/index.html\">[Documentation]</a>\n");
  printf("  <a href=\"../credits.html\">[Credits]</a>\n");
  printf("  <a href=\"mailto:streetmaster@cb1.com\">[Mail]</a>\n");
  printf("  <a href=\"http://www.cam.net.uk/\">[CamNet]</a>\n");
  printf("  <a href=\"http://www.cb1.com/cb1/\">[CB1]</a>\n");

  printf("<br>\n");
}

int main(argc, argv)
     int argc;
     char * argv [];
{
  struct style my_style;
  char *zoom = NULL;
  char *user = NULL;
  int argi;

  char *name = NULL;
  char *type = NULL;
  char *query_string = (char*)getenv("QUERY_STRING");

#if 0
  printf("Hello from getplan\n");
  exit(0);
#endif
  if (query_string == NULL)
    {
      error_header("No query_string given");
      error_trailer();
    } else {
      char *f;
      name = get_field(query_string, "name");
      type = get_field(query_string, "type");
#if 0
      printf("Query_String: \"%s\", ignoring argv\n", query_string);
#endif
      my_style.output_mode = get_field(query_string, "output");

      if ((name != NULL) && (strlen(name) > 200))
	{
	  error_header("name too long");
	  error_trailer();
	}

      if ((type != NULL) && (strlen(type) > 200))
	{
	  error_header("type too long");
	  error_trailer();
	}

      if (my_style.output_mode == NULL) my_style.output_mode = "html";

      if (name == NULL)
	{
	  name = "cambridge";
	  type = "settlement";
	} else {
	  if (type == NULL)
	    {
	      type = "road";
	    }
	}

#if 0
      html_header("Testing");
#endif

      setup_browser_features(&my_style);

      parse_style(query_string, &my_style);

      zoom = get_field(query_string, "zoom");
      user = get_field(query_string, "user");
    
    }

  if (my_style.output_mode == NULL)
    {
      error_header("No output mode given");
      error_trailer();
      exit(0);
    }

#if 0
  html_header("Testing");
#endif
  
#if 0
  {
    char *remote_host = getenv("REMOTE_HOST");
  }
#endif

  {
    struct entry *entries = NULL;
    struct entry *zoomed = NULL;
    int read_error = READ_OK;

    if (my_style.debug)
      {
	char debug_title_buffer[1024];
	char *debug_title = name;
	if (strlen(name) > 900) debug_title = "Title too long";
	sprintf(debug_title_buffer, "Debugging version of %s", debug_title);
	html_header(debug_title_buffer);
      }

    if (!read_data(type, name,
		   &entries, &read_error,
		   default_check_entry,
		   1,
		   my_style.unvetted,
		   my_style.debug))
      {
	error_header("Data file problem");
	printf("<h2>Problem reading attributes from (\"%s\",\"%s\")</h2>\n",
	       type, name);
	report_read_error(read_error, type, name);
	error_trailer();
	exit(0);
      } else {
	if (my_style.debug)
	  {
	  }
      }

    if (zoom != NULL)
      {
	for (zoomed = entries; zoomed != NULL; zoomed = zoomed->next)
	  {
	    char *old_name;

	    if (strcmp(zoom, zoomed->name) == 0)
	      {
		break;
	      }
	    
	    old_name = get_attr(zoomed, "old-name");

	    if ((old_name != NULL) &&
		(strcmp(zoom, old_name) == 0))
	      {
		break;
	      }
	  }
      }

    if (my_style.output_code == OUTPUT_DATA)
      {
	printf("Content-type: text/plain\n\n");
	output_as_data(entries);	
      } else if (my_style.output_code == OUTPUT_VRML)
	{
	  struct grid *grid = entries_to_grid(entries);
	  if (grid == NULL)
	    {
	      error_header("Failed to make grid");
	      free_entries(entries);
	      error_trailer();
	      exit(0);
	    }

	  vrml_header();

#if 1
	  if (my_style.debug)
	    {
#if 1
	      printf("# Entry list (before spread)\n");
	      show_entries(entries);
#endif
	      printf("# Debug grid (before spread)\n");
	      show_grid(grid);
	    }
#endif
	
	  if ((!complete_rectangle(grid)) ||
	      (!spread_across(grid)) ||
#if 0
	      (!extend_end_roads(grid)) ||
#endif
	      (!spread_down(grid))
#if 0
	      /* not fully working yet! 970110 JCGS */
	      || (!set_physical_coordinates(grid))
#endif
	    
	      )
	    {
	      error_header("Failed to spread grid");
	      free_entries(entries);
	      error_trailer();
	      exit(0);
	    }  
	  
#if 0
	  widen_roads_to_full_width(grid);
#endif

	  grid->page_type = entries->type;
	  grid->page_name = entries->name;

#if 1
	  if (my_style.debug)
	    {
#if 1
	      printf("# Entry list (after spread)\n");
	      show_entries(entries);
#endif
	      printf("# Debug grid (after spread)\n");
	      show_grid(grid);
	    }
#endif
	  grid_to_vrml(grid, &my_style);
	} else {
	  if (zoomed==NULL)
	    {
	      html_header(entries->name);
	      if (zoom != NULL)
		{
		  printf("<p>Could not find entry called %s!</p>\n", zoom);
		}
	    } else {
	      if ((strlen(zoomed->name) < 120) && (strlen(entries->name) < 120))
		{
		  char zoomhead[1024];
		  sprintf(zoomhead, "%s on %s", zoomed->name, entries->name);
		  html_header(zoomhead);
		} else {
		  html_header(zoomed->name);
		}
	    }

	  {
	    char *background_picture = get_attr(entries, "background-picture");
	    char *background_colour = get_attr(entries, "background-colour");
	    if (background_picture == NULL)
	      {
		if (background_colour == NULL)
		  {
		    background_colour="#7f8fff";
		  }
		printf("<body bgcolor=\"%s\">\n", background_colour);
	      } else {
		printf("<body background=\"%s\">\n", background_picture);
	      }
	  }
	  printf("<h1>%s</h1>\n", ((zoomed==NULL) ? entries : zoomed)->name);

	  if (my_style.debug)
	    {
	      printf("<p>Debugging output included for script called as <code>%s</code></p>\n",
		     argv[0]);
	      printf("<p>Browser recognized as \"%s\"</p>\n",
		     special_agent->name);
	      printf("<ul>\n");
	      if (special_agent->features & BROWSER_JUMP_IN_TABLES)
		{
		  printf("<li>can jump into tables\n");
		} else {
		  printf("<li>cannot jump into tables\n");
		}
	      if (special_agent->features & BROWSER_HAS_ALIGN)
		{
		  printf("<li>can flow-round images\n");
		} else {
		  printf("<li>cannot flow-round images\n");
		}
	      if (special_agent->features & BROWSER_ANTI_ALIAS)
		{
		  printf("<li>anti-aliases\n");
		} else {
		  printf("<li>does not anti-alias\n");
		}
	      if (special_agent->features & BROWSER_GRAPHICAL)
		{
		  printf("<li>graphical\n");
		} else {
		  printf("<li>text-only\n");
		}
	      if (special_agent->features & BROWSER_CELL_COLOURS)
		{
		  printf("<li>coloured table cells\n");
		} else {
		  printf("<li>non-coloured table cells\n");
		}
	      printf("</ul>\n");
	      printf("<ul>\n  <li>month=%s\n  <li>day=%s\n  <li>hour=%d\n</ul>\n",
		     months[my_style.month-1],
		     weekdays[my_style.day-1],
		     my_style.hour);
	    } else if (my_style.verbose)
	      {
		printf("<p>Browser recognized as \"%s\"</p>\n",
		       special_agent->name);
	      }

	  if (zoomed == NULL)
	    {
	      if (strcmp(entries->type, "road") == 0)
		{
		  output_road(entries->type, name,
			      entries,
			      &my_style);
		} else if (strcmp(entries->type, "area") == 0)
		  {
		    output_area(entries->name,
				entries, entries,
				&my_style);
		  } else {
		    struct grid griddle;
		    Setup_Minimal_Grid(griddle, type, name, entries, entries);
		    entries->grid = &griddle;

		    output_non_road(entries->type, entries->name,
				    entries, entries,
				    &my_style, NULL);
		  }
	    } else {
	      struct grid griddle;
	      Setup_Minimal_Grid(griddle, type, name, entries, zoomed);
	      zoomed->grid = &griddle;
	      entries->grid = &griddle;

	      output_non_road(entries->type, entries->name,
			      zoomed, entries,
			      &my_style, zoom);
	    }

	  output_links(type, name, entries, zoomed, &my_style);
      

	  if (my_style.verbose || my_style.debug)
	    {
	      print_run_details();
	    }
	  printf("</body></html>\n");
	}
    free_entries(entries);

  }

  exit(0);

}

/* end of getplan.c */
