/* psch.c -- Process SCHedule
   Processes a Cable Television Schedule
   Brian Krause
   June 1, 1989 */

#include <stdio.h>
#define PDLEN 256 /* maximum length of program description */
#define NUMPDS 600 /* maximum number of program descriptions */

struct pd {
  int month;
  int day;
  int channel;
  int dow; /* day of week */
  int sh; /* start hour of program */
  int sm; /* start minute of program */
  char text[PDLEN];} pdl[NUMPDS];

int pm[NUMPDS]; /* partition markers */
int pdsz;    /* size of list */
int cdow;    /* current values of variables */
int cmonth;
int cday;
int cchannel;
int lineno;  /* current line number */

/* procedures to pass to sort and partition routines */
int pdmonth(x)
struct pd x;
{
  return(x.month);
}

int pdday(x)
struct pd x;
{
  return(x.day);
}

int pdchannel(x)
struct pd x;
{
  return(x.channel);
}

int pdsh(x)
struct pd x;
{
  return(x.sh);
}

int pdsm(x)
struct pd x;
{
  return(x.sm);
}

unparse_dow(d)
{
  switch (d)
    {
    case 1: {printf("Sunday"); break;}
    case 2: {printf("Monday"); break;}
    case 3: {printf("Tuesday"); break;}
    case 4: {printf("Wednesday"); break;}
    case 5: {printf("Thursday"); break;}
    case 6: {printf("Friday"); break;}
    case 7: {printf("Saturday"); break;}
    }
}

unparse_month(m)
{
  switch (m)
    {
    case 1: {printf("January"); break;}
    case 2: {printf("February"); break;}
    case 3: {printf("March"); break;}
    case 4: {printf("April"); break;}
    case 5: {printf("May"); break;}
    case 6: {printf("June"); break;}
    case 7: {printf("July"); break;}
    case 8: {printf("August"); break;}
    case 9: {printf("September"); break;}
    case 10: {printf("October"); break;}
    case 11: {printf("November"); break;}
    case 12: {printf("December"); break;}
    }
}

partnsort(l, e, p, b) /* partition array l of size e already sorted by p and sort 
                         partitions by b */
struct pd l[];
int (*p)();
int (*b)();
{
  int v, s, c;
  
  s = 0;
  v = p(l[s]);
  c = s;
  while (c++ < e)
    {
      if ((p(l[s]) != p(l[c])) || pm[c])
	{
	  sort(l, s, c, b);
	  s = c;
	  pm[s] = 1;
	}
    }
  sort(l, s, e, b);
}

sort(l, s, e, pr) /* yeah, so, it's a bubble sort...I'll change it someday... */
struct pd l[];
int (*pr)(); /* pr should be a selector */
{
  int r;
  int chgd;

  chgd = 1;
  while (chgd)
    {
      --e;
      chgd = 0;
      for (r = s; (r < e); ++r)
	{
	  if (pr(l[r]) > pr(l[r + 1]))
	    {
	      struct pd tm;

	      tm = l[r + 1];
	      l[r + 1] = l[r];
	      l[r] = tm;
	      chgd = 1;
	    }
	}
    }
}
  

skip_whitespace()
{
  int c;

  while ((c = gch()) == ' ' || c == '\t' || c == '\n') {}
  ungetc(c, stdin);
}

skip_to_whitespace() /* skip over non-whitespace, then skip over whitespace */
{
  int c;

  while (!((c = gch()) == ' ' || c == '\t' || c== '\n')) {}
  skip_whitespace();
}
  

parse_int() 
{
  int n;
  int c;

  n = gch() - '0';
  if (n < 0 || n > 9) 
    {
      fprintf(stderr, "psch: found garbage where number was expected in line %d\n", lineno);
      return(-1);
    }
  else
    {
      while ((c = gch()) >= '0' && c <= '9')
	{
	  n *= 10;
          n += c - '0';
	}
    }
  ungetc(c, stdin);
  return(n);
}

int gch()
{
  int c;

  if ((c = getchar()) == '\n')
    {
      ++lineno;
    }
/*  fputc(c, stderr); for debugging */
  return(c);
}  

