/* Time-stamp: <96/12/23 16:52:02 john> */

/*
# Purpose: Converts a list of entry structures to a grid of cell structures.
 */

/* 
   Things do do:
   direction="across"
   direction="down"
 */

/* #define debug 1 */

#include <stdio.h>
#include "attr.h"
#include "grid.h"

static void free_cells(struct cell *cells)
{
  struct cell *next_cell;

  while (cells != NULL)
    {
      next_cell = cells->next;
      Free(cells);
      cells = next_cell;
    }
}

static void free_rows(struct row *rows)
{
  struct row *next_row;

  while (rows != NULL)
    {
      next_row = rows->next;
      free_cells(rows->cell_list);
      Free(rows);

      rows = next_row;
    }
}

void free_grid(struct grid *grid)
{
  if (grid->row_title_widths != NULL) Free(grid->row_title_widths);
  free_rows(grid->row_list);
  if (grid->rows != NULL) Free(grid->rows);
  Free(grid);
}

static hack_grid_row_list(struct grid *grid)
{
#if 1
  struct row *end_row = grid->row_list;
  if (end_row != NULL)
    {
      struct cell *middle_end_cell = cell_n(end_row, 0);
#ifdef debug
      show_row(end_row, 0);
      printf("Middle end cell is %#X\n", middle_end_cell);
      /* add extra rows at top to lengthen "above" part */
      printf("Middle end cell is %s\n", 
	     middle_end_cell == NULL ? "(null)" : middle_end_cell->cell_text);
#endif
    }

  /* add extra rows at bottom to lengthen "below" part */
#endif
  return 1;
}

static struct cell *make_central_cell(struct grid *grid)
{
  struct cell *new_cell = (struct cell*)malloc(sizeof(struct cell));

  if (new_cell == NULL)
    return 0;
	
  new_cell->entry = grid->central_entry;
  new_cell->entry->grid = grid;
  new_cell->x_into = 0;
  new_cell->y_into = 0;
  new_cell->title_width = strlen(grid->central_entry->name);
  new_cell->cell_text = new_cell->entry->name;
  new_cell->phys_width = 1;
  new_cell->phys_height = 1;

  return new_cell;
}

static int hack_some_raw_grid_things(struct grid* grid)
{
  struct cell *top_cell = cell_yx(grid, 0, 0);

  if (top_cell == NULL)
    {
#ifdef debug
      printf("Funny - no top entry\n");
#endif
      cell_yx_raw(grid, 0, 0) = make_central_cell(grid);
    } else {
      int has_top_entry = 0;
      int probable_main_cell_y;
      char *putative_top_entry_direction = get_attr(top_cell->entry, "direction");
      struct cell *what_was_there;
#ifdef debug
      printf("Main entry is %s, and top cell is %s\n",
	     grid->central_entry->name, top_cell->entry->name);
#endif
      if ((putative_top_entry_direction != NULL) &&
	  (strcmp(putative_top_entry_direction, "ahead") == 0))
	has_top_entry = 1;
      probable_main_cell_y = has_top_entry ? 1 : 0;
#ifdef debug
      printf("has_top_entry=%d so main cell probably at row %d\n",
	     has_top_entry, probable_main_cell_y);
#endif
      what_was_there = cell_yx(grid, probable_main_cell_y, 0);
      if ((what_was_there != NULL) &&
	  (strcmp(what_was_there->entry->type, "junction") == 0) &&
	  ((probable_main_cell_y+1) < grid->n_rows))
	{
	  probable_main_cell_y++;
	  what_was_there = cell_yx(grid, probable_main_cell_y, 0);
	}
      if (what_was_there == NULL)
	{
#ifdef debug
	  printf("It is clear\n");
#endif
	  if (cell_yx_storable(grid, probable_main_cell_y, 0))
	    {
	      cell_yx_raw(grid, probable_main_cell_y, 0) = make_central_cell(grid);
	    }
	} else {
#ifdef debug
	  printf("Occupied by %s of type %s\n",
		 what_was_there->entry->name,
		 what_was_there->entry->type);
#endif
	}
    }
  return 1;
}

/* Make a cell array for a row, from a cell list.
 */

