/*  interface.c-- high-level interface functions and misc. utilities */
/* Copyright (C) 1996-1999,2001  Robert Masenten          */
/* This program may be redistributed under the terms of the
   GNU General Public License, version 2; see agility.h for details. */
/*                                                       */
/*                                                           */
/* This is part of the source for AGiliTy, the (Mostly) Universal  */
/*       AGT Interpreter                                           */

/* This module contains a miscellany of things that are somewhat */
/* system dependent but not enough so to justify being put in */
/* OS_<whatever>.c */
/* --writestr() and writeln().*/
/* --Hooks for sound, pictures, and fonts.  */
/* --yesno() and wait_return() */
/* --Some lower level file stuff */
/* --main() and command line parseing stuff */

#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>

#include "agility.h"
#include "interp.h"

#ifdef _DOS
#ifdef UNIX_IO
#undef UNIX_IO
#endif
#endif

#ifndef REPLACE_GETFILE
#ifdef UNIX_IO
#include <dirent.h>
#endif 
#endif


#ifdef UNIX
 /* Needed because we are compiling with ANSI set */
FILE *popen(const char*,const char*);
int pclose(FILE*);
#endif

#ifndef REPLACE_BNW

/* #define DEBUG_BELLS_AND_WHISTLES */

/* Warning for fontcmd, pictcmd, musiccmd:
  These all extract filenames from fontlist, pictlist, pixlist, songlist.
 Any of these are allowed to be NULL and this should be checked
 before accessing them.  */

#ifdef DEBUG_BELLS_AND_WHISTLES
void bnw_report(char *cmdstr,filename *list,int index)
{
  writeln("");
  writestr(">** ");writestr(cmdstr);writestr(" ");
  if (list!=NULL) {
    writestr(list[index]);
    writestr(" ");}
  writeln("**<");
}
#endif /* DEBUG_BELLS_AND_WHISTLES */

void fontcmd(int cmd,int font)
/* 0=Load font, name is fontlist[font]  
   1=Restore original (pre-startup) font  
   2=Set startup font. (<gamename>.FNT)
*/
{
#ifdef DEBUG_BELLS_AND_WHISTLES
  if (cmd==0) bnw_report("Loading Font",fontlist,font);
  else if (cmd==1) bnw_report("Restoring original font",NULL,0);
#endif
  return;
}

void pictcmd(int cmd,int pict)
/* 1=show global picture, name is pictlist[pict]
   2=show room picture, name is pixlist[pict]
   3=show startup picture <gamename>.P..  
  */
{
#ifdef DEBUG_BELLS_AND_WHISTLES
  if (cmd==1) bnw_report("Showing picture",pictlist,pict);
  else if (cmd==2) bnw_report("Showing pix",pixlist,pict);
  agt_waitkey();
#endif
   return;
}



int musiccmd(int cmd,int song)
/* For cmd=1 or 2, the name of the song is songlist[song]
  The other commands don't take an additional argument.
   1=play song 
   2=repeat song
   3=end repeat
   4=end song
   5=suspend song
   6=resume song
   7=clean-up
   8=turn sound on
   9=turn sound off
   -1=Is a song playing? (0=false, -1=true)
   -2=Is the sound on?  (0=false, -1=true)
*/
{
  if (cmd==8) sound_on=1;
  else if (cmd==9) sound_on=0;
#ifdef DEBUG_BELLS_AND_WHISTLES
  switch (cmd) {
     case 1:bnw_report("Play song",songlist,song);break;
     case 2:bnw_report("Repeat song",songlist,song);break;
     case 3:bnw_report("End repeat",NULL,0);break;
     case 4:bnw_report("End song",NULL,0);break;
     case 5:bnw_report("Suspend song",NULL,0);break;
     case 6:bnw_report("Resume song",NULL,0);break;
     case 7:bnw_report("Clean up",NULL,0);break;
     case 8:bnw_report("Sound On",NULL,0);break;
     case 9:bnw_report("Sound Off",NULL,0);break;
     case -1:return yesno("Is song playing?");
     case -2:return 1;
     }
#endif
  return 0;
}

#endif /* REPLACE_BNW */

