/**************************************************************************
*
*  @@@BUILDINFO@@@ 71bridgetalk-2.jsx 2.0.1.63  26-March-2007
*  Copyright 2006-2007 Adobe Systems Incorporated
*  All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains the property of
* Adobe Systems Incorporated  and its suppliers,  if any.  The intellectual 
* and technical concepts contained herein are proprietary to  Adobe Systems 
* Incorporated  and its suppliers  and may be  covered by U.S.  and Foreign 
* Patents,patents in process,and are protected by trade secret or copyright 
* law.  Dissemination of this  information or reproduction of this material
* is strictly  forbidden  unless prior written permission is  obtained from 
* Adobe Systems Incorporated.
**************************************************************************/

/* 
BridgeTalk support routines.
The Toolkit has three BridgeTalk queues:
estoolkit-2.0: the main engine
estoolkit-2.0#estk: the queue for the backend (internal)
estoolkit-2.0#dbg: the debugger queue that other backends talk to
*/

//BridgeTalk.log = true;

// The targets object contains all BT targets as property named. The objects 
// themselves contain three properties: 
// name - the BT specifier
// displayName - the human-readable name
// connected - true if the app is connected

// The BridgeTalk stack contains all active BridgeTalk instances.

BridgeTalk.stack = [];

// Create a new BridgeTalk instance.

BridgeTalk.create = function( target, command, handleTimeout )
{
	var bt    = new BridgeTalk;
	bt.type   = "Debug";
	bt.sender = "estoolkit-2.0#dbg";
	bt.target = target + "#estk";	// this is the name of the debugging BT instance in the target
	if (command != undefined)
		bt.headers.Command = command;
	bt.headers.ProtocolVersion = $.version.split (' ')[0];	// ignore the (debug) suffix
	// If the shutdown member is set to true, the header Shutdown is set to 1,
	// which indicates to the target that it should not send any more messages.
	// The app.queryShutdown() method does this.
	if (app.shutdown)
		bt.headers.Shutdown = "1";
	this.stack.push (bt);
	// set the owner member to the current debugger for later checking 
	// (see the default versions of onResult and onError below)
	bt.owner = currentDebugger;
	// handle timeout
	if( handleTimeout )
	    bt.onTimeout = bt.timeoutHandler;
	    
	return bt;
}

// The default error and result handlers remove the BridgeTalk instance after use.
// They also check if the owner member still is the current debugger. If so,
// they call onOwnResult() or onOwnError() if they exist. These methods are defined
// by msgs initiated by Debugger instances, and the mechanism inhibits asynchronous
// replies being processed if the debugger has changed.

BridgeTalk.prototype.onError = function (bt)
{
	if (BridgeTalk.log)
		print ("e ", bt.sender, ' ', this.headers.Command, '=', bt.body);
	if (this.owner == currentDebugger && this.onOwnError)
		this.onOwnError (bt);
	this.destroy();
}

BridgeTalk.prototype.timeoutHandler = function (bt)
{
	if (BridgeTalk.log)
		print ("e ", bt.sender, ' ', this.headers.Command, '=', bt.body);
	if (this.owner == currentDebugger && this.onOwnTimeout)
		this.onOwnTimeout(bt);
    else if (this.owner == currentDebugger && this.onOwnError)
		this.onOwnError (bt);
	this.destroy();
}

BridgeTalk.prototype.onResult = function (bt)
{
	if (BridgeTalk.log)
		print ("r ", bt.sender, ' ', this.headers.Command, '=', bt.body.split ('\n')[0]);
	if (this.owner == currentDebugger && this.onOwnResult)
		this.onOwnResult (bt);
	this.destroy();
}

// The finalize handler is called when the BridgeTalk instance is about to go out of focus.

BridgeTalk.prototype.finalize = function()
{}

// Destroy a BridgeTalk instance by removing it from the list of all
// instances. Also set the target to the empty string to indicate its
// "destruction".

BridgeTalk.prototype.destroy = function()
{
    var target  = this.target.replace ("#estk", "");
    var command = this.headers.Command;
    
	for( var i=0; i<BridgeTalk.stack.length; i++ )
	{
		if( BridgeTalk.stack [i] == this )
		{
			BridgeTalk.stack.splice (i, 1);
			break;
		}
	}
	
	this.finalize();
	this.target = "";
	
	globalBroadcaster.notifyClients( 'destroyBT', target, command );
}

// find a BridgeTalk instance on the stack for target and command string

BridgeTalk.findInstance = function( target, command )
{
    var _target = target.replace ("#estk", "");

	for( var i = 0; i < BridgeTalk.stack.length; i++ )
	{
	    var btTarget = BridgeTalk.stack[i].target.replace( '#estk', '' );
	    
		if( btTarget == _target && BridgeTalk.stack[i].headers.Command == command )
		    return BridgeTalk.stack[i];
    }
    
    return null;
}

///////////////////////////////////////////////////////////////////////////////

// Decode the body according to the escaping guidelines and create a two-dimensional
// array. The major array contains each line as an array of elements that were separated
// by commas. Return that array.