static int fill_in_grid_row(struct grid *grid,
			    struct row *current_row,
			    int row_min_x,
			    int row_max_x,
			    int row_count)
{
  struct cell **row_array;
  struct cell *cell;

  current_row->min_rank = row_min_x;
  current_row->max_rank = row_max_x;

#ifdef debug
  printf("   allocating %d columns in array to cover %d..%d\n",
	 ((row_max_x - row_min_x) + 1),
	 row_min_x, row_max_x);
#endif
  row_array = (struct cell**)malloc(((row_max_x - row_min_x) + 1) * sizeof(struct cell*));

  if (row_array == NULL)
    {
      return 0;
    }

  current_row->cells = row_array;
  current_row->row_number = row_count;
	
  {
    int i;
    for (i=row_min_x; i<=row_max_x; i++)
      {
	cell_n(current_row, i) = NULL;
      }
  }

  cell = current_row->cell_list;

  while (cell != NULL)
    {
#ifdef debug
      printf("     Placing %s in column %d which is array element %d\n",
	     cell->entry->name,
	     cell->entry->column,
	     cell->entry->column - row_min_x);
#endif
      cell_n(current_row, cell->entry->column) = cell;
      cell = cell->next;
    }

  current_row->next = grid->row_list;
  grid->row_list = current_row;

  return 1;
}

/* Return whether a particular column is alread occupied in this row.
   We use this to decide whether to add an entry (specified as being in this
   column) to this row, or to go on to another row.
 */

static int row_column_is_occupied(struct row *row, int column, int width)
{
  struct cell *cell = row->cell_list;
  int my_end = column+width-1;

  while (cell != NULL)
    {
      int cell_start = cell->entry->column;
      int cell_end = cell_start + cell->entry->width -1;
#ifdef debug
      printf("  comparing %d..%d with %s[%d..%d]\n",
	     column, my_end,
	     cell->entry->name,
	     cell_start, cell_end);
#endif

      if (((column >= cell_start) && (column <= cell_end)) ||
	  ((my_end >= cell_start) && (my_end <= cell_end)))
	{
	  return 1;
	}
      cell = cell->next;
    }
  return 0;
}

static struct grid *alloc_empty_grid()
{
  struct grid *grid =  (struct grid*)malloc(sizeof(struct grid));

  if (grid == NULL)
    {
      return NULL;
    }

  grid->entries = NULL;
  grid->central_entry = NULL;
  grid->rows = NULL;
  grid->row_title_widths = NULL;
  grid->phys_xs = NULL;
  grid->phys_top = 0;
  grid->row_list = NULL;
  grid->page_type = "unknown";
  grid->page_name = "unknown";

  return grid;
}

/* Convert a list of rows (each with its number stored in it) into
   an array of rows.
 */

static int make_grid_row_array(struct grid *grid)
{
  struct row *current_row;

  grid->rows = (struct row**)malloc((grid->n_rows+1) * sizeof(struct row*));

  if (grid->rows == NULL)
    {
      return 0;
    }

  {
    int i;

    for (i = 0;
	 i < grid->n_rows;
	 i++)
      {
	grid->rows[i] = NULL;
      }
  }

  for (current_row = grid->row_list;
       current_row != NULL;
       current_row = current_row->next)
    {
      grid->rows[current_row->row_number] = current_row;
      /* may get changed later: */
      current_row->phys_y = current_row->row_number;
    }
  return 1;
}

/* Make a grid (suitable for output_grid_as_table or output_grid_as_ascii)
   from a linked list of entries.
*/

struct grid *entries_to_grid(struct entry *entries)
{
  struct grid *grid;
  struct row *current_row;
  int row_min_x = 0, row_max_x = 0, min_x = 0, max_x = 0;
  int row_count = 0;

  grid = alloc_empty_grid();

  if (grid == NULL)
    {
      return NULL;
    }

  grid->entries = entries;
  grid->central_entry = entries;

  entries = entries->next;

  current_row = (struct row*)malloc(sizeof(struct row));

  if (current_row == NULL)
    {
      Free(grid);
      return NULL;
    }

  current_row->cell_list = NULL;
  current_row->cells = NULL;
  current_row->min_rank = 0;
  current_row->max_rank = 0;
  current_row->phys_y = 0;
  current_row->next = NULL;

#ifdef debug
  printf("<pre>\n");
#endif