static char linebuff[100];
static int lp;  /* Line pointer */
static rbool savenl=0;
static rbool needfill; /* Used for paragraph filling */
static rbool quotemode=0; 

#ifdef UNIX
static rbool ispipe[3]={0,0,0}; /* script, log_in, log_out */
#endif



void debugout(const char *s)
{
  int i;

  if (DEBUG_OUT) fputs(s,debugfile);
  else {
    lp=0;
    for(;*s!=0;s++) {
      if (curr_x+lp>=screen_width || lp>80) {
	if (lp+curr_x>=screen_width)
	  lp=screen_width-curr_x-1;
	linebuff[lp]=0;
	agt_puts(linebuff);
	agt_newline();
	lp=0;
      }
      if (*s=='\n') 
	{linebuff[lp]=0;
	 agt_puts(linebuff);
	 agt_newline();
	 lp=0;}
      else if (*s=='\t') 
	{for(i=0;i<3;i++) linebuff[lp++]=' ';}
      else if (*s>=0 && *s<=9) linebuff[lp++]=' ';
      else linebuff[lp++]=*s;
    }
    linebuff[lp]=0;
    agt_puts(linebuff);
  }
}


int close_pfile(genfile f, int ft)
     /* ft=0 for script, 4 for log_in, 5 for log_out */
{
#ifdef UNIX
  if (ft>=4) ft-=3;
  if (ispipe[ft]) {
    ispipe[ft]=0;
    pclose(f);
    return 0;
  }
#endif   
  if (ft==4)
    writeclose(f,NO_FILE_ID);
  else 
    readclose(f);
  return 0;
}



static char *get_log(void)
/* Read string from logfile_in */ 
{
  char *s;
  static int dead_log;

  if (!filevalid(log_in,fLOG)) {  /* We are finishing up */
    if (++dead_log>100) fatal("Internal error: LOG.");
    assert(BATCH_MODE);
    s=rmalloc(2);
    s[0]=' ';
    s[1]=0;
    return s;
  }

  s=rmalloc(1000);s[0]=' ';s[1]=0;
  textgets(log_in,s,1000);
  if (texteof(log_in)) {  /* Reached end of logfile */
    close_pfile(log_in,1);
    log_in=BAD_TEXTFILE;
    if (BATCH_MODE) {
      writeln("");
      writeln("ERROR: Unexpected end of log file.");
      agt_quit(); /* This doesn't actually quit; it just sets things
		     up so we *will* quit. */
      dead_log=0;
    } else {
      logflag&=~2;    
      fast_replay=0;
    }
  } else { /* Need to delay or wait for keypress */
    if (logdelay==-1) agt_waitkey();
    else agt_delay(logdelay);
    if (s[0]!=0) writeln(s);
  }
  return s;
}


static void put_log(const char *s)
/* Write s to logfile_out */
{
  textputs(log_out,s);
  if (s[strlen(s)-1]!='\n')
    textputs(log_out,"\n");
}


char *agt_readline(int in_type)
{
  char *s;

  if (PURE_INPUT) agt_textcolor(-1);
  if (logflag&2) 
    s=get_log();
  else  
    s=agt_input(in_type);
  if (PURE_INPUT) agt_textcolor(-2);

  if (logflag&1)
    put_log(s);
  
  return s;
}

char agt_getchar(void)
{
  char c, *s, buff[2];

  if (PURE_INPUT) agt_textcolor(-1);
  if (logflag&2) {
    s=get_log();
    c=s[0];
    rfree(s);
  } else 
    c=agt_getkey(1);
  if (PURE_INPUT) agt_textcolor(-2);
  if (logflag&1) {
    buff[0]=c;buff[1]=0;
    put_log(buff);
  }
  return c;
}

void agt_center(rbool b) 
/* 1=turn on text centering, 0=turn off */
/* At the moment, this is only used for game end messages */
/* When it is on, text output with writeln() should be 'centered'; */
/* it is up to the interface to decide what that means. */
/* writestr() should not be called while centering is on. */
{
  center_on=b;
}

