static char rcs[] = "@(#)$Id: outline.c,v 1.7 2023/02/23 16:09:04 leavens Exp leavens $";

/* outline -- routines to manage and print counters for an outline.
 *
 * SYNOPSIS: Abstractly, the state of the outline consists of 5 counters,
 * one for each "level".  The first level of outline is the topmost,
 * it is formatted as I, II, III, IV, etc.  The second level of counters are
 * formatted as A, B, C, D, etc.  Similarly, the level 3 counter is
 * formatted as 1, 2, 3,  The level 4 counters are formatted as a, b, c, d,
 * etc.  The level 5 counters are formatted as i, ii, iii, iv, etc.
 * The level 6 counters are formatted as (1), (2), (3), (4), etc.
 * 
 * Whenever the counter for level i is incremented, the counters for all
 * higher-numbered (deeper in the outline) levels are reset, so that next
 * time they will generate their first index.  For example, if level 1
 * is incremented, the next level 2 counter to be formatted will be A.
 */

#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include "print_errors.h"
#include "outline.h"

static char * lower_case_roman(int n)
     /* requires: n > 0, n < 100 */
     /* ensures: result represents n in lower case roman numerals */
     /* note: the result may be modified by subsequent calls */
{
  static char * roman_codes[2][10] =
    { { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" },
	{ "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" }
    };
  static char heading_buffer[sizeof("lxxxviii")];

  strcpy(heading_buffer, roman_codes[1][n / 10]);
  strcat(heading_buffer, roman_codes[0][n % 10]);
  return heading_buffer;
}

static char * lower_case_alpha(int n)
     /* requires: n > 0, n < 26 */
     /* ensures: result is the nth lower case letter (1 => a, ..., 26 => z) */
     /* note: the result may be modified by subsequent calls */
{
  static char heading_buffer[2] = { '?', '\0' };
  heading_buffer[0] = 'a' + n - 1;
  return heading_buffer;
}

static char * numeral(int n)
     /* requires: n > 0, n < 100 */
     /* ensures: result is the numeral that represents n */
     /* note: the result may be modified by subsequent calls */
{
  static char heading_buffer[sizeof("9999")];
  sprintf(heading_buffer, "%d", n);
  return heading_buffer;
}

static char * paren_numeral(int n)
     /* requires: 0 < n and n < 100 */
     /* ensures: result is the numeral that represents n in parentheses */
     /* note: the result may be modified by subsequent calls */
{
  static char heading_buffer[sizeof("(9999)")];
  sprintf(heading_buffer, "(%d)", n);
  return heading_buffer;
}


static char * upcase(char * s)
     /* modifies: the characters in the string s */
     /* effect: change the characters in s to upper case, return s */
{
  char * ptr = s;
  while ( *ptr  != '\0' ) {
    *ptr = toupper(*ptr);
    ptr++;
  }
  return s;
}


/* counters for outline levels, 0 unused */
static int counters[OUTLINE_MAX_LEVEL+1];

void outline_set_top(int num)
     /* requires: 0 < num and num < 99 */
     /* modifies: counters */
     /* effect: set the top level counter to 1 */
{
  int i;

  /* reset counters for lower levels */
  for (i = 2; i <= OUTLINE_MAX_LEVEL; i++) {
    counters[i] = 0;
  }
  
  counters[1] = num;
}


char * outline_format_heading(int level)
     /* requires: 0 < level and level <= OUTLINE_MAX_LEVEL */
     /* modifies: counters[*] */
     /* effect: increments the counter for level, and
	returns a string representing the heading for level */
     /* note: the string may change on subsequent calls */
{
  int i;

  /* reset counters for lower levels */
  for (i = level+1; i <= OUTLINE_MAX_LEVEL; i++) {
    counters[i] = 0;
  }

  switch (level) {
  case 1:
    return upcase(lower_case_roman(++counters[1]));
    break;
  case 2:
    return upcase(lower_case_alpha(++counters[2]));
    break;
  case 3:
    return numeral(++counters[3]);
    break;
  case 4:
    return lower_case_alpha(++counters[4]);
    break;
  case 5:
    return lower_case_roman(++counters[5]);
    break;
  case 6:
    return paren_numeral(++counters[6]);
    break;
  default:
    error("illegal outline level %d", level);
    return("impossible");
    break;
  }
}