  while (entries != NULL)
    {
      char *rower;
      int rank = entries->column;
      struct cell *cell = current_row->cell_list;
#ifdef debug
      printf("\nprocessing %s in columns %d..%d\n", entries->name, rank, rank+entries->width-1);
#endif
      if (rank < row_min_x) row_min_x = rank;
      if ((rank+entries->width-1) > row_max_x) row_max_x = rank+entries->width-1;

      if ((row_column_is_occupied(current_row, rank, entries->width)) ||
	  (((rower=get_attr(entries, "row")) != NULL) &&
	   (strcmp(rower, "next")==0)))
	{
	  /* already occupied -- start new one */
#ifdef debug
	  printf(" %d occupied by %s in %d -- start new row\n",
		 rank, cell->entry->name, row_count);
#endif
	  if (row_min_x > 0) row_min_x = 0;
	  if (row_max_x < 0) row_max_x = 0;
	  if (!fill_in_grid_row(grid, current_row, row_min_x, row_max_x, row_count++))
	    return NULL;

	  current_row = (struct row*)malloc(sizeof(struct row));

	  if (current_row == NULL)
	    {
	      Free(grid);
	      return NULL;
	    }

	  if (row_min_x < min_x) { min_x = row_min_x; }
	  if (row_max_x > max_x) { max_x = row_max_x; }
	  row_min_x = rank; row_max_x = rank+entries->width-1;

	  current_row->cell_list = NULL;
	  current_row->cells = NULL;
	  current_row->min_rank = 0;
	  current_row->max_rank = 0;
	  current_row->phys_y = 0;
	  current_row->next = NULL;
	}
	

      cell = (struct cell*)malloc(sizeof(struct cell));

      if (cell == NULL)
	{
	  Free(grid);
	  return NULL;
	}
#ifdef debug
      printf(" Made cell with entry %s covering %d..%d\n",
	     entries->name,
	     entries->column, entries->column+entries->width-1);
#endif
      cell->entry = entries;
      cell->entry->grid = grid;
      cell->next = current_row->cell_list;
      current_row->cell_list = cell;
      cell->title_width = strlen(entries->name);
      cell->x_into = 0;
      cell->y_into = 0;
      cell->cell_text = entries->name;
      cell->phys_width = get_attr_integer(entries, "depth", 1);
      cell->phys_height = get_attr_integer(entries, "length", 1);

      entries = entries->next;
    }

  if (!fill_in_grid_row(grid, current_row, row_min_x, row_max_x, row_count++))
    return NULL;

  grid->min_rank = min_x;
  grid->max_rank = max_x;
  grid->n_rows = row_count;
  grid->phys_top = row_count + 1;
  grid->row_title_widths = NULL;

  if (!hack_grid_row_list(grid))
    return NULL;

  if (!make_grid_row_array(grid))
    {
      free_grid(grid);
      return NULL;
    }

  if (!hack_some_raw_grid_things(grid))
    {
      free_grid(grid);
      return NULL;
    }

#ifdef debug
  printf("</pre>\n");
#endif

  return grid;
}

/* Make a grid (suitable for output_grid_as_table or output_grid_as_ascii)
   from a string of area layout data, as documented in
   ../doc/authoring/field-types.html#area_layout_data
 */

