/* arclynx - simple version of Lynx for Acorn machines
 * (c) 1996 Andrew Wood
 *
 * Routines contained in this file (a_main.c):
 *   lynx_main()           - last modified 96/07/18
 *   lynx_redraw()         - last modified 96/07/18
 *   lynx_print()          - last modified 96/07/18
 *   lynx_main_goto()      - last modified 96/07/18
 *   lynx_main_yn()        - last modified 96/07/18
 *   lynx_main_nextpage()  - last modified 96/07/18
 *   lynx_main_prevpage()  - last modified 96/07/18
 *   lynx_main_linkrange() - last modified 96/07/18
 *   lynx_main_nextlink()  - last modified 96/07/18
 *   lynx_main_prevlink()  - last modified 96/07/18
 *   lynx_main_link()      - last modified 96/07/18
 */

#include "arclynx.h"
#include <string.h>


void lynx_redraw(Status *);                                 /* redraw screen */
void lynx_print(Status *);                  /* print parsed document to file */
void lynx_main_goto(Status *);                /* jump to new URL, user-input */
int lynx_main_yn(void);          /* wait for Y or N key press, return 1 if Y */
void lynx_main_nextpage(Status *,Screen *); /* skip to next page of document */
void lynx_main_prevpage(Status *,Screen *);     /* previous page of document */
void lynx_main_linkrange(Status *);        /* check current link is in range */
void lynx_main_nextlink(Status *,Screen *);             /* move to next link */
void lynx_main_prevlink(Status *,Screen *);         /* move to previous link */
void lynx_main_link(Status *);                        /* follow current link */


/******************************************************************************
 * lynx_main_goto(status) - go to new, user-specified URL
 *
 * NB this routine uses status->temp
 */
void lynx_main_goto(Status *status) {
 awterm_input("URL to jump to: ",status->temp+256);               /* get URL */
 lynx_history_push(status,status->build.url);           /* store current URL */
 lynx_fetch(status,status->temp+256);                       /* fetch new URL */
 if (!status->display.changed) {                                  /* failed? */
  lynx_history_pop(status);     /* yes - remove URL from history stack again */
  lynx_redraw(status);
 } else {
  lynx_redraw(status);
  if (status->display.top_link<status->build.anchor_hrefs)
   status->display.current_link=status->display.top_link;
 }
}


/******************************************************************************
 * lynx_main_link(status) - follow current link
 */
void lynx_main_link(Status *status) {
 static char temp[MAX_URL_LENGTH];         /* temporary copy of URL to go to */
 static char *p;                                 /* secondary string pointer */
 if (status->display.current_link<0) return;
 if (status->display.current_link>=status->build.anchor_hrefs) return;
 strcpy(temp,status->build.url);                         /* copy current URL */
 if ((p=strrchr(temp,'/'))!=NULL) {                             /* full URL? */
  p[1]=0;                                    /* yes - blank all after last / */
  strcat(temp,status->build.anchor_href[status->display.current_link]);
 } else {
  strcpy(temp,status->build.anchor_href[status->display.current_link]);
 }                          /* if new URL starts with #, that's all we need: */
 if (status->build.anchor_href[status->display.current_link][0]=='#') {
  strcpy(temp,status->build.anchor_href[status->display.current_link]);
 }
 if (strchr(status->build.anchor_href[status->display.current_link],':')
     !=NULL) {                 /* ie don't alter the URL if it has a : in it */
  strcpy(temp,status->build.anchor_href[status->display.current_link]);
 }
 lynx_history_push(status,status->build.url);           /* store current URL */
 lynx_fetch(status,temp);                                    /* get document */
 if (!status->display.changed) {
  lynx_history_pop(status);      /* if failed to fetch it, pop history again */
  lynx_redraw(status);
 } else {
  lynx_redraw(status);
  if (status->display.top_link<status->build.anchor_hrefs)
   status->display.current_link=status->display.top_link;
 }
}


