/* hv.c - Hypertext Notebook Viewer PROTOTYPE

  REV   DATE	REVISION NOTES
   -  10/16/02	Rename from htn.c; fix viewer; link support.

Keys:
  jkli	Scroll around
  b	Page up
  SPC	Page down
  ,.	Prev/Next link
  x	Follow link
  z	Back

File Format:
  My HTN files are ASCII text. A few special control characters serve the
  purpose of HTML tags, and ^@ (ascii 0) is the "end" tag.

  These are the tags so far: (but leave out the spaces)
    Center: ^C text ^@
    Table: ^T <row1 row2 ... rowN> ^@
       Row: cell1 ^I cell2 ^I ... cellN ^J
    Link: ^L filename ^@
    Include: TBD (in edit mode, I guess includes will look more like links)
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

// Colors
#define thcol0	0xb4
#define thcol1	0xf5
#define trcol0	0x06
#define trcol1	0x07
#define CLINK	0xb0	// Link
#define CLINK2	0xb1	// Link (selected)

char *file;
FILE *f;

char buf[4096*16];	// Edit buffer

int
  w=100, h=37,		// Screen size (my screen is really 100x37)
  cx=0, cy=0,		// Cursor position
  sx=0, sy=0;		// Scrolling location

// Temp variables for rendering.. draw(), etc.
int x, y;		// Current Position
char *p;		// Buffer ptr

struct {		// Coordinates for links
  int x, y;
  char *p;
  } link[100];
int
  links,		// Number of links in file
  clink=0;		// Currently selected link (in view mode)

char lstack[4096],	// Link stack
	*lp=lstack;

/* ---------------------------------------------------------- */

load(char *file) {
  int c, n;
  p = buf;
  
  if ( !(f = fopen(file, "r")) ) {
    printf("Couldn't open file '%s'\n", file);
    exit(1);
    }
  c = fgetc(f);
  while (!feof(f)) {
    *p++ = c;
    c = fgetc(f);
    }
  fclose(f);
  if (*(p-1)) *p++ = 0;		// Add final 0 if missing

  cx=cy=sx=sy=clink=0;
  }

/* ---------------------------------------------------------- */

myputc(char c) {
  if (c == 10) {
    if ( (y>=sy) && ((y-sy)<h-1) )
      putchar(c);
    x=0; y++;
    return;
    }
  if ( (x>=sx) && ((x-sx)<w) && (y>=sy) && ((y-sy)<h) )
    putchar(c);
  x++;
  }

myprintf(char *fmt, ...) {
  va_list arg;
  static char buf[1024];
  char *s;
  va_start(arg, fmt);
  vsprintf(buf, fmt, arg);
  va_end(arg);
  for (s=buf; *s; s++) myputc(*s);
  }

spaces(int n) {
  int i;
  for (i=0; i<n; i++) myputc(32);
  x += n;
  }

#include "term.h"

/* ---------------------------------------------------------- */
/* ---------------------------------------------------------- */

drawtable() {
  char *s;
  int cw[20] = { 0 };			// Column widths
  int tw=0;				// Table width
  int rows=0, cols=0;			// Number of rows & columns
  int row=0, col=0, pos=0;		// Counters

  // Pass I: Size it up
  for (s=p; *s; s++)
    switch (*s) {
      case 0: break;
      case 9: col++; pos=0; break;
      case 10: row++; if (col > cols) cols=col; col=0; pos=0; break;
      default: pos++; if (pos > cw[col]) cw[col] = pos;
      }
  cols++;
  for (col=0; col < cols; col++)
    tw += cw[col] + 2;
  tw--;

  // Pass II: Draw it
  mode(0);  
  for (row=0, col=0, pos=0; *p; p++) {
    switch (*p) {
      case 0:
        break;
      case 9:
        for (; pos < cw[col]+1; pos++) myputc(32);
        color( row ? (row%2 ? trcol0 : trcol1) : (row%2 ? thcol0 : thcol1) );
        myputc(32);
        col++; pos=0;
        break;
      case 10:
        while (x < tw) myputc(32);		// Pad to table width
//        for (; x < w; x++) myputc(32);		// Pad to screen width
        if (p[1] != 9) row++;
        col=0; pos=0;
        mode(0);
        myputc(10);
        if (p[1])
          color( col ? (row%2 ? trcol0 : trcol1) : (row%2 ? thcol0 : thcol1) );
        break;
      default:
        myputc(*p);
        pos++;
      }
    }
  }

