
#include "other.h"
#include "user.h"
#include "libraries.h"
#include "messages.h"
#include "more.h"
#include <signal.h>

#include "savescr.h" // pshuang
#include "ctype.h" // pshuang
extern void sleep(unsigned int); // pshuang
int dont_save_screen = 0;

bool InteractWithLibrary =0;
  // pshuang: moved out of Interface class, now User::read can access

class Interface
{
  Barton     Lbarton;
  CARL       Lcarl;
  BU_Library Lbu;
  UC_MELVYL  Lmelvyl;
  Harvard    Lharvard;

  char* ShortLibraryName();

  User  *usr             =NULL;
  void   w(char*msg) { usr->write(msg); };

  Library   *crnt_library =NULL;
  bool       HaveLibrary ()    { return (bool) crnt_library; };
  bool       LibraryStopped ();
					 
  void Use_Library   (Library&);
  void Close_Library ();

  void WaitForEnter(char* prompt, bool enforce); // pshuang
/* pshuang; moved out of class Interface 6-14-91
  bool InteractWithLibrary =0;
*/
  bool Cmd_Mode =0;
  char pending_cmd_char =NULL;
  void PushbackCommandChar(char c) { pending_cmd_char = c; };

  enum { _NotIn, _HavePrefix, _HaveIdChar } cmd_state;
  char cmd_id_char =NULL;
  bool parse_cmd(char);

  void show_welcome();
  void show_menu(), sm_hlpr(Library&);
  void show_help();
  void attempt_refresh(void);

  void WaitForInput() {};
  char WaitForUserKey();

  void cleanup();
  void quit();
  bool quit_now =0;
 public:
   Interface() : cmd_state(_NotIn) {}
  ~Interface() { cleanup(); };
  void Service (User& u);
};

int gpid = 0;
void telnet_kill_child();

main (int argc, char* argv[] ) {
  signal(SIGINT,telnet_kill_child);
  signal(SIGTERM,telnet_kill_child);
  signal(SIGHUP,telnet_kill_child);
  signal(SIGQUIT,telnet_kill_child);
  User u = User(argv[1],NULL);
  Interface us;
  us.Service(u);
}

void Interface::quit ()
{
  // pshuang: added notification for the user

  usr->userWindow.standout();
  usr->write("\n\nYou have told the interface program to quit: goodbye...\n\n");
  usr->userWindow.standend();
  usr->flush();

  sleep(2);

  quit_now =1;
}

void Interface::cleanup()
{
  Lbarton.Close();
  Lcarl.Close();
  Lbu.Close();
  Lmelvyl.Close();
  Lharvard.Close();
}

bool Interface::LibraryStopped ()
{
  return (HaveLibrary() &&
	  !crnt_library->isStarting() &&
	  !crnt_library->isOperational());
}

char Interface::WaitForUserKey ()
{
  usr->flush();
  char c =NULL;
  while(1) {
    int res = usr->read(&c,1);
    if(res==1) break;
    if(res==-1) {
      if(errno==EWOULDBLOCK) { errno =0; continue; }
      else break;
    }
  }
  return c;
}

void Interface::Service(User& u)
{
  usr = &u;

/* pshuang: replaced
  update_messages(); // pshuang: reads from /mit/mitlibs/info/welcome.barton
                     // or uses internal default if mitlibs not attached
*/
  welcome_message=update_message("/mit/mitlibs/info/welcome.barton",welcome_message);

  show_welcome();
  Use_Library(Lbarton);

  int cnt;
  char c;
  while (1) {
    if(quit_now) return;
    WaitForInput();
    cnt = u.read(&c,1);

/* pshuang: rewrote so that it doesn't send an extra NL after a command
   which messes up my spurious input trapping by leavng cursor on wrong line
    if(cnt==1)
      {
	bool isCmd = parse_cmd(c);
	if(!isCmd && !Cmd_Mode
	   && HaveLibrary() && crnt_library->isOperational() ) {
	  if (c == '\n') usr->working_message();
	  crnt_library->write(&c,1);
	}
	else usr->write(&c,1);
      }
*/

    if (cnt==1)
      {
	bool isCmd = parse_cmd(c);
	if (HaveLibrary() && crnt_library->isOperational())
	  {
	    if (isCmd)
	      {
		/* nothing */
	      }
	    else
	      {
		if (c=='\n')
		  usr->working_message();
		crnt_library->write(&c,1);
	      }
	  }
	else usr->write(&c,1);

      }

    if(HaveLibrary())
      {

	if(InteractWithLibrary && !Cmd_Mode) {
/* pshuang: changed
	  cnt = crnt_library->read(scratchbuffer,1);
*/
	  cnt = crnt_library->read(scratchbuffer,16);
	  if(cnt>0) usr->write(scratchbuffer,cnt);
	  else usr->flush();
	}

	if(LibraryStopped()) {
	  Library *old_lib = crnt_library;
	  
	  usr->flush();
	  crnt_library->Describe_Failure(usr->os);
	  
	  bool busyness = crnt_library->wasBusy();
	  Close_Library();
	  
	  if(busyness) {
	    usr->write(redialing_message);
	    Use_Library(*old_lib);
	  }
	  else {
	    usr->write(library_closed_message);
	  }
	}
      }
  }
}