main()
{
  int c;
  
  {
    int r;

    for (r = 0; r < NUMPDS; ++r)
      {
	pm[r] = 0;
      }
  }
  cdow = (cmonth = (cday = (cchannel = (pdsz = 0)))); /* initialize variables */
  lineno = 1;

  skip_whitespace(); /* a good way to start */
  while (c != EOF)
    {
      if ((c = gch()) == 'C')
	{
	  skip_to_whitespace();
	  cchannel = parse_int();
	}
      else if (c == 'F' || c == 'M' || c == 'S' || c == 'T' || c == 'W')
	{
	  int c2; /* for more lookahead */
	  
	  if (c == 'F') {cdow = 6; skip_to_whitespace();}
	  else if (c == 'M') {cdow = 2; skip_to_whitespace();}
	  else if (c == 'W') {cdow = 4; skip_to_whitespace();}
	  else if ((c2 = gch()) == 'a' && c == 'S')
	    {cdow = 7; skip_to_whitespace();}
	  else if (c == 'S' && c2 == 'u')
	    {cdow = 1; skip_to_whitespace();}
	  else if (c == 'T' && c2 == 'h')
	    {cdow = 5; skip_to_whitespace();}
	  else if (c == 'T' && c2 == 'u')
	    {cdow = 3; skip_to_whitespace();}
	  else
	    {
	      fprintf(stderr, "psch: found garbage where weekday name expected in line %d\n", lineno);
	      return(-1);
	    }
	  if ((c = gch()) == 'D') {cmonth = 12; skip_to_whitespace();}
	  else if (c == 'F') {cmonth = 2; skip_to_whitespace();}
	  else if (c == 'N') {cmonth = 11; skip_to_whitespace();}
	  else if (c == 'O') {cmonth = 10; skip_to_whitespace();}
	  else if (c == 'S') {cmonth = 9; skip_to_whitespace();}
	  else if ((c2 = gch()) == 'p' && c == 'A')
	    {cmonth = 4; skip_to_whitespace();}
	  else if (c == 'A' && c2 == 'u')
	    {cmonth = 8; skip_to_whitespace();}
	  else if (c == 'J' && c2 == 'a')
	    {cmonth = 1; skip_to_whitespace();}
	  else if ((c2 = gch()) == 'y' && c == 'M')
	    {cmonth = 5; skip_to_whitespace();}
	  else if (c == 'M' && c2 == 'r')
	    {cmonth = 3; skip_to_whitespace();}
	  else if (c == 'J' && c2 == 'n')
	    {cmonth = 6; skip_to_whitespace();}
	  else if (c == 'J' && c2 == 'l')
	    {cmonth = 7; skip_to_whitespace();}
	  else
	    {
	      fprintf(stderr, "psch: found garbage where month name expected in line %d\n", lineno);
	      return(-1);
	    }
	  cday = parse_int();
	  skip_whitespace();
	}
      else if (c >= '1' && c <= '9')
	{
	  int n;
	  int c2;
	  
	  pdl[pdsz].text[0] = c;
	  c2 = c;
	  for (n = 1; ((c = gch()) != '\n' || c2 != '\n') && c2 != EOF; ++n)
	    {
	      pdl[pdsz].text[n] = c;
	      c2 = c;
	    }
	  pdl[pdsz].text[n] = 0;
	  if (cdow == 0 || cmonth == 0 || cday == 0 || cchannel == 0)
	    {
	      fprintf(stderr, "psch: incompletely specified channel/date before first description in line %d\n", lineno);
	      return(-1);
	    }
	  pdl[pdsz].sm = 0;
	  n = pdl[pdsz].text[0] - '0';
	  if (pdl[pdsz].text[1] == ':')
	    {
	      pdl[pdsz].sm = (pdl[pdsz].text[2] - '0') * 10 + pdl[pdsz].text[3] - '0';
	    }
	  else if (pdl[pdsz].text[1] >= '0' && pdl[pdsz].text[1] <= '9')
	    {
	      n = n * 10 + pdl[pdsz].text[1] - '0';
	      if (pdl[pdsz].text[2] == ':')
		{
		  pdl[pdsz].sm = 
		    (pdl[pdsz].text[3] - '0') * 10 + pdl[pdsz].text[4] - '0';
		}
	    }
	  {
	    int r;
	    
	    for (r = 1; pdl[pdsz].text[r] != 0; ++r)
	      {
		if (pdl[pdsz].text[r] == 'a')
		  {
		    if (n == 12)
		      {
			n = 0;
		      }
		    break;
		  }
		else if (pdl[pdsz].text[r] == 'p')
		  {
		    if (n != 12)
		      {
			n += 12;
		      }
		    break;
		  }
		else if (pdl[pdsz].text[r] == 'n')
		  {
		    n = 12;
		    break;
		  }
		else if (pdl[pdsz].text[r] == 'm')
		  {
		    n = 0;
		    break;
		  }
	      }
	  }   
	  pdl[pdsz].sh = n;
	  pdl[pdsz].dow = cdow;
	  pdl[pdsz].month = cmonth;
	  pdl[pdsz].day = cday;
	  pdl[pdsz].channel = cchannel;
	  ++pdsz;
	}
      ungetc((c = getchar()), stdin);
    }
  sort(pdl, 0, pdsz, pdmonth);
  partnsort(pdl, pdsz, pdmonth, pdday);
  partnsort(pdl, pdsz, pdday, pdchannel);
  partnsort(pdl, pdsz, pdchannel, pdsh);
  partnsort(pdl, pdsz, pdsh, pdsm);
  unparse_dow((cdow = pdl[0].dow));
  printf(", ");
  unparse_month((cmonth = pdl[0].month));
  printf(" %d\n\nChannel %d\n\n", (cday = pdl[0].day), (cchannel = pdl[0].channel));
  {
    int r;
    for (r = 0; r < pdsz; ++r)
      {
	if (pdl[r].day != cday || pdl[r].month != cmonth)
	  {
	    unparse_dow((cdow = pdl[r].dow));
	    printf(", ");
	    unparse_month((cmonth = pdl[r].month));
	    printf(" %d\n\nChannel %d\n\n", (cday = pdl[r].day), (cchannel = pdl[r].channel));
	  }
	if (pdl[r].channel != cchannel)
	  {
	    printf("Channel %d\n\n", (cchannel = pdl[r].channel));
	  }
	printf("%s\n", pdl[r].text);
/* used for debugging
 *	printf("%2d/%2d (%d)  %2d:%02d Ch. %-2d  %s", pdl[r].month,
 *	                                              pdl[r].day,
 *	                                              pdl[r].dow,
 *	                                              pdl[r].sh,
 *                                                    pdl[r].sm,
 *                                                    pdl[r].channel,
 *                                                    pdl[r].text); */
      }
  } 
}