void agt_par(rbool b) 
/* This has been added for the sake of anyone trying to get this to */
/*  work on less-than-80 column screens. My personal opinion is that this */
/* is probably hopeless; many AGT games assume 80-column format for   */
/* creating tables and ascii graphics. Nevertheless... */
/*  Text between an agt_par(1) and an agt_par(0) is logically connected */
/* (all part of one description) and so it *might* be possible to reformat */
/* it, treating multiple lines as being one paragraph. */ 
/*    At the very least, you should look for blank lines and indentation */
/* since a single section of text could contain multiple paragraphs. */
/* Sections of text _not_ between an agt_par(1) and an agt_par(0) should */
/* be treated as though each line were a new paragraph */
{
  par_fill_on=b;
  if (b==0 && savenl) agt_newline();
  savenl=0;needfill=0;
}

/* This handles the various format code. They all show up after
   '\r'; unrecogonized codes are just ignored */
static uchar xlat_format_code(uchar c)
{
  if (c==0xFF) {
    if (fix_ascii) return trans_ibm[0xFF-0x80];
    else return 0xFF;   
  }
  return 0;
}

#define FMT_CODE_CNT 15

static void run_format_code(uchar c)
{
  if (c<FMT_CODE_CNT) 
    agt_textcolor(c-3);
}

#define format_code(c) ((c>0 && c<=LAST_TEXTCODE)||((uchar)c==FORMAT_CODE))  

void writestr(const char *s)
{
  int i, j;
  char c;
  int endmark, old_x;

  if (savenl) {
    assert(par_fill_on);
    if (!isalnum(s[0])) agt_newline();
    else agt_puts(" "); 
    /* If combining two lines, insert a space between them. */
  }
  savenl=0;
  i=0;lp=0;

  while(s[i]!=0) {
    for(;s[i]!=0 && lp<90 && curr_x+lp<screen_width;i++)
      if (s[i]=='\t') 
	for(j=0;j<TAB_SIZE && curr_x+lp<screen_width;j++) linebuff[lp++]=' ';
      else if (format_code(s[i])) {linebuff[lp++]=' ';break;}  /* Color code */
      else if (s[i]=='\r') { /* New format code */
	if (s[i+1]==0) continue; /* Bogus format code */
	if ( ((uchar)s[i+1])<FMT_CODE_CNT) break;
	c=(char)xlat_format_code((uchar)s[++i]);
	if (c!=0) linebuff[lp++]=c;
      }
      else if (s[i]=='\n') { break;}
      else linebuff[lp++]=s[i];

    linebuff[lp]=0;

    /* Backtrack to last space; in case of formatting codes, we should
     already have one */
    endmark=lp;
    
    if (!isspace(s[i]) && !format_code(s[i]) && s[i]!=0)
      {/* If we aren't conveniently at a break...*/
	do {    /* Find last space */
	  endmark--;
	} while(endmark>0 && !isspace(linebuff[endmark]));
      }

    if (endmark==0 && !isspace(linebuff[endmark])) { /* Can't find a break */
      if (curr_x+lp<screen_width)  /* Not a line break */
	endmark=lp; /* Break at end; it doesn't matter that much */
      else  /* We _need_ a line break but are having trouble finding one */ 
	if (curr_x>0)  /* already stuff on this line printed previously */
	  endmark=0;  /* i.e. print out nothing; move it to next line */
	else   /* We have a single word that is longer than our line */
	  endmark=screen_width;   /* Give up */
    }

    c=linebuff[endmark];
    linebuff[endmark]=0;
    old_x=curr_x;

    agt_puts(linebuff);

    linebuff[endmark]=c;

    if (old_x+lp>=screen_width)
      /* Need to insert line break and skip any spaces */
      {
	if (!quotemode) agt_newline(); 
	else return; /* In quote mode, just truncate */

	/* Now set up beginning of next line: skip over whitespace */
	while(endmark<lp && isspace(linebuff[endmark])) 
	  endmark++;  /* Eliminate EOL whitespace */
	if (endmark==lp) {/* Nothing left; eliminate whitespace at beginning
			    of next line */
	  while(isspace(s[i]) && s[i]!='\r') i++;
	  lp=endmark=0;
	}
	needfill=1;
	if (endmark==lp && s[i]==0) {
	  needfill=2;
	  return; /* If only spaces left, don't print them */
	}
      }

    /* Now copy remaining text */
    for(j=0;endmark<lp;j++,endmark++) linebuff[j]=linebuff[endmark];
    lp=j;

    /* Now to deal with format codes */
    if ((unsigned char)s[i]==FORMAT_CODE) { 
      i++;
      if (bold_mode)  /* Translate as BOLD toggle */
	{
	  if (textbold)
	    agt_textcolor(-2);  /* Turn bold off */
	  else agt_textcolor(-1); /* Turn bold on */
	  textbold=!textbold;
	}
      else /* translate as BLACK */
	agt_textcolor(0);
    }
    else if (s[i]>0 && s[i]<=LAST_TEXTCODE) 
      agt_textcolor(s[i++]);
    else if (s[i]=='\r') {
      run_format_code((uchar)s[i+1]);
      i+=2;
    }
    else if (s[i]=='\n') {
      i+=1;
      agt_newline();
    }
  }
}