struct grid *make_area_layout_grid(struct entry *entry,
				   struct entry *entries,
				   char *area_layout_data,
				   int debug_gridding)
{
  int grid_width;
  int grid_height;

  struct grid *grid;

  char *data_remaining = area_layout_data;
  int amount_read = 0;

  if (debug_gridding)
    {
      printf("<h2>Making area layout</h2>\n");
      printf("<h3>Raw layout data:</h3>\n");
      printf("<pre>\n");
      fputs(area_layout_data, stdout);
      printf("</pre>\n");
    }

  if (sscanf(data_remaining, "%d,%d\n%n",
	     &grid_width,
	     &grid_height,
	     &amount_read) != 2)
    {
      if (debug_gridding)
	{
	  printf("<p>Could not get width or height</p>\n");
	}
      return NULL;
    }

  data_remaining += amount_read;

  if (debug_gridding)
    {
#if 0
      printf("Data remaining: \"%s\"\n", data_remaining);
#endif
      printf("<p>Allocating grid (%d,%d)</p>\n", grid_width, grid_height);
    }

  grid = alloc_empty_grid();

  grid->page_type = "area";
  grid->page_name = entry->name;
  grid->central_entry = entries;
  grid->n_rows = grid_height;
  grid->min_rank = 0;
  grid->max_rank = grid_width;

  {
    int i;

    for (i = 0;
	 i <= grid_height;
	 i++)
      {
	struct row *blank_row;
	
	blank_row = (struct row*)malloc(sizeof(struct row));

	if (blank_row == NULL)
	  {
	    free_grid(grid);
	    return NULL;
	  }

	blank_row->next = NULL;
	blank_row->row_number = i;
	blank_row->cell_list = NULL;
	blank_row->cells = NULL;
	blank_row->phys_y = 0;
	
	if (!fill_in_grid_row(grid,
			      blank_row,
			      0,
			      grid_width,
			      i))
	  {
	    free(blank_row);
	    free_grid(grid);
	    return NULL;
	  }
      }
  }

  if (!make_grid_row_array(grid))
    {
      free_grid(grid);
      return NULL;
    }

  {
    int this_x, this_y, this_w, this_h;
    int x, y, xmax, ymax;
    char type[1024];
    char link[1024];
    char name[1024];
    while (sscanf(data_remaining,
		  "%d,%d,%d,%d,%[^,],%[^,],%[^\n]\n%n",
		  &this_x, &this_y, &this_w, &this_h,
		  type, link, name,
		  &amount_read) == 7)
      {
	struct entry *this_entry;
	data_remaining += amount_read;
	if (debug_gridding)
	  {
	    printf("<pre>Read (%s,%s) ``%s'' at (%d,%d), extending (%d,%d)\n",
		   type, link, name, this_x, this_y, this_w, this_h);
#if 0
	    printf("Data remaining: \"%s\"\n", data_remaining);
#endif
	  }

	this_entry =  (struct entry*)malloc(sizeof(struct entry));

	if (this_entry == NULL)
	  {
	    return NULL;
	  }

	this_entry->name = (char*)malloc(strlen(name)+1);
	if (this_entry->name == NULL)
	  {
	    return NULL;
	  }
	strcpy(this_entry->name, name);

	this_entry->type = (char*)malloc(strlen(type)+1);
	if (this_entry->type == NULL)
	  {
	    return NULL;
	  }
	strcpy(this_entry->type, type);

	this_entry->next = NULL;
	this_entry->grid = grid;
	this_entry->attributes = NULL;
	this_entry->inclusions_included = 0;

	this_entry->column = this_x;
	this_entry->width = this_w+1;
	this_entry->height = this_h+1;
	this_entry->max_height = this_h;
	this_entry->left = this_x;
	this_entry->top = this_y;

	set_attr(this_entry, "type", type);
	set_attr(this_entry, "link", link);
	set_attr(this_entry, "name", name);

	ymax = this_y + this_h;
	xmax = this_x + this_w;

	if (ymax > grid_height) ymax = grid_height;
	if (xmax > grid_width) xmax = grid_width;

	for (y = this_y;
	     y <= ymax;
	     y++)
	  {
	    struct row *current_row = grid->rows[y];

	    for (x = this_x;
		 x <= xmax;
		 x++)
	      {
		int cox = x-1;
		int coy = y-1;
		/* We should really do something about preoccupied
		   cells, especially any with their top left corner
		   here, as they aren't getting printed at the moment
		   because something is over them.
		   */
		struct cell *occupier_cell = cell_yx_raw(grid, coy, cox);

		if (occupier_cell == NULL)
		  {
		    struct cell *cell;
		
		    if (debug_gridding)
		      {
			printf("Adding ``%s'' to location (x=%d,y=%d)\n",
			       name, cox, coy);
		      }

		    cell = (struct cell*)malloc(sizeof(struct cell));

		    if (cell == NULL)
		      {
			return NULL;
		      }
#if 0
		    if (debug_gridding)
		      {
			printf(" Made cell with entry %s covering %d..%d\n",
			       this_entry->name,
			       this_entry->column, this_entry->column+this_entry->width-1);
		      }
#endif
		    cell->entry = this_entry;
		    cell->next = current_row->cell_list;
		    current_row->cell_list = cell;
		    cell->title_width = strlen(this_entry->name);
		    cell->x_into = x - this_x;
		    cell->y_into = y - this_y;
		    cell->cell_text = this_entry->name;
#if 1
		    cell->phys_width = 1;
		    cell->phys_height = 1;

#else
		    cell->phys_width = get_attr_integer(entries, "depth", 1);
		    cell->phys_height = get_attr_integer(entries, "length", 1);
#endif
#if 0
		    printf("cell_yx_storable(%d,%d)=%d\n",
			   coy, cox, cell_yx_storable(grid, coy, cox));
#endif
		    if (cell_yx_storable(grid, coy, cox))
		      {
			cell_yx_raw(grid, coy, cox) = cell;
		      }
		  } else {
		    if ((occupier_cell->x_into == 0) &&
			(occupier_cell->y_into == 0) &&
			(x == this_x) &&
			(y == this_y))
		      {
			struct entry *occupier = occupier_cell->entry;
			if (debug_gridding)
			  {
			    printf("Clash! want to put %s down, but %s is already there\n",
				   this_entry->name,
				   occupier->name);
			  }
#if 0
		  
#else
			{
			  int occupier_is_horizontal = occupier->width > occupier->height;
			  int this_is_horizontal = this_entry->width > this_entry->height;

			  if (occupier_is_horizontal)
			    {
			      if (this_is_horizontal)
				{
				  if (debug_gridding)
				    {
				      printf("Clash between two horizontal entries\n");
				    }
				} else {
				  /* move us down */
				  if (debug_gridding)
				    {
				      printf("Moving start of %s down from %d (%d high)\n",
					     this_entry->name,
					     this_entry->top,
					     this_entry->height);
				    }
				  this_entry->top++;
				  this_y++;
				  if (this_entry->height > 1)
				    {
				      this_entry->height--;
				    }
				  if (debug_gridding)
				    {
				      printf("Moved start of %s down to %d (%d high)\n",
					     this_entry->name,
					     this_entry->top,
					     this_entry->height);
				    }
				}
			    } else {
			      if (this_is_horizontal)
				{
				  /* move us across */
				  if (debug_gridding)
				    {
				      printf("Moving start of %s across\n", this_entry->name);
				    }
				  this_entry->left++;
				  this_x++;
				  if (this_entry->width > 1)
				    {
				      this_entry->width--;
				    }
				} else {
				  if (debug_gridding)
				    {
				      printf("Clash between two horizontal entries\n");
				    }
				}
			    }
			}
			/* probably free cell here */
			continue;
#endif
		      } else {
			if (debug_gridding)
			  {
			    printf("Clash not at start of any entry, leaving original there\n");
			  }
		      }
		  }

	      }
	  }
	if (debug_gridding)
	  {
	    printf("</pre>\n");
	  }
	  
      }
  }

  if (debug_gridding)
    {
      printf("<h2>Area Layout Grid</h2>\n");
      printf("<pre>\n");
      show_grid(grid);
      printf("</pre>\n");
    }

  return grid;
}