/******************************************************************************
 * lynx_main_yn() - return 1 if Y pressed, 0 if N pressed
 */
int lynx_main_yn(void) {
 static int c;                                          /* character pressed */
 do {                                                /* wait for 'y' or 'n': */
  c=awterm_getch(); if ((c>='A') & (c<='Z')) c+=32;       /* lowercase input */
 } while ((c!='y') & (c!='n'));
 if (c=='y') return(1);                                           /* 'y' = 1 */
 return(0);                                                       /* 'n' = 0 */
}


/******************************************************************************
 * lynx_main_nextpage(status,screen) - go to next page
 */
void lynx_main_nextpage(Status *status,Screen *screen) {
 status->display.screen_top+=(screen->height-6);
 if (status->display.bottom_link>=0)
  status->display.current_link=status->display.bottom_link;
 lynx_redraw(status);
}


/******************************************************************************
 * lynx_main_prevpage(status,screen) - go to previous page
 */
void lynx_main_prevpage(Status *status,Screen *screen) {
 status->display.screen_top-=(screen->height-6);
 if (status->display.top_link<status->build.anchor_hrefs)
  status->display.current_link=status->display.top_link;
 lynx_redraw(status);
}


/******************************************************************************
 * lynx_main_linkrange(status) - check that currently selected link is in range
 */
void lynx_main_linkrange(Status *status) {
 if (status->display.current_link<0) status->display.current_link=0;
 if (status->display.current_link>status->build.anchor_hrefs)
  status->display.current_link=status->build.anchor_hrefs-1;
}


/******************************************************************************
 * lynx_main_nextlink(status,screen) - go to next link
 */
void lynx_main_nextlink(Status *status,Screen *screen) {
 status->display.current_link++;
 lynx_main_linkrange(status);
 if (status->display.current_link>status->display.bottom_link)
  status->display.screen_top+=(screen->height-6);
 lynx_redraw(status);
}


/******************************************************************************
 * lynx_main_prevlink(status,screen) - go to previous link
 */
void lynx_main_prevlink(Status *status,Screen *screen) {
 status->display.current_link--;
 lynx_main_linkrange(status);
 if (status->display.current_link<status->display.top_link)
  status->display.screen_top-=(screen->height-6);
 lynx_redraw(status);
}


/******************************************************************************
 * lynx_redraw(status) - redraw screen, correcting any out-of-bounds position
 *                       variables
 *
 * NB this routine uses status->temp
 */