void writeln(const char *s)
{
  int i,pad;
  char *padstr;

  if (center_on && strlen(s)+curr_x<screen_width) {
    pad=(screen_width-strlen(s))/2;
    padstr=rmalloc((pad+1)*sizeof(char));
    for(i=0;i<pad;i++) padstr[i]=' ';
    padstr[i]=0;
    agt_puts(padstr);
    rfree(padstr);
  }
  writestr(s);
  /* needfill=2 if writestr ended with a line that wrapped only
     because of excess spaces (which have been discarded); needfill==1
     if writestr wrapped a line for any reason */
  /* If needfill==2, we've already issued a new-line, so don't issue a 
     second one. */
  /* If needfill==1, set savenl rather than wrapping (writestr will
     then decide to wrap or not depending on whether the next line starts
     with text or nontext), *unless* we are version magx, in which case
     the game author presumably knew what they were doing, so honor their
     wishes. */
  if (par_fill_on && needfill==1)
    if (aver>=AGX00) agt_newline();
    else savenl=1;
  else if (needfill!=2)
    agt_newline();
  needfill=0;
}
 

static char fixstatchar(uchar c) 
/* Eliminate formating characters in the status line */
{
  if (c=='\t' || c<=LAST_TEXTCODE || 
      (c==FORMAT_CODE) || c=='\r' || c=='\n')
    return ' ';
  return c;
}

void print_statline(void)
/* Use strings in l_stat and r_stat */
{
  int i,j;
  char *s, *t;
  static rbool lastline=0; /* Was a non-empty status line printed  last time? */

  s=rmalloc((status_width+1)*sizeof(char));

  /* If both strings empty, don't print the status line */
  if (l_stat[0]==0 && r_stat[0]==0 && !lastline) return;
  lastline=(l_stat[0] || r_stat[0]);

  i=status_width-strlen(l_stat)-strlen(r_stat);

  j=0;
  if (r_stat[0]==0) {/* Center the status line */
    while(j<i/2) s[j++]=' ';
    i-=j;
  } else if (i>6) {s[j++]=' ';i-=2;}  /* If statline is wide enough, put a 
				       space on each side */

  if (strlen(l_stat)<status_width)    /* Copy left side of status line into s*/
    for(t=l_stat;*t!=0;t++) s[j++]=fixstatchar(*t); 

  for(;i>0;i--) s[j++]=' ';  /* Insert space between left and right sides */

  if (j+strlen(r_stat)<=status_width) /*Copy right side into s */
    for(t=r_stat;*t!=0;t++) s[j++]=fixstatchar(*t);

  while(j<status_width) s[j++]=' '; /* Pad any extra width with spaces */
  s[j]=0;  /* Put end of string marker */
  agt_statline(s); /* Output it */
  rfree(s);
}


void padout(int padleng)
{
  int i;
  char *pstr;
  
  if (padleng<=0) return;
  pstr=rmalloc(padleng+1);
  for(i=0;i<padleng;i++) pstr[i]=' ';
  pstr[padleng]=0;
  writestr(pstr);
  free(pstr);
}

