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

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 ();
					 
  bool InteractWithLibrary =0;
  void Use_Library   (Library&);
  void Close_Library ();

  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 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 ()
{
  usr->write("\nGoodbye...\n");
  usr->flush();
  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;
  
  update_messages();

  show_welcome();
  Use_Library(Lbarton);

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

    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(HaveLibrary())
      {

	if(InteractWithLibrary && !Cmd_Mode) {
	  cnt = crnt_library->read(scratchbuffer,1);
	  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);
  usr->Library_is_VT100(vt);
  if(crnt_library->isNotOpen()) crnt_library->Open(vt);

  InteractWithLibrary =1;


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

  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)
{
  if(cmd_state==_NotIn && x!='\\') return 0;
  if(cmd_state==_NotIn && x=='\\') { 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;

	switch(cmd_id_char) {
	case '\\': show_menu(); break;
	case '?':
	case 'H':
	case 'h': show_help(); 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();
	  break;
	case 'V':
	case 'v': usr->Person_is_VT100(1);
	  usr->write_ClearScreen();
	  usr->write("Using VT100 interface.\n");
	  usr->flush();
	  break;
	case 'T':
	case 't': usr->Person_is_VT100(0);
	  usr->write("\nUsing hardcopy interface.\n");
	  usr->flush();
	  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.
	  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;
}


void Interface::show_welcome ()
{
  usr->write_ClearScreen();
  w(welcome_message);
  WaitForUserKey();
}


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

  usr->write_ClearScreen();

  w("Commands:   (always valid) (Must type the two letters and then a 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");

  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); }
  }
}
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");
}

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::show_help ()
{
  if(!HaveLibrary()) {
    More& hlp = More(general_help_message);
    hlp.serve(usr);
  }
  if(HaveLibrary())  {
    More& hlp = More (crnt_library->Help);
    hlp.serve(usr);
  }
}
