/*
* A small progam to provide a curses based slides tool.  It paints
* slides from a text file.
*
* Slides are simply segments of text ended by "--" on a line by
* itself.
*/
#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/* command line arguments, held as a global variable */
char *argFileName;


/* a simple list structure to store the lines on a slide */
typedef struct lineNode {
 char *text;               /* the contents of the line itself */
 struct lineNode *next;    /* the next node */
} lineNode_t;


/* a doubly linked list to store the slide deck */
typedef struct slideNode {
 lineNode_t *lines;               /* the set of lines on this slide */
 int lineCount;                   /* the number of lines */
 int width;                       /* the width of the longest line */
 struct slideNode *next;          /* the next slide */
 struct slideNode *prev;          /* the previous slide */
} slideNode_t;


/* process the command line arguments.
  Returns 1 on success and 0 on invalid arguments. */
int processArguments(int argc, char** argv);

/* display a usage message for the wayward user */
void displayUsage(int argc, char **argv);

/* loads the deck return a pointer to the deck, null on failure */
slideNode_t *loadDeck();

/* display the slide show, returns when the user quits the slideshow */
void slideShow(slideNode_t *deck);

/* constructor functions */
slideNode_t *newSlide();
lineNode_t *newLine();

int
main(int argc, char **argv) {
 slideNode_t *deck;

 /* startup procedures */
 if(!processArguments(argc, argv)) {
   displayUsage(argc, argv);
   return -1;
 }
 deck=loadDeck();

 /* run the show */
 slideShow(deck);

 return 0;
}



/* process the command line arguments.
  Returns 1 on success and 0 on invalid arguments. */
int
processArguments(int argc, char** argv) {
 /* there shall be 2 arguments, and the number of arguments shall be
  * 2.  1 argument there shall not be, unless there exists a second
  * argument after it.  3 is right out!
  */
 if(argc != 2)
   return 0;

 /* get the file name */
 argFileName = argv[1];

 return 1;
}


/* display a usage message for the wayward user */
void
displayUsage(int argc, char **argv) {
 fprintf(stderr, "\nUsage: %s fileName\n\n", argv[0]);
}


/* loads the deck return a pointer to the deck, null on failure */
slideNode_t *
loadDeck() {
 slideNode_t *deck=NULL;
 slideNode_t *curSlide, *lastSlide;
 lineNode_t *curLine, *lastLine=NULL;
 char *text=malloc(100);
 int len;
 size_t bufSize=100;
 FILE *f;

 /* open the file, as best we can */
 f = fopen(argFileName, "r");
 if(!f) {
   perror("Error Loading Slide Deck");
   return NULL;
 }

 /* start the deck off */
 deck = curSlide = lastSlide = newSlide();

 /* Read the file */
 while(!feof(f)) {
   /* read the text line, strip newline */
   len = getline(&text, &bufSize, f);
   if(len <= 0)
     continue;
   if(text[len-1]=='\n') {
     text[len-1]='\0';
     len--;
   }

   /* handle new slides */
   if(!strcmp(text, "--")) {
     curSlide = newSlide();
     curSlide->prev = lastSlide;
     lastSlide->next = curSlide;
     lastSlide = curSlide;
     continue;
   }

   /* create and link the line */
   curLine = newLine();
   if(!(curSlide->lines)) {
     curSlide->lines = curLine;
   } else {
     lastLine->next = curLine;
   }
   lastLine = curLine;

   /* populate the line, and handle length */
   curLine->text = strdup(text);
   if(len > curSlide->width)
     curSlide->width = len;
   curSlide->lineCount++;
 }
 fclose(f);
 free(text);
 return deck;
}


/* display the slide show, returns when the user quits the slideshow */
void
slideShow(slideNode_t *deck) {
 slideNode_t *curSlide;
 lineNode_t *curLine;
 int x, y;
 int maxx, maxy;
 int c;

 /* handle empty slide decks */
 if(!deck) {
   fprintf(stderr, "Error:  No slides in deck!\n");
   return;
 }

 /* initialize curses */
 initscr();                  /* initialize  the screen */
 noecho();                   /* do not echo characters */
 raw();                      /* turn off character translation */
 keypad(stdscr, TRUE);       /* translate escape sequences to keys  */
 timeout(-1);                /* block indefinitely for input  */
 curs_set(0);                /* set invisible cursor */

 /* get our maxes */
 getmaxyx(stdscr, maxy, maxx);

 curSlide=deck;
 while(curSlide && curSlide->lines) {
   /* compute offsets */
   getbegyx(stdscr, y, x);
   y += (maxy - curSlide->lineCount) / 2;
   x += (maxx - curSlide->width) / 2;

   /* display the lines */
   clear();
   for(curLine=curSlide->lines; curLine; curLine=curLine->next) {
     mvaddstr(y++, x, curLine->text);
   }

   /* get and handle the command */
   c = getch();
   switch(c) {
   case KEY_RIGHT:
   case KEY_DOWN:
   case ' ':
     curSlide = curSlide->next;
     break;

   case KEY_LEFT:
   case KEY_UP:
     if(curSlide->prev)
       curSlide = curSlide->prev;
     break;

   case 'q':
   case KEY_EXIT:
     goto finish;
     break;

   }
 }

 /* put things back to their default behavior */
 finish:
 clear();
 endwin();
}


slideNode_t *
newSlide() {
 slideNode_t *result;

 result = malloc(sizeof(slideNode_t));
 result->lineCount = 0;
 result->width = 0;
 result->prev = result->next = NULL;

 return result;
}


lineNode_t *
newLine() {
 lineNode_t *result;

 result = malloc(sizeof(lineNode_t));
 result->text = NULL;
 result->next = NULL;

 return result;
}