void Interface::Use_Library (Library& lib)
{
  if (HaveLibrary() && crnt_library!=&lib)
    Close_Library();

  crnt_library = &lib;

  bool vt = (lib.supports_vt100 && usr->isVT100) || !(lib.supports_hardcopy);

  /* pshuang: rewrote to make sure that all the silly variables like
     receivingBarton and receivingVT100 and VTBartonHack_enabled get set. */

  usr->Library_is_VT100(vt);

  if (&lib==&Lbarton)
    usr->Now_using_BARTON(1);
  else
    usr->Now_using_BARTON(0);

  usr->Library_is_VT100(vt);

  if (&lib==&Lbarton)
    usr->Now_using_BARTON(1);
  else
    usr->Now_using_BARTON(0);

  // pshuang: added
  usr->userWindow.standend();
  usr->write_ClearScreen();

  if (crnt_library->isNotOpen())
    crnt_library->Open(vt);

  InteractWithLibrary =1;

  attempt_refresh();
}

void Interface::Close_Library()
{
  InteractWithLibrary =0;

  if(HaveLibrary()) crnt_library->Close();
  crnt_library =NULL;

  if(usr->isVT100) usr->write("[0m"); // assure they are not in boldface.
}


bool Interface::parse_cmd(char x)
{
  // pshuang: added checking for "<non-alpha>END\n" in user input stream
  static History END_check_hist(6);
  static char input_string[2];
  switch (x)
    {
    case '\r':
    case '\n':
      input_string[0]='\n';
      break;
    default:
      input_string[0]=toupper(x);
    };	
  input_string[1]='\000';
  END_check_hist.Append(input_string,1);
  if (END_check_hist.Match("\nEND\n",1))
    {
      quit();
      return 1;
    };

  if(cmd_state==_NotIn && x!='\\') return 0;

/* pshuang: rewrote
  if(cmd_state==_NotIn && x=='\\') { cmd_state=_HavePrefix; return 0; }
*/

  if(cmd_state==_NotIn && x=='\\')
    {
      if (!dont_save_screen)
	usr->userWindow.saveScreen();
      cmd_state=_HavePrefix;
      return 0;
    }

  if(cmd_state==_HavePrefix) { cmd_state=_HaveIdChar; cmd_id_char =x; return 0; }
  if(cmd_state==_HaveIdChar) {
    if( x!='\n' && x!='\r') {
      if( cmd_id_char=='\\' ) { cmd_state=_HavePrefix; return 0; }
      else                   { cmd_state=_NotIn;      return 0; }
    }
    else  // Now probably a command!  Now have full <prefix><char><return>.
      {
	cmd_state =_NotIn;

	usr->userWindow.standend(); // pshuang: make sure bolding gets turned off

#define CLEANUP usr->write("  ",3) // pshuang: remove screen remnants

	switch(cmd_id_char) {
	case '\\':
	  show_menu();
	  attempt_refresh(); // pshuang: 6-21-91
	  CLEANUP;
	  break;
	case '?':
	case 'H':
	case 'h':
	  show_help();
	  attempt_refresh(); // pshuang: 6-21-91
	  CLEANUP;
	  break;
        /* pshuang: 6-21-91: added implementation of refresh-request option */
	case 'R':
	case 'r':
	  attempt_refresh();
	  CLEANUP;
	  break;
	case 'Q':
	case 'q':
	  quit();
	  return 1;
	case 'C':
	case 'c':
	  if(HaveLibrary()) w("\nClosed.\n");
	  else w("\nSorry, you must select a library before closing it.\n");
	  Close_Library();
	  usr->flush();
	  show_menu();
	  CLEANUP;
	  break;
	case 'V':
	case 'v':
/* pshuang: rewrote
	  usr->Person_is_VT100(1);
	  usr->write_ClearScreen();
	  usr->write("Using VT100 interface.\n");
	  usr->flush();
	  CLEANUP;
	  break;
*/
	  if (usr->status_Person_is_VT100)
	    ; /* do nothing, already using VT100 */
	  else
	    {
	      usr->Person_is_VT100(1);
	      attempt_refresh(); // flush stored screen
	      usr->write_ClearScreen();
	      usr->write("Now using VT 100 interface.\n");
	      sleep(1);
	      usr->write_ClearScreen();
	    }
	  break;
	case 'T':
	case 't':
/* pshuang: rewrote
	  usr->Person_is_VT100(0);
	  usr->write("\nUsing hardcopy interface.\n");
	  usr->flush();
	  CLEANUP;
	  break;
*/
	  if (usr->status_Person_is_VT100)
	    {
	      attempt_refresh(); // flushes stored screen
	      usr->flush();
	      usr->Person_is_VT100(0);
	      usr->write_ClearScreen();
	      usr->write("\nNow using hardcopy (tty) interface.\n\n");
	    }
	  else
	    ; /* already is hardcopy, do nothing */
	  break;
	case 'B':
	case 'b':
	  usr->VTBartonHack_Enable(!(usr->VTBartonHack_enabled));
	  w("\nVT100 BARTON customization turned ");
	  if(usr->VTBartonHack_enabled) w("ON"); else w("OFF");
	  w(".\n");
	  break;
	case '1':
	  Use_Library(Lbarton);
	  break;
	case '2':
	  Use_Library(Lcarl);
	  break;
	case '3':
	  Use_Library(Lharvard);
	  break;
	case '4':
	  Use_Library(Lbu);
	  break;
	case '5':
	  Use_Library(Lmelvyl);
	  break;
	default:
	  // oops, wasnt a command after all.
	  CLEANUP;
	  return 0;
	}
	// try to erase command from library input.
	if (HaveLibrary() && InteractWithLibrary)
	  crnt_library->write("",2);
	return 1;
      }
  }
}