static int textwidth(char *s)
{
  int n;

  n=0;
  for(;*s!=0;s++) n+=(*s=='\t') ? TAB_SIZE : 1;
  return n;
}

void textbox(char *(txt[]),int len,unsigned long flags)
/* TB_TTL, TB_BOLD, TB_BORDER, TB_CENTER */
{
  int i, width, padwidth;
  int *linewidth;

  agt_textcolor(7);
  if (flags & TB_BOLD) agt_textcolor(-1);
  else agt_textcolor(-2);

  linewidth=rmalloc(len*sizeof(int));

  width=0;  /* This contains the maximum width of any line */
  for(i=0;i<len;i++) {
    linewidth[i]=textwidth(txt[i]);
    if (linewidth[i]>width) width=linewidth[i];
  }

  agt_makebox(width,len,flags&~(TB_BOLD|TB_CENTER));
  quotemode=1;  /* So newlines will cause truncation rather than a 
		   real newline */
  for(i=0;i<len;i++) {
    padwidth=width-linewidth[i]; /* Amount of padding we need */
    if (flags&TB_CENTER)
      {  padout(padwidth/2);padwidth-=padwidth/2;}
    writestr(txt[i]);
    padout(padwidth);
    if (i!=len-1) agt_qnewline();
  }
  agt_endbox();
  quotemode=0; /* Back to normal */

  agt_textcolor(7);
  textbold=0;
}


#ifndef REPLACE_MENU

int agt_menu(char *header,int size,int width,menuentry *menu)
/* This is _very_ minimal as it stands */
{
  int i,j;
  char sbuff[10];
  int numcol, colheight;

  if (size==0) return 0;

  width=width+5;
  numcol=screen_width/width;
  colheight=size/numcol;
  if (size%numcol!=0) colheight++;
      
  writeln(header);  
  for(i=0;i<colheight;i++) {
    for(j=0;j<numcol;j++) {
      if (j*colheight+i>=size) break;
      sprintf(sbuff,"%2d.",j*colheight+i+1);
      writestr(sbuff);
      writestr(menu[j*colheight+i]);
      if (j<numcol-1) padout(width-3-strlen(menu[j*colheight+i]));
    }
    writeln("");
  }
  do {
    writestr("Choice:");
    i=read_number()-1;
    if (i<0 || i>=size) 
      writeln("Please choose an option from the menu.");
  } while (i<0 || i>=size);
  return i;
}

#endif /* REPLACE_MENU */



void prompt_out(int n)
/* n=1 standard prompt
   n=2 question prompt */
{
  agt_textcolor(7);
  if (PURE_INPUT && n==1) agt_textcolor(-1);
  if (n==1) {
    agt_newline();
    gen_sysmsg(1,">",MSG_MAIN,NULL);
  }
  if (n==2) agt_puts("? ");
  agt_textcolor(7);
}

void agt_waitkey(void)
{
  if (BATCH_MODE || fast_replay)
    return;
  agt_getkey(0);
}


void wait_return(void)
{
  writeln("          --- HIT ANY KEY ---");
  agt_waitkey();
}


rbool yesno(const char *s)
/* True for yes, false for no. */
{
  char c;

  writestr(s);writestr(" ");
  c='y';
  do {
    if (c!='y') 
      writestr("Please answer <y>es or <n>o. ");
    c=tolower(agt_getchar());
  } while (c!='y' && c!='n' && !quitflag);
  return (c=='y');
}


void set_test_mode(fc_type fc)
{
  char *errstr;

  log_in=readopen(fc,fLOG,&errstr);

  if (make_test) {
    if (errstr==NULL)
      fatal("Log file already exists.");    
    log_out=writeopen(fc,fLOG,NULL,&errstr);
    if (errstr!=NULL)
      fatal("Couldn't create log file.");    
    logflag=1;
    return;
  }

  logdelay=0;
  if (errstr!=NULL)
    fatal("Couldn't open log file.");
  logflag=2;

  script_on=1;
  scriptfile=writeopen(fc,fSCR,NULL,&errstr);
  if (errstr!=NULL)
    fatal("Couldn't open script file.");
}


