#include "LYCurses.h"
#include "HTUtils.h"
#include "HTAccess.h"
#include "LYGlobalDefs.h"
#include "LYUtils.h"
#include "GridText.h"
#include "LYStrings.h"
#include "LYOptions.h"
#include "LYSignal.h"
#include "LYGetFile.h"
#include "HTForms.h"
#include "LYSearch.h"
#include "LYClean.h"
#include "LYHistory.h"
#include "LYPrint.h"

/*
 * here's where we do all the work
 * mainloop is basically just a big switch dependent on the users input
 * I have tried to offload most of the work done here to procedures to
 * make it more modular, but this procedure still does alot of variable
 * manipulation.  This need some work to make it neater.
 */

void mainloop ()
{
    int  c, arrowup=FALSE, show_help=FALSE;
    int lines_in_file= -1;
    int itmp;
    int newline;
    char prev_target[MAXHIGHLIGHT];
    char line_buffer[1024];
    char address_buffer[1024];
    char *owner_address;  /* holds the responsible owner's address */
    document newdoc, curdoc;
    char *cp, *tmptr;
    FILE *fp = NULL, *oldfp;
    BOOLEAN first_file=TRUE;
    BOOLEAN refresh_screen=FALSE;

/*
 *  curdoc.address contains the name of the file that is currently open
 *  newdoc.address contains the name of the file that will soon be 
 *		   opened if it exits
 *  prev_target contains the last search string the user searched for
 *  newdoc.title contains the link name that the user last chose to get into
 *		   the current link (file)
 */
      /* initalize some variables*/
    nhist = 0;
    newdoc.address = startfile;
    newdoc.title = line_buffer;
    strcpy(newdoc.title, "Entry into main menu");
    newdoc.line = 1;
    newdoc.link = 0;
    curdoc.address 	= NULL;
    *prev_target	= '\0';

    if(TRACE)
	fprintf(stderr,"Entering mainloop, startfile=%s\n",startfile);

    if(user_mode==NOVICE_MODE)
        display_lines = LINES-4;
    else
        display_lines = LINES-2;

    while (TRUE) {
	/* if newdoc.address is different then curdoc.address then we need 
	 * to go out and find and load newdoc.address
	 */
	if (curdoc.address==NULL || newdoc.address==NULL ||
			!STREQ(curdoc.address, newdoc.address)) {

		/* push the old file onto the history stack
	 	 */
		if(curdoc.address!=NULL && newdoc.address!=NULL) {
		    push(&curdoc);

		} else if(newdoc.address==NULL) {
		    /* if newdoc.address is empty then pop a file 
		     * and load it 
		     */
                    pop(&newdoc);
		}

		switch(getfile(&newdoc)) {


		case NOT_FOUND: 

        	           /* OK! can't find the file */
                           /* so it must not be around now.  Print an error */
                   statusline(LINK_NOT_FOUND);
		   LYgetch();  /* pause for awhile */

		   if(error_logging && !first_file && owner_address) 
			/* send an error message */
                      mailmsg(curdoc.link, owner_address, 
			history[nhist-1].hfname, history[nhist-1].hightext);

			/* do the NULL stuff also and reload the old file */

		   /* the first file wasn't found or has gone missing */
		   if(!nhist) { 
			/* if nhist = 0 then it must be the first file */
			cleanup();
		        printf("\nlynx: Can't access start file %s\n",
								  startfile);
			exit(1);
		    }

		case NULLFILE:  /* not supposed to return any file */

		   /* the first file wasn't found or has gone missing */
		   if(!nhist) { 
			/* if nhist = 0 then it must be the first file */
			cleanup();
			printf("\nlynx: Start file not displayable.  Exiting...\n");
			exit(1);
		    }

		    pop(&newdoc);
		    newdoc.line = 1; /* reset */
		    newdoc.link = 1; /* reset */
	
                    break;

		case NORMAL: 
#ifdef TRAVERSAL
		   /* during traversal build up a list of all links
		    * traversed.  Traversal mode is a special 
		    * feature to traverse every link in the web
		    */	
		   add_to_traverse_list(curdoc.address, curdoc.title);
#endif TRAVERSAL
		   curdoc.line = -1; 

		  /*
            	   * set newline to the saved line
            	   * savline contains the line the
            	   * user was on if he has been in
            	   * the file before or it is 1
            	   * if this is a new file
		   */
                   newline = newdoc.line;

	  	   break;	
		}  /* end switch */

           owner_address = HText_getOwner();

	   if(TRACE)
	      sleep(2); /* allow me to look at the results */

	   /* set the files the same */
	   curdoc.address = newdoc.address;

	   /* reset WWW present mode so that if we were getting
	    * the source, we get rendered HTML from now on
	    */
	   HTOutputFormat = WWW_PRESENT;

  	} /* end if(STREQ(newdoc.address,curdoc.address) */

        if(dump_output_immediately) {
            print_wwwfile_to_fd(stdout);
	    exit(0);
	}

	/* if the resent_sizechange variable is set to true
	   then the window size changed recently. 
	*/
	if(recent_sizechange == TRUE) {
		stop_curses();
		start_curses();
		refresh_screen = TRUE; /*to force a redraw */
		recent_sizechange=FALSE;
		display_lines = LINES-2;
	}

        if(www_search_result != -1) {
             /* This was a WWW search, set the line
              * to the result of the search
              */
             newline = www_search_result;
             www_search_result = -1;  /* reset */
	     more = HText_canScrollDown();
        }


	/* if the curdoc.line is different than newline then there must
	 * have been a change since last update. Run showpage.
	 * showpage will put a fresh screen of text out.
	 * If this is a WWW document then use the 
	 * WWW routine HText_pageDisplay to put the page on
	 * the screen
         */
	if (curdoc.line != newline) {
	
   	    refresh_screen = FALSE;

	    HText_pageDisplay(newline, prev_target);
	    /* if more equals true then there is more
	     * info below this page 
	     */
	    more = HText_canScrollDown();
	    curdoc.line = newline = HText_getTopOfScreen()+1;
            lines_in_file = HText_getNumOfLines();

            if(HText_getTitle()) {
	        curdoc.title = HText_getTitle();
	    } else {
	        curdoc.title = newdoc.title;
		if(curdoc.title == NULL)
		    curdoc.title = empty_string;
	    }

	   /* put the novice line stuff on the screen */
	   if(user_mode == NOVICE_MODE) 
		noviceline(more);  /* print help message */

	   if (arrowup) { 
		/* arrow up is set if we just came up from
		 * a page below 
		 */
	        curdoc.link = nlinks - 1;
	        arrowup = FALSE;
	   } else {
	        curdoc.link = newdoc.link;
	   }

	   show_help = FALSE; /* reset */
	   newdoc.link = 0;
	   newdoc.line = 1;
	   curdoc.line = newline; /* set */
	}

	/* refesh the screen if neccessary */
	if(refresh_screen) {
	    HText_pageDisplay(newline, prev_target);
	    if(user_mode == NOVICE_MODE) 
		noviceline(more);  /* print help message */
	    refresh_screen=FALSE;

	}

	if(first_file == TRUE) { /* we can never again have the first file */
	   first_file = FALSE;  
	   show_help=TRUE;
	   if(user_mode==NOVICE_MODE) {
	      /* do nothing */
	   } else if(is_www_index && more)
		statusline(WWW_INDEX_MORE_MESSAGE);
	   else if(is_www_index)
		statusline(WWW_INDEX_MESSAGE);
	   else if(more)  /* show some help for the first file */
               statusline(MOREHELP);
           else
               statusline(HELP);
	 }  
		
	/* if help is not on the screen 
	 * then put a message on the screen
	 * to tell the user other misc info
	 */
	if (!show_help) {
	    /* if we are in forms mode then explicitly
	     * tell the user what each kind of link is
	     */
	    if(lynx_mode==FORMS_LYNX_MODE) {
                if(links[curdoc.link].type == WWW_FORM_LINK_TYPE)
	            statusline(FORM_LINK_MESSAGE);
                else
	            statusline(NORMAL_LINK_MESSAGE);

	    } else if(user_mode == NOVICE_MODE) {
		/* do nothing */
	    } else if(user_mode == ADVANCED_MODE && nlinks>0) {
		/* show the URL */
		statusline(links[curdoc.link].lname);
	    } else if(is_www_index && more) {
		statusline(WWW_INDEX_MORE_MESSAGE);
	    } else if(is_www_index) {
		statusline(WWW_INDEX_MESSAGE);
	    } else if (more) {
		statusline(MORE);
	    } else {
	       statusline(HELP);
   	    }	     
	} else {
	   show_help = FALSE;
	}

	highlight(ON, curdoc.link);	/* highlight current link */

#ifdef TRAVERSAL
	/* this is a special feature to traverse every link in
	 * the database, and look for errors
	 */
	if( lookup(links[cur].lname)) {
		if(more || (cur > -1 && cur < nlinks-1))
		   c=DNARROW;
		else {
		   if(STREQ(curdoc.title,"Entry into main menu"))
		       exit(0);
		   c=LTARROW;
		}
	} else {
		add_to_table(links[cur].lname,);
		c = RTARROW;
	}
#else
	/* Get a keystroke from the user
	 */ 
	  c=LYgetch();   /* get user input */
	

#endif TRAVERSAL

        if(vi_keys)   /* convert h,j,k,l to a arrows for vi key users */
	   switch(c) {  /* note: only lowercase keys are mapped :) */
	   case 'j':
		c=DNARROW;
		break;
	   case 'k':
		c=UPARROW;
		break;
	   case 'h':
		c=LTARROW;
		break;
	   case 'l':
		c=RTARROW;
	   }
		
       if(emacs_keys) /* convert ^P, ^N, ^B, ^F to arrows for emacs keys */
	  switch(c) {  /* note: only lowercase keys are mapped :) */
          case 14: /* really control-N, not ^N */
              c=DNARROW;
              break;
          case 16: /* really control-P not ^P */
              c=UPARROW;
              break;
          case 2: /* really control-B not ^B */
              c=LTARROW;
              break;
          case 6: /* really control-F not ^F */
              c=RTARROW;
        }


	/* convert 4, 6, 2, 8 to left, right, down, up, etc. */
	if(keypad_mode == NUMBERS_AS_ARROWS)
	    c = number2arrows(c);


	switch(c) {
	case DO_NOTHING:
	    /* pretty self explanitory */
	    break;
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	    /* get a number from the user and follow that link number */
	    switch(follow_link_number(c, &curdoc.link)) {
	    case DO_LINK_STUFF:
                    /* follow a normal link */
                newdoc.address = links[curdoc.link].lname;
		break;

	    case PRINT_ERROR:
		statusline(BAD_LINK_NUM_ENTERED);
		sleep(1);
		break;
	    }
	    break;

	case '\\':  /* toggle view source mode */
	     if(HTisDocumentSource()) 
	         HTOutputFormat = WWW_PRESENT;
	     else
	         HTOutputFormat = WWW_SOURCE;
	     HTuncache_current_document();
	     newdoc.address = curdoc.address;
	     curdoc.address = NULL;	
	     break;

	case 18:  /* control-R to reload and refresh */
	     HTuncache_current_document();
	     newdoc.address = curdoc.address;
	     curdoc.address = NULL;	
	     newdoc.line=curdoc.line;
	     newdoc.link=curdoc.link;
#ifdef VMS
	     clearok(curscr, TRUE);
	     refresh();
#endif /* VMS */
	     break;

#ifdef NOT_DONE_YET
	case '|':  
	    /* ignore for now */
	    break
#endif NOT_DONE_YET

	case 'Q':
	    return;  /* dont ask the user about quitting */
	case 'q':	/* quit */
	case 4:
	case EOF:
	    statusline("Are you sure you want to quit? [Y] ");
	    if(toupper(LYgetch()) != 'N')
	        return;
	    else {
	        statusline("Excellent!");
		sleep(1);
	    }
	    break;
	
#ifdef VMS
	case 26:  /* CTRL-Z force immediate quit */
	    return;
	    break;
#endif VMS

	case ' ':	/* next page */
	case '+':
	case 44:   /* plus on mac keypad */
	case PGDOWN:  /* page down */
	case 6:	/* control F */
	    if(more)
	           newline += display_lines;
	    break;

	case 'b':	/* prev page */
	case 'B':
	case '-':
	case PGUP:  /* page up */
	case 2:	/* control B */
	   if(newline > 1) {
	           newline -= display_lines;
	    }
	    break;

	case  16:  /* control p */
	    newline -= 2;
	    break;

	case  14: /* control n */
	    newline += 2;
	    break;

	/* control R DOES NOT just wipe the screen any more */
	case 23:  /* control W to wipe the screen */
	   refresh_screen=TRUE;
	   newdoc.link=curdoc.link;
#ifdef VMS
	   clearok(curscr, TRUE);
	   refresh();
#endif /* VMS */
	   break;

	case HOME:
	    newline = 1;
	    break;

	case END:
	    if(more) {
	       newline = MAXINT; /* go to end of file */
	       arrowup = TRUE;  /* position on last link */
	    }
	    break;

	case UPARROW:
	    if (curdoc.link>0) {		/* previous link */
		highlight(OFF, curdoc.link);   /* unhighlight the current link */
		curdoc.link--;

	    } else if(!more && curdoc.link==0 && newline==1) { /* at the top of list */ 
		/* if there is only one page of data and the user
		 * goes off the top, then just move the curser to
		 * last link on the page
		 */
		highlight(OFF,curdoc.link); /* unhighlight the current link */
		curdoc.link = nlinks-1;  /* the last link */

	    } else if (curdoc.line > 1) {	/* previous page */
		/* go back to the previous page */
		    newline -= (display_lines);
		    arrowup = TRUE;
	    }
	    break;

	case DNARROW:
	case 9  : /* the TAB key */
	    if (curdoc.link<nlinks-1) {		/* next link */
		highlight(OFF, curdoc.link);
		curdoc.link++;
	    /* at the bottom of list and there is only one page 
	     * move to the top link on the page
	     */
	    } else if(!more && newline==1 && curdoc.link==nlinks-1) {
		highlight(OFF,curdoc.link); 
		curdoc.link = 0;

	    } else if (more) {	/* next page */
		    newline += (display_lines);
	    }
	    break;

	case 127: /* delete */  /* show the history page */
	case 8:   /* back space */
	  if(strcmp(curdoc.title,"Lynx History Page")) {	
		/* don't do if already viewing history page */	

		/* push current file so that the history
		 * list contains the curent file for printing
		 * purposes.
		 * pop the file afterwards to prevent multiple copies 
		 */
                push(&curdoc);

		/* print history options to file */
	    	showhistory(&newdoc.address);  

                pop(&curdoc);

		refresh_screen=TRUE;

	        break; 
	  } /* end if strncmp */

	/* dont put break here so that if the backspace key is pressed in
	 * the history page, then it acts like a left arrow 
	 */

	case LTARROW:			/* back up a level */
	case 'u':
	case 'U':
	        if (nhist>0) {  /* if there is anything to go back to */

		    /* set newdoc.address to empty to pop a file */
		    newdoc.address = NULL;

	        } else if(child_lynx==TRUE) {
	   	   return; /* exit on left arrow in main screen */ 
		}
	     break;

	case RTARROW:			/* follow a link */
	case '\n':
	case '\r':
	    if (nlinks > 0) {
	        if(links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
		        curdoc.link += change_form_link(&links[curdoc.link],
				    FORM_UP, &newdoc, &refresh_screen);

	        } else {   /* Not a forms link */
		    /* follow a normal link */
		    newdoc.address = links[curdoc.link].lname;
		    newdoc.title = links[curdoc.link].hightext;
		}
	    }
	    break;

	case 'g':   /* 'g' to goto a random file  */
	case 'G':
	 
	    if(anonymous) {
	    	statusline("Anonymous users may not Goto a random URL!");
		sleep(1);
		break;
	    }
	 
	    statusline("Filename or URL to open: "); 
	    *address_buffer = '\0';
	    LYgetstr(address_buffer, VISIBLE); /* ask the user */
	    if(*newdoc.address != '\0') {
	    
		/* make a name for this new file */
		newdoc.title = line_buffer;
	        strcpy(newdoc.title, "A file specified by the user");
	        newdoc.address = address_buffer;
	    } 
	    break;

	case '?':			/* show help file */
	case 'H':
	case 'h':
	case F1:   /* f1 key */
	    if(!STREQ(curdoc.address, helpfile)) {
	        newdoc.address = helpfile;  /* set the filename */

		/* make a name for this help file */
		newdoc.title = line_buffer;
	        strcpy(newdoc.title, "Help Screen");
	    }
	    break;

	case 'I':  /* index file */
	case 'i':  /* a special non-WWW, Lynx feature */
		/* make sure we are not in the index already */
	    if(!STREQ(curdoc.address, indexfile)) {

	        if(indexfile[0]=='\0') { /* no defined index */
		    statusline("No index is currently available");
		    sleep(1);

	        } else {
	            newdoc.address = indexfile;
		    newdoc.title = line_buffer;
	            strcpy(newdoc.title, "System Index"); /* name it */
	        } /* end else */
	    }  /* end if */
	    break;

	case 'x': 
	case 'X':  /* change form */
	    if(lynx_mode == FORMS_LYNX_MODE) {
		if(links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
		    curdoc.link += change_form_link(&links[curdoc.link],
				    FORM_UP, &newdoc, &refresh_screen);
		} else {
		    statusline("'X' can only toggle a form link");
		}
	    } else {
		statusline("'X' only toggles in forms mode");
	    }
	    break;	

	case 'z': 
	case 'Z':  /* change form */
	    if(lynx_mode==FORMS_LYNX_MODE) {
		if(links[curdoc.link].type == WWW_FORM_LINK_TYPE)
		    curdoc.link += change_form_link(&links[curdoc.link],
				    FORM_DOWN,&newdoc,&refresh_screen);
		else
		    statusline("'Z' can only toggle a form link");
	    } else {
		statusline("'Z' only toggles in forms mode");
	    }
	    break;	

	case 'm':	/* return to main screen */
	case 'M':
		/* if its already the startfile then don't reload it */
	    if(!STREQ(curdoc.address,startfile)) {
		
		statusline("Do you really want to go to the Main Menu? (y/n) [n] ");
		if(toupper(LYgetch())=='Y') {
	            newdoc.address = startfile;
		    newdoc.title = line_buffer;
                    strcpy(newdoc.title, "Entry into main menu");
	            highlight(OFF,curdoc.link); 
		}
	    } else {
		statusline("Already at main menu!");
		sleep(1);
	    }
	    break;

	case 'o':     /* options screen */
	case 'O':
	    options(); /* do the options stuff */
	    refresh_screen = TRUE; /* to repaint screen */
	    break;

	case 's': /* search for a user string */
	case 'S':
	     if(is_www_index) {
               /* perform a database search */

		/* do_www_search will try to go out and get the document
		 * if it returns yes a new document was returned and is
		 * named in the newdoc.address
		 */
		if(do_www_search(&newdoc.address)) {
                    push(&curdoc);
		
		   /* make the curdoc.address the newdoc.address so that
		    * getfile doesn't try to get the newdoc.address.
		    * Since we have already gotton it.
		    */ 
		   curdoc.address = newdoc.address;
		   curdoc.line = -1;
		   refresh_screen = TRUE; /* redisplay it */
		} else {
		   /* restore the old file */
		   newdoc.address = curdoc.address;  
		}
	     } else {
		statusline("Not a searchable indexed document -- press '/' to search for a text string");
		sleep(1);
	     }
	     break;

	case '/':  /* search within the document */
	case 'n':  /* search for the next occurance of the user string */
 	case 'N':
            /* user search */
	    if(toupper(c) != 'N')
	        *prev_target = '\0';  /* reset old target */
	    textsearch(&curdoc, prev_target); 
	    break;

	case 'r':
	case 'R':  /* reply by mail */
	case 'c':
	case 'C':
	   if(!owner_address) {
		statusline("No owner is defined for this file so you cannot send a comment");
		sleep(1);
	   } else {
		statusline("Do you wish to send a comment? [N]");
	       if(toupper(LYgetch()) == 'Y') {

	          if(is_url(owner_address) != MAILTO_URL_TYPE) { 
			/* the address is a url */
			/* just follow the link */
			
		       newdoc.address = owner_address;

	          } else {
		    /* the owner_address is a mailto: url type */
		       if(strchr(owner_address,':')!=NULL)
			    /* send a reply. The address is after the colon */
	      	         reply_by_mail(strchr(owner_address,':')+1,curdoc.address); 
		       else
	      	         reply_by_mail(owner_address,curdoc.address); 

	               refresh_screen=TRUE;  /* to force a showpage */
	          }
	       }
	   }
	   break;

	case 'e':  /* edit */
	case 'E':
	   if(*editor != '\0' && !anonymous) {
		if(edit_current_file(newdoc.address,curdoc.link,newline))
	            HTuncache_current_document();
		newdoc.address = curdoc.address;
	        curdoc.address = NULL;	
		newdoc.line = curdoc.line;
		newdoc.link = curdoc.link;

		refresh_screen=TRUE; /* to force a showpage */
	   } else if(*editor == '\0') {
		statusline("No editor is defined!");
		sleep(1);
	   }
	   break;

	case '=':  /* show document info */
	   /* show some info */
	   showinfo(&curdoc, lines_in_file, owner_address);
	   refresh_screen=TRUE;
	   break;

	case 'p':
	case 'P':  /* print the file */
		/* don't do if already viewing print options page */	
	   if(strcmp(curdoc.title,"Lynx Printing Options")) {	

		/* save the old file */
                print_options(&newdoc.address, lines_in_file);
	        refresh_screen=TRUE;  /* redisplay */
	   }
	   break;

	case 'a':  /* a to add link to bookmark file */
	case 'A':
	   if(strcmp(curdoc.title,HISTORY_PAGE_TITLE) && 
				strcmp(curdoc.title,PRINT_OPTIONS_TITLE)) {

		statusline("Do you wish to save this document in your current bookmark file? (y/n)");
		c = LYgetch();
		if(toupper(c) == 'Y')
		    save_bookmark_link(&curdoc);
	   } else {
		statusline("History and Print files cannot be saved in the bookmark page");
		sleep(1);
	   }
		
	   break;

#ifdef NOT_WORKING_YET
	case 'd':  /* delete home page link */
	case 'D':
	    if(strstr(newdoc.address,bookmark_page)) {
		rewind(fp);
		remove_bookmark_link(fp, newdoc.address, curdoc.link, newline);
	        refresh_screen=TRUE;  /* reparse and display file */
		*curdoc.address = '\0';  /* reopen file */
	    } else {
		statusline("D)elete only works on bookmark page links");
		sleep(1);
	    }
	    break;
#endif NOT_WORKING_YET

	case 'v':   /* v to view home page */
	case 'V':   /* this used to be 'g' for goto */
	    /* see if a home page exists
	     * if it does replace newdoc.address with it's name 
	     */
	    if(get_bookmark_filename(&newdoc.address)) {
		LYforce_HTML_mode = TRUE;  /* force HTML */
		LYforce_no_cache = TRUE;  /*force the document to be reloaded*/
		newdoc.title = line_buffer;
	        strcpy(newdoc.title, "Bookmark Page");
	    } 
	    break;

	case '!':  /* shell escape */
#ifdef VMS
	case '$':  /* VMS shell escape */
#endif VMS
	    if(!anonymous) {
	        stop_curses();
                signal(SIGINT, SIG_IGN);
#ifdef VMS
		printf("Spawning DCL subprocess.  Logout to return to Lynx.");
		fflush(stdout);
		lib$spawn();
#else
		printf(
		 "Spawning your default shell.  Use 'exit' to return to Lynx.\n");
	        fflush(stdout);
	        system(getenv("SHELL"));
#endif VMS
                signal (SIGINT, cleanup_sig);
	        start_curses();
	        refresh_screen=TRUE;  /* for a showpage */
	    }
	    break;

	default:
	    if (more)
		statusline(MOREHELP);
	    else
		statusline(HELP);
	    show_help = TRUE;

	    if(TRACE)
	        printw("%d", c);  /* show the user input */

	    break;
	}
    }
}

