// HSapi.js
//
// General utilities for HyperScript/DOM interface
//
// To initialize, call initHS();
//
// Author: Mike Bergsma
//
// Modifications:
//
//    $Log: HSapi.js,v $
//    Revision 1.30  2008-06-25 17:28:12  bergsma
//    no message
//
//    Revision 1.29  2008-06-11 08:48:10  bergsma
//    *** empty log message ***
//
//    Revision 1.28  2008-06-09 02:36:57  bergsma
//    no message
//
//    Revision 1.27  2008-06-08 21:23:25  bergsma
//    no message
//
//    Revision 1.25  2008-06-06 20:20:19  bergsma
//    no message
//
//    Revision 1.24  2008-06-06 00:46:45  bergsma
//    no message
//
//    Revision 1.22  2008-06-03 04:42:05  bergsma
//    no message
//
//    Revision 1.21  2008-06-01 23:26:04  bergsma
//    no message
//
//    Revision 1.20  2008-05-24 18:45:18  bergsma
//    no message
//
//    Revision 1.18  2008-05-09 18:28:19  bergsma
//    Find HYP files in /local/run/dom
//    Add back support for activeX
//
//    Revision 1.17  2008-04-25 17:51:29  bergsma
//    no message
//
//
//

// GLOBAL VARIABLES
var isBootStrapped = false ;
var isBootStrapping = false ;
var isLoaded = false ;
var isLoading = false ;
var ab = null ;
var isJava = 0 ;
var isActiveX = 0 ;
var maxBoots = 3 ;
var maxLoads = 6 ;
var bootIntervalID = null ;
var loadIntervalID = null ;
var nodeName = "" ;
var server = "" ;
var portnum = "" ;
var target = "" ; 
var deployMethod="java";

function initHSapi()
{
  return ;
}

///////////////////////////////////////////////////////////////////
//
// BOOTSTRAP SECTION
//
///////////////////////////////////////////////////////////////////

// FUNCTION TO SET THE HANDLE TO HYPERSCRIPT
function setAb( fn )
{
  if ( ab != null ) return ab ;
  ab = document.HyperScript2 ;
  if ( ab == null ) {
    var e = fn+ ": No HyperScript for deploy method '"+deployMethod+"'" ;
    debug( e, 1 ) ;
    alert ( e ) ; 
  }
  return ab ;
}

// BOOTSTRAP_HYPERSCRIPT
// 
// It can take time for the object (JAVA or ACTIVEX) to be ready to
// load in the hyperscript bootstrap code.
// We must go through a timer wait until the object is ready.

function bootstrap_hyperscript ( dm )
{
  debug( 'Bootstrapping hyperscript...', 1 ) ;

  deployMethod = dm ;

  var x = new Array() ;
  x = location.host.split ( ":" ) ;
  server = x[0] ;
  portnum = location.port ;
  if ( portnum == "" ) portnum = 80 ;

  isJava = ( deployMethod == "java" ) ;
  isActiveX = ( deployMethod == "activex" );

  // We always invoke bootstrap under a repeating timer.  Try every second.
  if ( bootIntervalID != null ) window.clearInterval(bootIntervalID) ;
  bootIntervalID = window.setInterval("bootstrap2()",1000) ;
}

function bootstrap2 ( )
{
  debug("bootstrap2",3);

  // Don't try more than maxBoot times
  maxBoots-- ;

  // If there are no objects loaded yet, return and
  // wait for the timer to go off again.

  // For the ActiveX case, there'll be 2 objects.
  debug("Applets = "+document.applets.length,3);
  if ( document.applets.length == 0 && maxBoots > 0 ) return ;

  // We think we've got HyperScript. Cancel the timer.
  if ( bootIntervalID != null ) {
    window.clearInterval ( bootIntervalID ) ;
    //bootIntervalID = null ;
  }
  
  // If we exceeded our timer, then fail. 
  if ( maxBoots <= 0 ) {
    var e = "Failed to bootstrap HyperScript2"  ;
    debug( e, 1 ) ;
    alert ( e ) ;
    if ( loadIntervalID != null ) {
      window.clearInterval ( loadIntervalID ) ;
      //loadIntervalID = null ;
    }
    return ;
  }

  // Otherwise, onto booting.
  bootstrap3() ;
}

