
#include "library.h"

#include "telnet.h"
#include "libraries.h"
#include "messages.h"


// ---- Messages ----

char*      Library_default_Dropped_message =

  "The library either has disconnected itself, or has not responded recently.
You might try reconnecting.";

char*      Library_default_Busy_message =

  "The library refused our connection request.
This _may_ be a short term condition (such as all lines busy),
so you might try again.";

char*      Library_default_Down_message =

  "It was not posible to connect to the library.
Perhaps the library computer is down or unreachable.";

char*      Library_default_Bug_message = 
  "Sorry.  A bug just occured in this program.
Perhaps you might try again in a little while.";

char*      Library_default_Help_message = 
  "No local help available about this library.
You could try HELP, ?, HELP HELP, etc.
";

char*      Library_default_Banner_message =
  "";


// ----------


Library::Library ()
     : state(_Closed), result(_none),

       nl("\n"), cr("\r"), bs("\b"), del(""),
       strRedraw(""), supports_redraw(0),

       number_of_logon_steps (0),
       strLogoff(""),
       response_timeout (60),

       HumanName("BugOccurred"),
       HostName ("ProgramBug"),
       HostNumber("ProgramBug"),

       use_vt100_if_available(0),
       supports_vt100(0),
       supports_hardcopy(0),

       strDropped(Library_default_Dropped_message),
       strBusy   (Library_default_Busy_message),
       strDown   (Library_default_Down_message),
       strBug    (Library_default_Bug_message),
       strElaboration (""),

       strBanner (Library_default_Banner_message),
       Help      (Library_default_Help_message)

{
  ohist = new History(10);
}
 Library::~Library ()
{
  Close();
  delete ohist;
}

bool Library::isStarting()   { state_update(); return state ==_SetUp || state==_Logon;}
bool Library::isOperational(){ state_update(); return state ==_Operate;}
bool Library::wasDropped()   { state_update(); return result==_Dropped;}
bool Library::wasBusy()      { state_update(); return result==_Busy;}
bool Library::wasDown()      { state_update(); return result==_Down;}
bool Library::hadBug()       { state_update(); return result==_Bug;}

void Library::Open (bool vt100p)
{
  Close();
  use_vt100_if_available = vt100p;
  tn = new Telnet(&HostName[0],response_timeout);
  state = _SetUp;
  result= _none;
}
void Library::Close ()
{
  if(state==_Operate && result==_none) Send_Logoff();
  if(tn!=NULL) delete tn;
  state = _Closed;
  tn = NULL;
}
void Library::redraw()
{
  write(strRedraw);
}

int Library::theReadFileDescriptor()
{
  if(tn) return tn->theReadFileDescriptor();
  else   return -1;
}

int Library::read (char* buf, int nchars)
{
  if(!tn) return -1;
  int res =  tn->read(buf,nchars);
  if(res>0) ohist->Append(buf,res);
  return res;
}

int Library::write (char* buf, int nchars)
{
  if(!tn) return -1;
  if(nchars > 1) return IncrementalWrite(buf,nchars);

  if(nchars < 1) return 0;
  int res;
  switch(*buf) {
  case '': res = 1; break;
  case '': res = 1; tn->write(del); break;
  case '': res = 1; tn->write(bs);  break;
  case '\n': res = 1; tn->write(nl);  break;
  case '\r': res = 1; tn->write(cr);  break;
  default:   res = tn->write(buf,1);
  }
  return res;
}
int Library::IncrementalWrite (char* buf, int nchars)
{
  int ccount =0;
  for(int i=0;i<nchars;i++) {
    int res = write(&buf[i],1);
    if(res==-1) return -1;
    if(res!=1)  return -1;
    ccount += res;
  }
  return ccount;
}

void Library::state_update ()
{
  if(!tn) return;

  if(state==_SetUp   && tn->isConnecting() ) return;
  if(state==_SetUp   && tn->isConnected()  ) {
    debug("Library: connected.");
    state = _Logon;
    if( LoggingOn(1) ) state=_Operate;
    return;
  }
  if(state==_Logon   && tn->isConnected()  ) {
    if( LoggingOn(0) ) state=_Operate;
    return;
  }
  if(state==_Operate && tn->isConnected()  ) return;

  // Terminating conditions.

  debug("Library: terminal condition.");
  if(tn->wasClosed() || tn->wasNotResponding())
    result = _Dropped;
  if(tn->wasUnknown() || tn->wasUnable() || tn->wasTimedOut())
    result = _Down;
  if(tn->wasRefused())
    result = _Busy;
  if(tn->hadBug())
    result = _Bug;

  if(result!=_none) Close();
}

void Library::Describe_Failure (ostream& o)
{
  o << "\n\n=======\n";
  if(wasDropped()) o << strDropped;
  if(wasBusy())    o << strBusy;
  if(wasDown())    o << strDown;
  if(hadBug())     o << strBug;
  o << strElaboration;
  o << "\n=======\n\n";
  o.flush();
}

int  Library::LoggingOn   (bool just_now_connected) 
{
  if(just_now_connected)  logon_step =0;

  if(logon_step>=number_of_logon_steps) {
    debug("Logon sequence complete.");
    return 1;
  }

  if(ohist->Match(LogonSequence[logon_step].Trigger,3))
    {
      debug("Trigger Match");
      sleep(LogonSequence[logon_step].Delay);

      write(LogonSequence[logon_step].Action);

      if(use_vt100_if_available && supports_vt100)
	write(LogonSequence[logon_step].vtAction);
      else
	write(LogonSequence[logon_step].hdAction);

      debug("Actions sent");
      if(tn) tn->response_expected();
      logon_step++;
    }

  // drive Library::ohist and telnet timeout mechanism.
/*
  char c;
  if(tn) if(-1==read(&c,1)) return 1;
*/

  return 0;
}

void Library::Send_Logoff()
{
  debug("Sending Logoff");
  write(strLogoff);
}