void lynx_redraw(Status *status) {
 static Screen *screen;                     /* pointer to screen information */
 static int a,b,c,d,i,j,k;            /* misc. variables, iteration counters */
 static char anchor,selected,bold,italic,heading;            /* colour flags */
 static int current_page,num_pages;         /* current page, number of pages */
 status->display.changed=0;                          /* clear 'changed' flag */
 if (status->build.url[0]!=0) {              /* display current URL, if any: */
  sprintf(status->temp,"Current URL: %s",status->build.url);
 } else {
  sprintf(status->temp,"No current URL");
 }
 awterm_status(status->temp);
 screen=awterm_workspace();                            /* get screen pointer */
 if (status->display.screen_top>=status->build.lines)
  status->display.screen_top=status->build.lines-(screen->height-5);
 if (status->display.screen_top<0) status->display.screen_top=0;
 num_pages=status->build.lines/(screen->height-5);
 if (num_pages*(screen->height-5)<=status->build.lines) num_pages++;
 a=status->display.screen_top+(screen->height-5)/2;
 current_page=a/(screen->height-5);
 if (current_page*(screen->height-5)<=a) current_page++;
 if (current_page>num_pages) current_page=num_pages;
 if (num_pages>1) {
  a=strlen(status->build.title);
  sprintf(status->temp," (%d/%d)",current_page,num_pages);
  if (a>(screen->width-strlen(status->temp)))
   a=screen->width-strlen(status->temp);
  strcpy(status->temp,status->build.title);
  sprintf(status->temp+a," (%d/%d)",current_page,num_pages);
 } else {
  strcpy(status->temp,status->build.title);
  status->temp[screen->width]=0;
 }
 a=strlen(status->temp);
 awterm_move(0,0); awterm_colour(LYNX_COL_PLAIN);
 for(i=0; i<(screen->width-a); i++) {awterm_char(' ');}
 for(i=0; i<a; i++) {awterm_char(status->temp[i]);}
 status->display.top_link=32767; status->display.bottom_link=-1;
 for(i=0; i<(screen->height-5); i++) {
  awterm_move(0,2+i);
  a=status->display.screen_top+i;
  if (a>=status->build.lines) {
   for(j=0; j<screen->width; j++) {awterm_char(' ');}
  } else {
   j=0; k=0; anchor=0; selected=0; bold=0; italic=0; heading=0;
   c=LYNX_COL_PLAIN; awterm_colour(c);
   while (status->build.line[a][j]!=0) {
    switch(status->build.line[a][j]) {
     case LYNX_A_HREF_CODE:
      anchor=1;
      j++; b=status->build.line[a][j];
      j++; b+=256*status->build.line[a][j];
      if (b==status->display.current_link) selected=1;
      if (status->display.top_link>b) status->display.top_link=b;
      if (status->display.bottom_link<b) status->display.bottom_link=b;
      break;
     case LYNX_A_HREF_END:anchor=0; selected=0; break;
     case LYNX_A_NAME_CODE:anchor=1; selected=0; j+=2; break;
     case LYNX_A_NAME_END:anchor=0; selected=0; break;
     case LYNX_BOLD_CODE:bold=1-bold; break;
     case LYNX_ITALIC_CODE:italic=1-italic; break;
     case LYNX_HEADING_CODE:heading=1-heading; break;
     case LYNX_SPACES_CODE:
      j++; b=status->build.line[a][j];
      while(b>0) {
       if (k<screen->width) {awterm_char(' '); k++;}
       b--;
      }
      break;
     default:
      d=c; c=LYNX_COL_PLAIN;
      if (heading) c=LYNX_COL_HEADING;
      if (italic) c=LYNX_COL_ITALIC;
      if (bold) c=LYNX_COL_BOLD;
      if (selected) c=LYNX_COL_SELECTED;
      if (anchor) c+=LYNX_COL_ANCHOR;
      if (d!=c) awterm_colour(c);
      if (k<screen->width) {awterm_char(status->build.line[a][j]); k++;}
      break;
    }
    j++;
   }
   while (k<screen->width) {awterm_char(' '); k++;}
  }
 }
 if ((status->display.current_link>=0) & 
     (status->display.current_link<status->build.anchor_hrefs)) {
  sprintf(status->temp,"Link selected: %s",
          status->build.anchor_href[status->display.current_link]);
 } else {sprintf(status->temp,"");}
 awterm_info(status->temp);
}


/******************************************************************************
 * lynx_print(status) - print parsed document to a file (having asked user
 *                      for filename)
 *
 * NB this routine uses status->temp
 */