function bootstrap3()
{
  debug ( "Bootstrap3 "+deployMethod, 3 ) ;

  // Make sure some other thread isn't doing the work.
  if ( isBootStrapping ) return ;

  // Set flag to indicate we are booting
  isBootStrapping = true ;

  openconsole();
  
  var httpRequest = new HttpXMLRequest( ) ;
  httpRequest.loadXMLDocSync( "GET","/local/run/dom/bootstrap.hyp","",5000) ;
  var s = httpRequest.responseText() ;

  // Locate the HyperScript object
  if ( setAb("Bootstrap") == null ) return ;

  // Load the bootstrap part of the HyperScript program
  debug("Parsing bootstrap.hyp",3);
  var ret = ab.Parse( s ) ;
  if ( typeof(ret) != "undefined" && ret != "$ACK" ) {
    var e = "Bootstrap: Failed to parse hyperscript bootstrap.hyp, reason is " + ret ;
    debug( e, 1 ) ;
    alert ( e );
    return ;
  }

  // Decide which loopback to use for communication
  // between JAVA and HYPERSCRIPT
  // We choose a random localhost port
  debug("Defining loopback port",3);
  var a = Math.round(Math.random() * 124) ;
  var b = Math.round(Math.random() * 124) ;
  var c = Math.round(Math.random() * 124) ;
  var loopbackAddr = 127 + "." + a + "." + b + "." + c ;
  var loopbackPort = 8088 ;

  // Put HS in idle mode, awaiting requests.
  var bootstrap = 'BOOTSTRAP("' + 
			loopbackAddr + '",' +
			loopbackPort + '); idle();' ;

  debug (bootstrap,3 );
  ret = ab.Parse ( bootstrap ) ;
   if ( typeof(ret) != "undefined" && ret != "$ACK" ) {
    var e = "Bootstrap: Failed to idle(), reason is " + ret ;
    debug( e, 1 ) ;
    alert ( e );
    return ;
  }

  // ActiveX uses the IE mailbox to send requests to 
  // HyperScript.  JAVA uses a loopback connection,
  // which must be established first.
  if ( isJava ) {
    var ret = ab.openSocketStream(loopbackAddr,loopbackPort);
    if ( ret != "$ACK" ) {
      var e = "Failed to connect hyperscript, reason is " + ret ;
      debug( e, 1 ) ;
      alert ( e );
    }
  }

  debug('Bootstrap completed', 1 ) ;

  // Set flag to indicate we are done bootstrapping
  isBootStrapping = false ;
  isBootStrapped = true ;

  if ( !isLoading ) {
    // For netscape / mozilla / firefox, the load_brains() may never
    // get underway, so we give it a little shove.
    //alert ( "Load brains not happening, kicking");
    if ( loadIntervalID != null ) window.clearInterval(loadIntervalID) ;
    loadIntervalID = window.setInterval("load_brains2()",1000) ;
  }
}

// LOAD_BRAINS - do this after BOOTSTRAP!!
function load_brains( nn, dm )
{
  debug ( "load_brains "+nn+", "+dm, 3);

  nodeName = nn ;
  target = "/local/run/dom/" + nodeName + ".hyp" ;  

  // If we've loaded once before, skip the rest and just go direct.
  if ( isLoaded ) return load_brains3() ;

  if ( !isBootStrapped ) {
    // Not bootstrapped yet.  Do that first, then load_brains2() will be called
    bootstrap_hyperscript( dm ) ;
    return ;
  }
  // We always invoke load_brains under a repeating timer. 
  // Try every 1 second, give time for bootstrapping to complete.
  if ( loadIntervalID != null ) window.clearInterval(loadIntervalID) ;
  loadIntervalID = window.setInterval("load_brains2()",1000) ;
}