/* ---------------------------------------------------------- */

drawcenter() {
  int len;
  for (len=0; p[len]; len++);
  spaces((w-len)/2);
  myprintf(p);
  p += len;
  }

/* ---------------------------------------------------------- */

int tmplink;

drawlink() {
  int len;
  link[tmplink].x = x;
  link[tmplink].y = y;
  link[tmplink].p = p;
  for (len=0; p[len]; len++);
  color(tmplink==clink? CLINK2 : CLINK);
  myprintf(p);
  p += len;
  mode(0);
  tmplink++;
  }

/* ---------------------------------------------------------- */

draw() {
  cls();
  tmplink=0;
  x=y=0;

  for (p=buf; *p; p++) {
    switch (*p) {
      case 3:  p++; drawcenter(); break;	// centered text
      case 20: p++; drawtable(); break;		// table
      case 12: p++; drawlink(); break;		// link
      case 0: mode(0); break;			// end
      default: myputc(*p);
      }
    }
  links = tmplink;
  }

// (un)highlight a link
hlink(int l, int c) {
  if (links && link[l].y>=sy && link[l].y<sy+h && link[l].x>=sx) {
    printf("\e7");
    xy(link[l].x -sx+1, link[l].y -sy+1);
    color(c);
    myprintf(link[l].p);
    mode(0);
    }
  }

/* ---------------------------------------------------------- */

view(char *file) {
  int c;
  
  load(file);
  draw();
  
  for(;;) {
    c = getchar();
    switch (c) {
      case 'q':
        return;
      case 12:		// ^L Redraw
        draw();
        break;
      case 'i':		// Up
        if (!sy) break;
        sy--; draw(); break;
      case 'k':		// Down
//        if (sy+1 >= lines) break;
        sy++; draw(); break;
      case 'b':		// Page up
        if (!sy) break;
        sy-=h;
        if (sy<0) sy=0;
        draw(); break;
      case ' ':		// Page down
//        if (sy+h > lines) break;
        sy+=h; draw(); break;
      case 'j':		// Left
        if (!sx) break;
        sx--; draw(); break;
      case 'l':		// Right
        sx++; draw(); break;
      case ',':		// Previous link
        if (!clink) break;
        hlink(clink, CLINK);
        clink--;
        hlink(clink, CLINK2);
        break;
      case '.':		// Next link
        if (clink+1 == links) break;
        hlink(clink, CLINK);
        clink++;
        hlink(clink, CLINK2);
        break;
      case 'x':		// Follow link
        if (!links) break;
        for (; *lp; lp++); lp++;
	strcpy(lp, link[clink].p);	// Push filename on link stack
        load(link[clink].p);
        draw();
        break;
      case 'z':		// Back
        if (lp==lstack) break;
        for (lp-=2; lp>=lstack && *lp; lp--); lp++;
        load(lp); draw();
        break;
      }
    }
  }

/* ---------------------------------------------------------- */

main(int argc, char **argv) {
  if (argc != 2) {
    printf("tcn's Hypertext Notebook Viewer\nUsage: hv <filename>\n");
    exit(1);
    }
  file = argv[1];
  term_init();
  strcpy(lp, file);		// Push filename on link stack
  view(file);
  term_cleanup();
  }

/* ---------------------------------------------------------- */