void show_row(struct row *row, int row_number_for_labelling)
{
  struct cell *cell;
  int i;
  printf("  row %d [%d..%d]\n",
	 row_number_for_labelling, row->min_rank, row->max_rank);

  for (i=row->min_rank; i<=row->max_rank; i++)
    {
      cell = cell_n(row, i);
      if (cell == NULL)
	{
	  printf("    [%d,%d]: --\n", row_number_for_labelling, i);
	} else {
	  if ((cell->x_into > 0) || (cell->y_into > 0))
	    {
	      printf("    [%d,%d]: %s: %s(%d,%d)[%d,%d]\n",
		     row_number_for_labelling, i,
		     cell->entry->type, cell->entry->name,
		     cell->entry->height, cell->entry->width,
		     cell->y_into, cell->x_into);
	    } else {
	      if ((cell->entry->width != 1) ||
		  (cell->entry->height != 1) ||
		  (cell->entry->max_height != 1))
		{
		  printf("    [%d,%d]: %s: %s(%d,%d)\n",
			 row_number_for_labelling, i,
			 cell->entry->type, cell->entry->name,
			 cell->entry->height, cell->entry->width);
		} else {
		  printf("    [%d,%d]: %s: %s\n",
			 row_number_for_labelling, i,
			 cell->entry->type, cell->entry->name);
		}
	    }
	}
    }
}

void show_grid(struct grid *grid)
{
  struct row *row;
  int i;

  printf("Grid of %d rows, cols %d..%d\n",
	 grid->n_rows, grid->min_rank, grid->max_rank);
#if 1
  row = grid->row_list;
  printf("Grid by list of rows:\n");
  while (row != NULL)
    {
      show_row(row, 0);
      row = row->next;
    }
#endif
  printf("Grid by array of rows:\n");
  for (i = 0; i < grid->n_rows; i++)
    {
      show_row(grid->rows[i], i);
    }
  printf("End of grid\n");
}

/* end of make-grid.c */