char* Interface::ShortLibraryName()
{
  static char *blnk = "none";
  if(!crnt_library) return blnk;
  return crnt_library->HumanName;
}

// pshuang: simple routine to wait until user hits enter
void Interface::WaitForEnter(char* prompt, bool enforce)
{
  char c;
  dont_save_screen++;
  w(prompt);
  while (1)
    {
      c=WaitForUserKey();
      if (!enforce) parse_cmd(c);
      if ((c=='\n') || (c=='\r'))
	{
	  dont_save_screen--;
	  return;
	}
      if (!enforce)
	{
	  usr->userWindow.addch(c);
	  usr->userWindow.refresh();
	}
    }
}

void Interface::show_welcome ()
{
  usr->write_ClearScreen();
  w(welcome_message);
/* pshuang: changed to only accept NL or CR, not just any character
  WaitForUserKey();
*/
  WaitForEnter(" [07.24.1991 gamma version] ",1);
}


void Interface::show_menu ()
{
  InteractWithLibrary =0;

  usr->write_ClearScreen();

/* pshuang: 6-23-91: rewrote menu screen
  w("Commands:   (always valid) (Must type the two letters and then a ENTER!)\n");

  w("These are valid in any library (Must type backslash-letter and then <ENTER>).\n");

  w("    \\q  Quit.  (closes any remaining connections).\n");

  w(" \\? \\h  Help. \n");

  w("    \\c  Close current library (");
  w(ShortLibraryName());
  w(").\n");

  w("    \\\\  Show this menu.\n");

  w("  terminal types:\n");

  w("    \\v  VT100");
  if (usr->isVT100) w("*  <now using>");
  w("\n");

  w("    \\t  other");
  if(!usr->isVT100) w("*  <now using>");
  w("\n");

  w("    \\b    Customize VT100 when using BARTON. ");
  if(usr->VTBartonHack_enabled) w("<enabled>");
  else w("<disabled>");

  w(" (24lines,noBold,slower)\n");

  w("  libraries:\n");

  w("    \\1  BARTON (MIT).                                ");
  sm_hlpr(Lbarton);

  w("    \\2  Bos.Lib.Consortium Periodical Catalog (CARL) ");
  sm_hlpr(Lcarl);

  w("    \\3  Harvard University                           ");
  sm_hlpr(Lharvard);

  w("    \\4  Boston University                            ");
  sm_hlpr(Lbu);

  w("    \\5  MELVYL (CA USA)(large collection)            "); 
  sm_hlpr(Lmelvyl);

  w("\n");

*/


  w("MIT-based Internet library access program special commands (GENERAL)\n");
  w("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
  w("Following commands are valid even when interacting with a library.\n");
  w("You must type the backslash followed by the letter and then hit ENTER:\n");
  w("\n");
  w("    \\q  Quit interface program (will close any open library connections)\n");
  w(" \\?,\\h  Help\n");
  w("    \\c  Close the library currently being used");
    w(" <"); w(ShortLibraryName()); w(">\n");
  w("    \\\\  Show this menu (but you've figured that out already...)\n");
  w("  Terminal types:\n");
  w("    \\v  VT100");
    if (usr->isVT100) w(" <now being used>");
    w("\n");
  w("    \\t  Teletype, useful for hardcopy or capturing to disk");
    if (!usr->isVT100) w(" <now being used>");
    w("\n");
  w("    \\b  Customized VT100 when using MIT's BARTON system (default)");
    if (usr->VTBartonHack_enabled)
      w(" <enabled>\n");
    else w(" <disabled>\n");
  w("  Libraries available:\n");
  w("    \\1  BARTON (MIT)                                  ");
    sm_hlpr(Lbarton);
  w("    \\2  Bos.Lib.Consortium Periodical Catalog (CARL)  ");
    sm_hlpr(Lcarl);
  w("    \\3  Harvard University                            ");
    sm_hlpr(Lharvard);
  w("    \\4  Boston University                             ");
    sm_hlpr(Lbu);
  w("    \\5  Univ. of Calif. MELVYL                        ");
    sm_hlpr(Lmelvyl);
  w("\n");


/* pshuang: 6-27-91 replaced with below code
  if(crnt_library) {
    w("Pressing just ENTER will return you to ");
    w(ShortLibraryName());
    w(".\n");

    char c = WaitForUserKey();

    if( c != '\n' && c!= '\r' ) { Cmd_Mode =1; PushbackCommandChar(c); }
    else                        { Cmd_Mode =0; Use_Library(*crnt_library); }
*/

  WaitForEnter("Press <ENTER> to exit this menu, or issue a command now... ",0);
  InteractWithLibrary = 1;
}

void Interface::sm_hlpr (Library& l)
{
  if(!l.isStarting() &&
     !l.isOperational()) w("           ");
  if(l.isStarting())     w("<starting.>");
  if(l.isOperational())  w("#connected#");

  if(crnt_library==&l)   w(" <<< Current");
  w("\n");
}

/* pshuang: rewrote function
void Interface::attempt_refresh()
{
  usr->write_ClearScreen();
  if(HaveLibrary)
    {
      if (crnt_library->isOperational())
	{
	  if(crnt_library->supports_redraw)
	    crnt_library->redraw();
	  else
	    usr->write(cant_refresh_message);
	}
      else { w(""); }
    }
}
*/

void Interface::attempt_refresh(void)
{
  if (dont_save_screen) return;
  if (HaveLibrary)
    {
      if (crnt_library->isOperational())
	{
	  if (usr->userWindow.savedScreenAvailable())
	    usr->userWindow.restoreScreen();
	  else
	    {
	      if ((crnt_library->isOperational()) && (crnt_library->supports_redraw))
		{
		  usr->write_ClearScreen();
		  crnt_library->redraw();
		}
	      else usr->write(cant_refresh_message);
	    }
	}
      else
	; /* do nothing */
    }
  else
    usr->write("\nNo library is selected; cannot refresh.\n\n");
}

void Interface::show_help ()
{

/* pshuang: replaced with below code, doesn't seem that More works
  if(!HaveLibrary()) {
    More& hlp = More(general_help_message);
    hlp.serve(usr);
  }
  if(HaveLibrary())  {
    More& hlp = More (crnt_library->Help);
    hlp.serve(usr);
  }
*/

  InteractWithLibrary=0; // pshuang: disable my User::read hack

  usr->write_ClearScreen();
  if (!HaveLibrary())
    w(general_help_message);
  else
    w(crnt_library->Help);

  WaitForEnter("Press <ENTER> to exit this help screen, or issue a command now... ",0);
  InteractWithLibrary=1; // pshuang: re-enable my User::read hack

}
