/******************************************************************
 * AgentApplet.js
 *
 * NOTE: This file is deprecated, and cannot be used in conjunction
 * with the flash audio applets.  Please port your app to use
 * FlashAudio.js if possible.
 *****************************************************************/

/******************************************************************
 * class AgentApplet
 *****************************************************************/
var agentDebug = false;

// static ID offset
AgentApplet.NEXT_ID = new Date().getTime();

// Static registry
AgentApplet.REGISTRY = new Object();
var AgentItem = AgentApplet.REGISTRY;

// Proxy SWF filename
AgentApplet.PROXY_SWF = "JavaScriptFlashGateway.swf";

// User callback function
AgentApplet.STATUS_CALLBACK = null;

AgentApplet.prototype.setStatusCallback = function (fn) {
    AgentApplet.STATUS_CALLBACK = fn;
}

function AgentApplet (num, swfPath, proxyPath, seqPath, width, height, bgcolor) {
    if (AgentApplet.REGISTRY[num] === undefined) {
        this.init(num, swfPath, proxyPath, seqPath, width, height, bgcolor);
    } else {
        return false;
    }
}

AgentApplet.prototype.init = function (num, swfPath, proxyPath, seqPath, width, height, bgcolor) {
    if (!num) { return; }

    // Identifcation
    this.agentNum = num;
    // user does not have control over ID, since it is just for
    // internal flash use and must be unique across client's system
    this.id = this.nextId();

    // Link to flash item
    this.node = document.getElementById(this.id);

    // Settings
    this.seqPath = seqPath;
    this.setSwfPath(swfPath);
    this.setProxyPath(proxyPath);
    this.setSize(width, height);
    this.bgcolor = (bgcolor === undefined ? "FFFFFF" : bgcolor);
    this.mode = "transparent";
    this.proxy = new FlashProxy(this.id, this.proxyPath + AgentApplet.PROXY_SWF);

    // Set this if you want the first sequence in the file to be played as soon as all is loaded.
    this.autoplay = false;

    // Set this to the location of the CSS for styling the speech bubble text
    this.cssPath = null;

    // For offline use, set this to the location of the directory with the stored cache files
    this.ttsPath = null;

    // Floating container (see DragBox)
    // Will be linked to DragBox object
    this.dragBox = null;

    // Register self
    this.register();

    // Callbacks
    this.loaded = false;
    this.pendingProxyCalls = new Array();
}

// Make sure this applet has an ID unique across client system
AgentApplet.prototype.nextId = function () {
    return "Agent" + AgentApplet.NEXT_ID++;
}

// Register self for lookups
AgentApplet.prototype.register = function () {
    AgentApplet.REGISTRY[this.agentNum] = this;
    // alert("Registering agent applet '" + this.agentNum + "'");
}

// Make sure path to applet SWF is either "" or ends in "/"
AgentApplet.prototype.setSwfPath = function (path) {
    if (!path) {
        this.swfPath = "";
    } else if (path.lastIndexOf('/') != (path.length - 1)) {
        this.swfPath = path + "/";
    } else {
        this.swfPath = path;
    }
}

// Make sure path to proxy SWF is either "" or ends in "/"
AgentApplet.prototype.setProxyPath = function (path) {
    if (!path) {
        this.proxyPath = "";
    } else if (path.lastIndexOf('/') != (path.length - 1)) {
        this.proxyPath = path + "/";
    } else {
        this.proxyPath = path;
    }
}

AgentApplet.prototype.getName = function () {
    return this.name;
}

AgentApplet.prototype.proxyCall = function (args) {
    if (this.loaded) {
        // alert("proxy.call: '" + args[0] + "', '" + args[1] + "'");
        this.proxy.call.apply(this.proxy, args);
    } else {
        var npc = this.pendingProxyCalls.push(args);
        alert("adding pending call #" + npc + ": '" + args[0] + "', '" + args[1] + "'");
    }
}

AgentApplet.prototype.setLoaded = function (val) {
    this.loaded = val;
    // alert(this.name + ": setload " + val);
    if (val && this.pendingProxyCalls) {
        var npc = this.pendingProxyCalls.length;
        // alert(npc + " pending proxy calls.");
        for (var i = 0; i < npc; i++) {
            var proxyArgs = this.pendingProxyCalls[i];
            this.proxy.call.apply(this.proxy, proxyArgs);
        }
        // now zero out the array since we made all the pending calls
        this.pendingProxyCalls = new Array();
    }
}

// Override default height and width.
// This base class just sets them each to 100%.
// Override this method in subclasses.
AgentApplet.prototype.setSize = function (width, height) {
    this.width = (width === undefined ? "100%" : width);
    this.height = (height === undefined ? "100%" : height);
}

// Build HTML object/embed code
AgentApplet.prototype.constructTag = function () {
    var tag = new FlashTag(this.getAppletSwfUrl(), this.width, this.height);
    tag.setFlashvars(this.getAppletVarString());
    // alert("Flashvars = '" + this.getAppletVarString() + "'");
    tag.setBgcolor(this.bgcolor);
    tag.setMode(this.mode);
    return tag;
}