function load_brains2 () 
{
  debug("load_brains2",3);

  // Don't try more than maxBoot times
  maxLoads-- ;

  // If we have not completed bootstrapping,
  // then we cannot proceed
  if ( !isBootStrapped ) {
    // We can wait a bit more
    if ( maxLoads > 0 ) return ;
  }
   
  // We might be ok, cancel timer 
  if ( loadIntervalID != null ) {
    window.clearInterval ( loadIntervalID ) ;
    //loadIntervalID = null ;
  }

  if ( maxLoads <= 0 ) {
    var e = "Failed to load brains for HyperScript2 " ;
    debug( e, 1 ) ;
    alert ( e ) ;
    return ;
  }

  // Do the real loading of brains
  load_brains3() ;
}

function load_brains3()
{
  // Make sure some other thread isn't doing the work.
  if ( isLoading ) return ;

  debug ( "load_brains3",3 ) ;

  if ( !isBootStrapped ) {
    // In case we have not bootstrapped.
    var e = "Bootstrap not happening, kicking" ;
    debug( e, 1 ) ;
    alert ( e ) ;
    isBootStrapping = false ;
    if ( bootIntervalID != null ) window.clearInterval(bootIntervalID) ;
    bootIntervalID = window.setInterval("bootstrap2()",1000) ;
    return ;
  }

  // Now we load 
  isLoading = true ;

  debug('Loading hyperscript code '+target, 2 ) ;

  if ( setAb("Load Brains") == null ) return ;

  //if ( !isLoaded ) openconsole() ;

  var cmd = 'LOAD_BRAINS("' + 
	    server   + '",' + 
	    portnum  + ',"' + 
	    target   + '","' +
	    nodeName + '","' + 
	    deployMethod + '","' + 
	    sb_username + '","' + 
	    sb_sessionid   + '");' ;

  debug( cmd, 3 ) ;

  ret = ab.Eval ( cmd ) ;
  if ( typeof(ret) != "undefined" && ret != "$ACK" ) {
    var e = "Load Brains: Failed to Eval(), reason is " + ret ;
    debug( e, 1 ) ;
    alert ( e );
    return ;
  }
  debug('Load complete', 3 ) ;
  isLoading = false ;
  isLoaded = true ;
}

///////////////////////////////////////////////////////////////////
//
// HYPERSCRIPT INTERFACE
//
///////////////////////////////////////////////////////////////////


// HSPARSE is a front-end to calling ab.Parse, the
// way in which to get tokens into hyperscript when it
// is not in an idle state. Most often used at bootstrap,
// to load in the brains of the hyperscript program
//
// For both ACTIVEX and JAVA, calling hsparse is equivalent.
//
function hsparse ( cmd )
{
  if ( setAb("Parse") == null ) return ;
  ret = ab.Parse( cmd ) ;
  if ( typeof(ret) != "undefined" && ret != "$ACK" ) {
    var e = "HSparse: Failed to Parse(), reason is " + ret ;
    debug( e, 1 ) ;
    alert ( e );
    return ;
  }
}

// The JS function 'hseval' is a front-end to calling ab.Eval, the 
// way in which to send program code to Hyperscript
// to evaluate. 
//
// For both ACTIVEX and JAVA, calling hseval is equivalent.
//
// HOWEVER, Ab.Eval() is different between ActiveX and JAVA
// 
// 	ActiveX:	Ab.Eval sends its data on the HS mailslot
//			interface as a AIP message (method=EVAL) 
//			and the data is the message payload, which
//			becomes the segment to eval.
//
//	JAVA:		Ab.Eval sends raw data on the HS loopback
//			interface as raw data, and the data is 
//			captured by the HS method HSEVAL, which
//			becomes the segment to eval.
//
// THEREFORE, IN GERERAL, YOU SHOULD NEVER USE hseval() DIRECTLY.
//
// Use 'hscall' or 'hscall2' instead.
//