#ifndef REPLACE_GETFILE


#ifdef UNIX_IO

extern const char *extname[]; /* From filename.c */

static rbool check_fname(char *name, filetype ext)
{
  return 0==strcmp(name+strlen(name)-strlen(extname[ext]),extname[ext]);
}


static void list_files(char *type, filetype ext)
{
  DIR *currdir;
  struct dirent *entry;
  char **filelist;
  int filecnt, listsize;
  int maxleng; /* Longest filename; used for formatting */
  int i,j;
  int numcols, height;
  
  filelist=NULL; filecnt=listsize=0; maxleng=0;
  currdir=opendir(".");
  if (currdir==NULL) return; /* Nothing we can do except give up */
  do {
    entry=readdir(currdir);
    if ( entry!=NULL && check_fname(entry->d_name,ext) ) {
      /* It has the right extension; add it to our list of files */
      if (filecnt>=listsize) {
	listsize+=5;
	filelist=rrealloc(filelist,listsize*sizeof(char*));
      }
      filelist[filecnt]=rstrdup(entry->d_name);      
      i=strlen(entry->d_name);
      if (i>screen_width-1) {
	filelist[filecnt][screen_width-1]=0;
	i=screen_width-1;
      }
      if (i>maxleng) maxleng=i;
      filecnt++;
    }
  } while (entry!=NULL);
  closedir(currdir);
  if (filecnt==0) return; /* No files */
  
  numcols=(screen_width-1)/(maxleng+2); /* Two spaces between columns */
  if (numcols<1)
    numcols=1;
  height=(filecnt+numcols-1)/numcols; /* Height, rounded up. */

  writeln("");
  writestr("Existing ");writestr(type);writestr("files:");
  for(i=0;i<height;i++) {
    writeln("");
    for(j=0;j<numcols;j++) 
      if (i+j*height < filecnt) {
	if (maxleng+2<=screen_width-1) writestr("  ");
	writestr(filelist[i+j*height]);
	padout(maxleng-strlen(filelist[i+j*height]));
	rfree(filelist[i+j*height]);
    }
  }
  writeln("");
  rfree(filelist);
}
#endif /* UNIX_IO */




/* This opens the file refered to by fname and returns it */
static genfile uf_open(fc_type fc, filetype ext, rbool rw)
{
  char *errstr;
  genfile f;

  if (rw) { /* Check to see if we are overwriting... */
#ifdef UNIX
    if (fc->special) 
      f=writeopen(fc,ext,NULL,&errstr);
    else 
#endif
      {
	if (fileexist(fc,ext) && ext!=fSCR) {
	  if (!yesno("This file already exists; overwrite?")) 
	    /* That is, DON'T overwrite */
	    return badfile(ext);
	}
	f=writeopen(fc,ext,NULL,&errstr);
      }
  } else 
    f=readopen(fc,ext,&errstr);
  if (errstr!=NULL) writeln(errstr);
  return f;
}

static fc_type last_save=NULL;
static fc_type last_log=NULL;
static fc_type last_script=NULL;