AgentApplet.prototype.getAppletSwfUrl = function () {
    // abstract: must override
}

AgentApplet.prototype.onWriteTag = function () {
    // base class does nothing here
}

// Insert into document in a moveable div container (see DragBox)
AgentApplet.prototype.createDragBox = function () {
    var tag = this.constructTag();
    var content = tag.toString();
    // Create container item and insert agent applet
    var newItem = new DragBox('dragAgent' + this.agentNum, content);
    // Clear when dragBox is hidden so agent will stop talking
    newItem.clearOnHide = true;
    // Specify DrabBox parent object
    this.dragBox = DragItem['dragAgent' + this.agentNum];
    // Add agent hide handler on close tab item
    var subEls = this.dragBox.node.getElementsByTagName("A");
    for (var i=0; i < subEls.length; i++) {
        if (subEls[i].className.indexOf("dragcloselink") >= 0) {
            var newHref = subEls[i].getAttribute("href", 0) + " hideAgent("+ this.agentNum + ");";
            subEls[i].setAttribute("href", newHref, 0);
        }
    }
    // Set flash visible
    this.setVisible(true);
}

// Insert into document at current location:
// Call this during rendering in a script tag in the location where the agent should be appear.
AgentApplet.prototype.writeTag = function (doc) {
    var tag = this.constructTag();
    if (!doc) { doc = document; }
    tag.write(doc);
    this.onWriteTag();
}

// Insert into document inside specified element:
// Wait till loaded and then inject it into the element with id <code>elementId</code>.
AgentApplet.prototype.insertTag = function (elementId) {
    var elt = document.getElementById(elementId);
    if (elt) {
        var tag = this.constructTag();
        elt.innerHTML = tag.toString();
        this.onWriteTag();
    }
}

// Callback functions must take one arg:
// The applet that is making the callback
AgentApplet.prototype.callback = function (fn) {
    if (typeof fn == "function") {
        fn.call(null, this);
        return true;
    }
    return false;
}

/******************************************************************
 * class AgentChar extends AgentApplet
 *****************************************************************/

function AgentChar (num, swfPath, proxyPath, seqPath) {
    if (AgentApplet.REGISTRY[num] === undefined) {
        this.init(num, swfPath, proxyPath, seqPath);
    } else {
        return false;
    }
}

AgentChar.prototype = new AgentApplet();
AgentChar.prototype.constructor = AgentChar;
AgentChar.superclass = AgentApplet.prototype;

AgentChar.prototype.init = function (num, swfPath, proxyPath, seqPath) {
    if (!num) { return; }
    AgentChar.superclass.init.call(this, num, swfPath, proxyPath, seqPath);

    // Status settings
    this.visible = false;
    this.playing = false;
    this.played = false;

    // Callbacks
    this.onPlay = null;
    this.onPlayStop = null;
    this.onReady = null;
    this.onError = null;
}

AgentChar.prototype.getAppletSwfUrl = function () {
    return this.swfPath + "agent.swf";
}

AgentChar.prototype.getAppletVarString = function () {
    var str = "agentNum=" + this.agentNum + "&lcId=" + this.id + "&xmlPath=" + this.seqPath;
	if (this.cssPath != null) {
		str += "&cssPath=" + this.cssPath;
	}
	if (this.ttsPath != null) {
		str += "&ttsPath=" + this.ttsPath;
	}
	if (this.autoplay) {
		str += "&autoplay=true";
	}
	return str;
}

AgentChar.prototype.updateSettings = function () {
    var cfg = {
        "xmlPath": this.seqPath
    };
    this.proxyCall(["updateSettings", cfg]);
    this.setLoaded(false);
}

AgentChar.prototype.setSize = function (width, height) {
    this.width = (width === undefined ? "100%" : width);
    this.height = (height === undefined ? "100%" : height);
}

AgentChar.prototype.isVisible = function () {
    return this.visible;
}

AgentChar.prototype.setVisible = function (vis) {
    this.visible = vis;
    var elt = document.getElementById(this.domId);
    if (!elt) { return; }
    if (vis) {
        elt.style.visibility = "visible";
    } else {
        elt.style.visibility = "hidden";
    }
}

// Determine current X location
AgentChar.prototype.currentX = function () {
    if (this.node) {
        var locX = parseInt(this.node.style.left, 10);
        if (isNaN(locX)) locX = this.node.offsetLeft;
        return locX;
    } else {
        return 0;
    }
}

// Determine current Y location
AgentChar.prototype.currentY = function () {
    if (this.node) {
        var locY = parseInt(this.node.style.top, 10);
        if (isNaN(locY)) locY = this.node.offsetTop;
        return locY;
    } else {
        return 0;
    }
}