function hseval ( cmd )
{
  if ( setAb("Eval") == null ) return ;
  ret = ab.Eval( cmd ) ;
  if ( typeof(ret) != "undefined" && ret != "$ACK" ) {
    var e = "HSeval: Failed to Eval(), reason is " + ret ;
    debug( e, 1 ) ;
    alert ( e );
    return ;

  }
}

function hscall ( t, m, argument )
{
  // Send arguments to HS
  // 
  //    instance named <t>
  //    method named <m>
  //    passing <argument> for HS to deref 
  //
  // Use this to execute a method where the HS variable is already
  // defined in the HS program, that is, it is not coming from
  // javascript.
  // 
  // Eg:  event ( "INSTANCE#CONCEPT", "MYMETHOD", {&MYARG} ) ;
  //
  // 	  where 'MYARG' is a HS variable in your HS program
  // 

  var c = 	'event("' + 
		t + 
		'#"+self(2),"' + 
		m + 
		'", {&'  +
		argument
		'} );' ;

  //alert ( c ) ;

  hseval ( c ) ;

  // Return false so that the form won't submit.
  return false ; 
}

function hscall2 ( t, m, arg )
{
  // Send quoted string argument to HS
  // 
  //    instance named <t>
  //    method named <m>
  //    passing "<argument>" for HS to deref
  // 
  // Use this to execute a method with a single str argument, which is the
  // inline HS code to (dereference).  
  //
  // Eg:  event( "INSTANCE#CONCEPT", "MYMETHOD", {}, {"list myArg = {1,2,3};"} ) ;
  //
  var argument ;
  try {
    argument = arg.toString();
  }
  catch ( e ) {}

  if ( isJava ) {
    // Before sending argument, replace all quotes with \", pipes with \174, single quotes with \'
    argument = argument.replace( /\042/gi, "\\\"" );
    argument = argument.replace( /\174/gi, "\\174"); 
    argument = argument.replace( /\047/gi, "\\\'" ); 
  }
  else {
    // For ActiveX, we best just hide the quotes with 42
    // Always wondered what 42 was good for, now I know.
    argument = argument.replace( /\042/gi, "\\\\042" );
    // Get rid of the \r
    argument = argument.replace( /\r/gi, "" ); 
  }


  // Next split all the lines up, making a {"val1","val2","val3"} structure.
  var argList = argument.split ( "\n" );
  var argString = "{" ;
  var n = argList.length ;
  for ( i=0;i<n;i++ ) {
    if ( i == 0 )
      argString = argString + '"' + argList[i] + '"' ;
    else {
      if ( isActiveX ) 
        argString = argString + ',"' + argList[i] + '"' ;
      else
        argString = argString + '\n,"' + argList[i] + '"' ;
    }
  }
  argString = argString + '}' ;

  var c ;
  if ( isJava )
   // Add linefeeds to keep deref happy
   c = 	'event ( "' + 
	t + 
	'#" + self(2),\n "' + 
	m + '", {} ,\n' + 
	argString + 
	' ) ;' ;
  else
   c = 	'event ( "' + 
	t + 
	'#" + self(2), "' + 
	m + '", {} ,' + 
	argString + 
	' ) ;' ;

  //alert ( c ) ;

  // If this is JAVA, then 'c' will be sent directly down the loopback pipe.
  // If this is ACTIVEX, then 'c' will be further embedded in a AIP message to send

  hseval ( c ) ;

  // Return false so that the form won't submit.
  return false ; 
}


function hsputs ( argument )
{
  var c = 'event( self(2), "' + "PUTS" + '",{}, "' + argument + '" );' ;
  //alert ( c ) ;
  hseval ( c ) ;
  // Return false so that the form won't submit.
  return false; 
}

// ACTIVEX FUNCTIONS
function openconsole()
{
  if ( isActiveX ) document.WebPickle.CreateListBox() ;
}


function noop()
{
  return false ;
}


function writeconsole( message )
{
  // Get rid of the \r
  //message = message.replace( /\r/gi, "" ); 
  document.WebPickle.Puts( message ) ;
}