genfile get_user_file(int ft)
/* ft= 0:script, 1:save 2:restore, 3:log(read) 4:log(write)  */
/* Should return file in open state, ready to be read or written to,
   as the case may be */
{
  /* int extlen;*/
  rbool rw;  /* True if writing, false if reading */
  filetype ext;
  genfile fd;
  fc_type def_fc, fc;
  char *fname;
  char *ftype;
  char *p,*q;

  switch (ft) {
    case 0: ftype="script ";
      def_fc=last_script;
      rw=1;ext=fSCR; break;
    case 1: ftype="save ";
      def_fc=last_save;
      rw=1;ext=fSAV;break;
    case 2: ftype="restore ";
      def_fc=last_save;
      rw=0;ext=fSAV;break;
    case 3: ftype="log ";
      def_fc=last_log;
      rw=0;ext=fLOG;break;
    case 4: ftype="log ";
      def_fc=last_log;
      rw=1;ext=fLOG;break;
    default: writeln("<INTERNAL ERROR: invalid file type>");
      return badfile(fSAV);
    }
#ifdef UNIX_IO
  if (!rw) { /* List available files. */
    list_files(ftype,ext);
    ftype=NULL;
  } else
#else
    writestr(" ");
#endif
  writestr("Enter ");
  if (ftype!=NULL) writestr(ftype);
  writestr("file name");
  if (def_fc!=NULL) {
    char *s;
    s=formal_name(def_fc,ext);
    writestr(" (");
    writestr(s);
    writestr(")");
    rfree(s);
  }
  writestr(": ");

  if (PURE_INPUT) agt_textcolor(-1);
  fname=agt_input(4);
  if (PURE_INPUT) agt_textcolor(-2);

  /* Delete whitespace before and after the file name. */
  for(p=fname;isspace(*p);p++);
  if (*p==0) { /* Line is all whitespace; use default if there is one */
    if (def_fc==NULL) {
      writeln("Never mind.");
      rfree(fname);
      return badfile(ext);}
    else {
      rfree(fname);
      fc=def_fc;
    }
  } else {   /* Line is _not_ all whitespace: we have a file name */
    for(q=fname;*p!=0;p++,q++) 
      *q=*p;
    q--;
    while(isspace(*q)) q--;
    q++;
    *q=0;
    fc=init_file_context(fname,ext);
  } 

  fd=uf_open(fc,ext,rw);

  if (!filevalid(fd,ext)) {
    if (fc!=def_fc) release_file_context(&fc);
    return fd;
  }

  switch(ft) 
    {
    case 0: last_script=fc;break;
    case 1: last_save=fc; break;
    case 2: last_save=fc; break;
    case 3: last_log=fc; break;
    case 4: last_log=fc; break;
    }
  if (fc!=def_fc) release_file_context(&def_fc);
  return fd;
}


void set_default_filenames(fc_type fc)
{
  last_save=convert_file_context(fc,fSAV,NULL);
  last_log=convert_file_context(fc,fLOG,NULL);
  last_script=convert_file_context(fc,fSCR,NULL);
}



#endif  /* REPLACE_GETFILE */




void script(uchar onp)
{
  if (onp==script_on)
    if (onp==0) writeln("Scripting wasn't on.");
    else writeln("Scripting is already on.");
  else if (onp==1) {
    scriptfile=get_user_file(0);
    if (filevalid(scriptfile,fSCR)) script_on=1;
  } else if (filevalid(scriptfile,fSCR)) {    
    if (close_pfile(scriptfile,0))
      rprintf("Script file: %s",strerror(errno));
    scriptfile=BAD_TEXTFILE;
    script_on=0;
  }
}


void logon(void)
{
  if (logflag&1) {
    writeln("Already logging");
    return;}
  log_out=get_user_file(4);
  if (filevalid(log_out,fLOG))
    logflag|=1;
}

void replay(int delay)
{
  if (logflag&2) return; /* Nested replays are meaningless */
  log_in=get_user_file(3);
  if (filevalid(log_in,fLOG)) {
    logflag|=2;
    logdelay=delay;
  }
}


/* These two are intended to be called by the platform-dependent
   interface (e.g. if the user had chosen these from some general purpose
   menu) */
/* They're never called from the rest of the code */

void agt_save(void)
{
  savegame();
}

void agt_restore(void)
{
  doing_restore=1;
}

void agt_restart(void)
{
  doing_restore=2;
}

void agt_quit(void)
{
  doing_restore=4;
}


/* This should be rmalloc'd */
static fc_type newgame_fc;

fc_type new_game(void)
{
  return newgame_fc;
}

void agt_newgame(fc_type fc)
{
  newgame_fc=fc;
  doing_restore=3;
}

static rbool end_cmd_options;

void set_default_options(void)
{
  init_flags();
  flag=rmalloc(sizeof(rbool));
  debug_parse=0;
  DEBUG_AGT_CMD=0;
  DEBUG_EXEC_VERB=0;
  DEBUG_DISAMBIG=0;
  DEBUG_SMSG=0;
}