// x and y are integers and will be interpreted as pixels ("px")
AgentChar.prototype.snapTo = function (x, y) {
    if (x === undefined || y === undefined) { return; }
    if (x == this.currentX() && y == this.currentY()) { return; }
    if (this.dragBox != null) {
        this.dragBox.snapTo(x, y);
    } else {
        if (this.node) {
            this.node.style.left = x;
            this.node.style.top = y;
        }
    }
}

AgentChar.prototype.moveTo = function (x, y) {
    if (x === undefined || y === undefined) { return; }
    if (x == this.currentX() && y == this.currentY()) { return; }
    if (this.isVisible() && this.dragBox != null) {
        this.dragBox.moveTo(x, y);
    } else {
        this.setPos(x, y);
    }
}

AgentChar.prototype.moveBy = function (x, y) {
    if (x === undefined || y === undefined) { return; }
    if (this.isVisible() && this.dragBox != null) {
        this.dragBox.moveBy(x, y);
    } else {
        this.setPos(this.currentX() + x, this.currentY() + y);
    }
}

AgentChar.prototype.moveRelative = function (id, x, y, h, v, delay) {
    if (x === undefined || y === undefined) { return; }
    if (this.isVisible() && this.dragBox != null) {
        this.dragBox.moveRelative(id, x, y, h, v, delay);
    }
}

AgentChar.prototype.playSequence = function (seqID) {
    // Show box -- currently for testing
    if (this.dragBox != null) {
        this.dragBox.show();
    }

    if (this.loaded) {
        this.playing = true;
        this.proxy.call('playSequence',seqID);
    }
}

// -------------------------------------- Status reporting for AgentChar
AgentChar.prototype.isPlaying = function () {
    return this.playing;
}

// Error handler: if no callback, default is to show error to user
AgentApplet.prototype.handleError = function (code, message) {
    if (this.onError) {
	this.errCode = code;
	this.errMessage = message;
	this.callback(this.onError);
    } else {
	alert("ERROR CODE " + code + ": " + message);
    }
}

// STATUS CODES FOR AGENT CHARACTER
// 01 = ready/stopped
// 02 = loading
// 03 = playing animation
// 08 = Error with Actions XML
// 09 = Error with Sequence XML
// 10 = Error, action not found in library
// 99 = sequence complete
AgentChar.prototype.handleEvent = function (code, message) {
    if (code === undefined) { return; }

    ; // this semicolon needed for emacs indenting of the switch below!
    switch (code + "") {
        case '01': {
            this.setLoaded(true);
            this.playing = false;
            this.callback(this.onReady);
            break;
        }
	case '02': {
	    this.setLoaded(false);
	    this.playing = false;
	    break;
	}
	case '03': {
	    this.playing = true;
	    this.callback(this.onPlay);
	    break;
	}
        case '99': {
            this.playing = false;
            this.callback(this.onPlayStop);
            break;
        }
        default: {
	    this.handleError (code, message);
        }
    }
}

// -------------------------------------- Callbacks for AgentChar

// NONE CURRENTLY

AgentChar.prototype.setOnReady = function (fn) {
    this.onReady = fn;
}

AgentChar.prototype.setOnPlay = function (fn) {
    this.onPlay = fn;
}

AgentChar.prototype.setOnPlayStop = function (fn) {
    this.onPlayStop = fn;
}

AgentChar.prototype.setOnError = function (fn) {
    this.onError = fn;
}

/******************************************************************
 * STATIC HACKS DUE TO HARD-CODED CALLBACKS FROM FLASH
 *****************************************************************/

// Status callback issued by agent applet
function updateAppletStatus(appletID, statusCode, statusText, agentNum) {
    var applet = AgentApplet.REGISTRY[agentNum];

    // Ignore callback to undefined item
    if (agentNum === undefined) {
        return;
    }

    // now also call user callback, if any
    if (typeof AgentApplet.STATUS_CALLBACK == "function") {
        AgentApplet.STATUS_CALLBACK(applet, statusCode, statusText);
    }
    if (!applet) {
        alert("Agent number unknown: " + agentNum);
		return;
    }
    if (!statusCode) {
		alert("No status code reported for " + applet);
		return;
	}
    applet.handleEvent(statusCode, statusText);
}

// Right-click/Hide event
function hideAgent(agentNum) {
    //alert("HIDE: AgentNum '" + agentNum + "'");
    // Stop any playing action
    // FIXME - need hook into agent flash
    // Set flash invisible
    AgentItem[agentNum].setVisible(false);
    // Hide dragBox if applicable
    if (AgentItem[agentNum].dragBox != null) {
        AgentItem[agentNum].dragBox.hide();
    }
}

// Movement Event
function moveAgent (agentNum, duration, x, y, destObj, align, valign) {
	var agent = AgentItem[agentNum];
	if (agent != null) {
		if (destObj != null) {
			agent.moveRelative (destObj, x, y, align, valign, duration);
		} else {
			agent.moveTo (x, y, duration);
		}
	} else {
		alert ("no such agent");
	}
}