BridgeTalk.prototype.splitBody = function()
{
	var reply = this.body.split ('\n');
	// forget about an empty line at the end
	if (reply [reply.length-1].length == 0)
		reply.pop();
	for (var i = 0; i < reply.length; i++)
	{
		var line = reply [i];
		reply [i] = [];
		var esc = false;
		var item = "";
		for (var j = 0; j < line.length; j++)
		{
			var ch = line [j];
			if (ch == '\\' && !esc)
				esc = true;
			else if (esc)
			{
				// escaped character
				if (ch == 'n')
					ch = '\n';					
				item += ch;
				esc = false;
			}
			else if (ch == ',')
			{
				// separator found; eval the part to get rid of escapes
				reply [i].push (item);
				item = "";
				// comma at end: push an empty string
				if (j == line.length-1)
					reply [i].push (item);
			}
			else
				item += ch;
		}
		if (item.length)
			reply [i].push (item);
	}
	return reply;
}

// Encode a string according to the protocol conventions.

BridgeTalk.encode = function (s)
{
	var t = "";
	for (var i = 0; i < s.length; i++)
	{
		switch (s[i])
		{
			case ',':	t += "\\,"; break;
			case '\\':	t += "\\\\"; break;
			case '\n':	t += "\\n"; break;
			default:	t += s [i];
		}
	}
	return t;
}

// Send a BT message after making sure that the target is running

BridgeTalk.prototype.safeSend = function()
{
	var target = this.target.replace ("#estk", "");

	if (BridgeTalk.log)
		print ("S ", this.target, ' ', this.type, ' ', this.headers.Command);

    //
	// send this message only if it either an initial Connect message,
	// or if the target still appears to be connected
	//
	var ret = false;
	
	if( targetMgr.getConnected( target ) )
		return this.send();
	else if( this.headers.Command == "Connect" )
	{
		if (BridgeTalk.isRunning (target) || app.launchTarget (target))
			return this.send();
		else if(currentDebugger)
		{
			// switch back to the current debugger
			currentDebugger.activate();
		}
	}
	
	if( !ret )
	    this.destroy();
	    
    return ret;
}

////////////////////////////////////////////////////////////////

// The BridgeTalk receive handler dispatches incoming messages
// according to the type. Messges with type == "Debug" go to
// the current debugger. These are notification messages from
// debugging targets, like hitting a breakpoint or a runtime
// error, or exiting debug mode.

BridgeTalk.onReceive = function (bt)
{
	if (BridgeTalk.log)
		print ("R ", bt.sender, ' ', bt.type, ' ', bt.headers.Command);
	switch (bt.type)
	{
		case "Debug":
			try
			{
			    if( bt.headers.Command && bt.headers.Command.length > 0 )
			    {
			        //
			        // process classic commands
			        //
				    switch (bt.headers.Command)
				    {
					    case "Print":		
						    // write msg to console (no line feed appended)
						    console.write (bt.body);
						    break;
					    case "ConnectRequest":
						    // Sent by a target app wishing to connect
						    // If the target is not registered, register it now
						    var target = bt.sender.replace ("#estk", "");
						    targetMgr.checkTarget (target);
						    Debugger.remoteConnect (bt);
						    
						    //
						    // if the app was launched by another app with a connect request
						    // then open a new document and set the target to the calling
						    // application. But not, if a debugging request follows!
						    //
						    if( remoteLaunched )
						    {
				                var src =  "if(remoteLaunched){                                         \
				                                var doc = Document.create();                            \
								                if(doc) { doc = doc.document;                           \
								                    var targetObj = targetMgr.findTarget('"+target+"'); \
								                    if(targetObj){                                      \
								                        targetMgr.setActive( targetObj.targetName, ''); \
								                        doc.selectActiveTarget(targetObj);}}}"
                								
				                app.scheduleTask( src, 200 );
						    }
						    break;
					    case "Open":
						    // sent by a second instance of the Toolkit
						    // so bring this app to front in any case
						    if (bt.body.length)
						    {
							    var f = new File (bt.body);
							    if (f.exists)
								    scripts.loadFile (f);
						    }
						    app.toFront();
						    break;

					    case "FWDMESSAGES":
					    case "FWDLOGS":
					    case "FWDERRORS":
						    // sent by a target to let us know that this message 
						    // has been added, updated, or deleted to, in, and from the message queues.	
						    if (typeof bridgetalk == "object")
							    bridgetalk.updatePane( bt );
						    break;
					    default:
					        // reset remote flag
					        remoteLaunched = false;
					        
						    // all others: let the debugger process them
						    var dbg = Debugger.find (bt.sender, bt.headers.Engine);
						    if (dbg)
							    dbg.processMsg (bt);
				    }
				}
				else
				{
					var xml, cmd;
					try
					{
						xml = new XML( bt.body );
						cmd = xml.name();
					}
					catch (e)
					{
						xml = "";
						cmd = "";
					}
				    
				    if( cmd )
				        cmd = cmd.toString();
				    
				    if( cmd.length > 0 )
				    {
				        switch( cmd )
				        {
				            case "engine-name":
				            {
				                var target = targetMgr.findTarget( bt.sender.replace( '#estk', '' ) )
    				            
				                if( target )
				                    target.changeEngine( xml.@old.toString(), xml.@new.toString() );
				            }
				            break;
    				        
					        default:
						        // all others: let the debugger process them
						        var dbg = Debugger.find (bt.sender, bt.headers.Engine);
						        if (dbg)
							        dbg.processMsg (bt);
				        }
				    }
				}
			}
			catch (err)
			{
				print ("*** INTERNAL ERROR");
				print (err.name, ": ", err.message);
				print ("File ", err.fileName, ", line ", err.line);
			}
			break;
		case "ExtendScript":
			// standard handler for scripts sent to myself
			return eval (bt.body);
	}
}