void helpmsg(void)
{
  printf("AGiliTy: "
	 "The (Mostly) Universal AGT Interpreter, ");
  printf("%s\n",version_str);
  printf("  Copyright (C) 1996-1999,2001 Robert Masenten\n");
  printf("[%s]\n",portstr);
  printf("Syntax: agil <options> <game name>\n");
  printf("Options:\n");
  printf("(all of these can be turned off by putting a - after the option)\n");
  printf(" -i Try to use IBM character set.\n");
  printf(" -1 IRUN Mode: Print messages in first person\n");
  printf(" -h Print out this message\n");
  printf(" -d Debug metacommand execution\n");
  printf(" -t Test mode; see accompanying documentation. Implies -r.\n");
  printf(" -c Create test file.\n");
  printf(" -m Force descriptions to be loaded from disk.\n");
#ifdef OPEN_AS_TEXT
  printf(" -b Open data files as binary files.\n");
#endif
  printf("\nTechnical options (intended for debugging AGiliTy itself).\n");
  printf(" -p Debug parser\n");
  printf(" -x Debug verb execution loop\n");
  printf(" -a Debug disambiguation system\n");
  printf(" -s Debug STANDARD message handler\n");
#ifdef MEM_INFO
  printf(" -M Debug memory allocation\n");
#endif
}


static rbool setarg(char **optptr)
{
  if ( (*optptr)[1]=='+') {(*optptr)++;return 1;}
  if ( (*optptr)[1]=='-') {(*optptr)++;return 0;}
  return 1;
}

#ifdef UNIX
#define fixcase(c) (c)
#else
#define fixcase(c) tolower(c)
#endif

void parse_options(char *opt,char *next)
{
  if (opt[0]=='-' && opt[1]==0)   /* -- */
    {end_cmd_options=1;return;}
  for(;*opt!=0;opt++)
    switch(fixcase(*opt)) 
      {
      case '?': case 'h':
	helpmsg();
	exit(EXIT_SUCCESS);
      case 'p': debug_parse=setarg(&opt);break; 
      case 'a': DEBUG_DISAMBIG=setarg(&opt);break;
      case 'd': DEBUG_AGT_CMD=setarg(&opt);break;
      case 'x':DEBUG_EXEC_VERB=setarg(&opt);break;
      case 's':DEBUG_SMSG=setarg(&opt);break;
#ifdef MEM_INFO
      case 'M': DEBUG_MEM=setarg(&opt);break;
#endif
      case 'm': descr_maxmem=0; break;
      case 'i': fix_ascii_flag=!setarg(&opt);break;
      case 't': BATCH_MODE=setarg(&opt); break;
      case 'c': make_test=setarg(&opt); break;
      case '1': irun_mode=setarg(&opt);break;
#ifdef OPEN_FILE_AS_TEXT
      case 'b': open_as_binary=setarg(&opt);break;
#endif
      default:printf("Do not recognize option %c\n",*opt);
	helpmsg();
	exit(EXIT_FAILURE);
      }
}


#ifndef REPLACE_MAIN

#ifdef MSDOS
extern rbool use_bios;
#endif


int main(int argc,char *argv[])
{
  int i;
  char *gamefile; 
#ifdef MSDOS
  rbool biosvar=0;
#endif

  set_default_options();
  end_cmd_options=0;
  gamefile=NULL;
  for(i=1;i<argc;i++)
    if (argv[i][0]=='-' && !end_cmd_options)
      parse_options(argv[i]+1,argv[i+1]);
#ifdef MSDOS  /* For backward compatibility w/ original AGT interpreters */
    else if (argv[i][0]=='/' && tolower(argv[i][1])=='b' 
	     && argv[i][2]==0)
      biosvar=1;
#endif
    else if (gamefile==NULL)
      gamefile=argv[i];
    else fatal("Please specify only one game\n");
  if (gamefile==NULL)  {
    helpmsg();
    exit(EXIT_FAILURE);}

  init_interface(argc,argv);
 /* From this point on, MUST use writestr/writeln or may
    cause problems w/ the interfaces on some platforms
    that have to keep track of cursor position */
#ifdef MSDOS  
  use_bios=biosvar;
#endif
  run_game(init_file_context(gamefile,fDA1));
  return EXIT_SUCCESS;
}

#endif /* REPLACE_MAIN */