void lynx_print(Status *status) {
 static FILE *fptr;
 static int a,i,j;
 static char links;
 awterm_status("Save list of links as well?");
 do {
  a=awterm_getch(); if ((a>='A') & (a<='Z')) a+=32;
 } while ((a!='y') & (a!='n'));
 if (a=='y') {links=1;} else {links=0;}
 awterm_status("");
 awterm_input("Enter filename: ",status->temp);
 fptr=fopen(status->temp,"w");
 if (fptr==NULL) {
  awterm_status("Failed to open file for output");
  awterm_pause();
  return;
 }
 fprintf(fptr,"----------------------------------------");
 fprintf(fptr,"--------------------------------------\n");
 fprintf(fptr,"Document URL: %s\n",status->build.url);
 fprintf(fptr,"----------------------------------------");
 fprintf(fptr,"--------------------------------------\n");
 for(i=0; i<(status->build.lines); i++) {
  if (status->build.line[i]!=NULL) {
   j=0; while (status->build.line[i][j]!=0) {
    switch(status->build.line[i][j]) {
     case LYNX_A_HREF_CODE:
     case LYNX_A_NAME_CODE:j+=2; break;
     case LYNX_A_HREF_END:
     case LYNX_A_NAME_END:j++; break;
     case LYNX_BOLD_CODE:
     case LYNX_ITALIC_CODE:
     case LYNX_HEADING_CODE:break;
     case LYNX_SPACES_CODE:
      j++; a=status->build.line[i][j];
      while (a>0) {fprintf(fptr," "); a--;}
      break;
     default:fprintf(fptr,"%c",status->build.line[i][j]); break;
    }
    j++;
   }
  }
  fprintf(fptr,"\n");
 }
 if (links & (status->build.anchor_hrefs>0)) {
  fprintf(fptr,"\n----------------------------------------");
  fprintf(fptr,"--------------------------------------\n");
  fprintf(fptr,"Document contained the following links:\n");
  for(i=0; i<(status->build.anchor_hrefs); i++) {
   fprintf(fptr," %s\n",status->build.anchor_href[i]);
  }
 }
 fclose(fptr);
 awterm_status("Document saved");
 awterm_pause();
 awterm_status("");
}


/******************************************************************************
 * lynx_main(status) - main program loop: check for key presses, redraw screen,
 *                     get new documents
 *
 * NB this routine uses status->temp
 */
void lynx_main(Status *status) {
 static char quit;                                            /* 'quit' flag */
 static int c;                                                /* key pressed */
 static Screen *screen;                                       /* screen data */
 awterm_clear();                                             /* clear screen */
 screen=awterm_workspace();                       /* get screen info pointer */
 status->display.screen_top=0;
 status->display.current_link=0;
 status->display.changed=1;                      /* 'update screen' flag set */
 quit=0;                                              /* 'quit' flag cleared */
 do {
  if (status->finished) return;                            /* abort if error */
  if (status->display.changed) lynx_redraw(status);/*redraw screen if changed*/
  c=awterm_getch();                                              /* read key */
  if ((c>='A') & (c<='Z')) c+=32;                   /* convert to lower case */
  switch(c) {
   case 12:awterm_clear(); lynx_redraw(status); break;      /* redraw screen */
   case 32:lynx_main_nextpage(status,screen); break;            /* next page */
   case '-':lynx_main_prevpage(status,screen); break;       /* previous page */
   case '0':
   case 'b':                                       /* beginning of document: */
    status->display.screen_top=0; status->display.current_link=0;/* top line */
    lynx_redraw(status); break;
   case 'd':lynx_debug(status); break;                              /* debug */
   case 'g':lynx_main_goto(status); break;                          /* go to */
   case 'p':lynx_print(status); break;
   case 'q':                                                        /* quit: */
    awterm_status("Quit - are you sure?");           /* confirmation message */
    quit=lynx_main_yn(); lynx_redraw(status); break;           /* get answer */
   case 'z':                                             /* end of document: */
    status->display.screen_top=status->build.lines-(screen->height-5);
    status->display.current_link=status->build.anchor_hrefs-1;/* last screen */
    lynx_redraw(status); break;
   case 136:lynx_fetch(status,lynx_history_pop(status)); break;   /* go back */
   case 137:                                                  /* follow link */
   case 13:                                                   /**/
   case 10:lynx_main_link(status); break;                     /**/
   case 138:lynx_main_nextlink(status,screen); break;           /* next link */
   case 139:lynx_main_prevlink(status,screen); break;       /* previous link */
   default:break;
  }
 } while (!quit);
}
