/******************************************************************
 * FlashApplet.js - requires JavaScriptFlashGateway.js
 *****************************************************************/

/******************************************************************
 * class FlashApplet
 *****************************************************************/

function FlashApplet (name, swfPath, proxyPath, width, height, bgcolor) {
    this.init(name, swfPath, proxyPath, width, height, bgcolor);
}

// static ID offset
FlashApplet.NEXT_ID = new Date().getTime();
// static ID registry
FlashApplet.ID_REGISTRY = new Object();
// static name registry
FlashApplet.NAME_REGISTRY = new Object();
// constant
FlashApplet.PROXY_SWF = "JavaScriptFlashGateway.swf";
// user callback function
FlashApplet.STATUS_CALLBACK = null;
// callback function for debugging
FlashApplet.DEBUG = null;

/**
 * STATIC method
 * call user's debugging callback function, if any
 */
FlashApplet.debug = function (txt) {
    if (typeof FlashApplet.DEBUG == "function") {
        FlashApplet.DEBUG.call(this, txt);
    }
};

/**
 * STATIC method
 */
FlashApplet.setStatusCallback = function (fn) {
    FlashApplet.STATUS_CALLBACK = fn;
};

/**
 * STATIC method
 */
FlashApplet.lookupID = function (id) {
    return FlashApplet.ID_REGISTRY[id];
};

/**
 * STATIC method
 * agents use a number, audio applet uses string names.  combine both to 
 * use string names
 */
FlashApplet.lookupNumber = function (num) {
    return FlashApplet.NAME_REGISTRY[num + ""];
};

/**
 * STATIC method
 */
FlashApplet.lookupName = function (name) {
    return FlashApplet.NAME_REGISTRY[name];
};

FlashApplet.prototype.init = function (name, swfPath, proxyPath, width, height, bgcolor) {
    if (!name) { return; };

    this.name = name;
    // 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();
    this.setSwfPath(swfPath);
    this.setProxyPath(proxyPath);
    this.setSize(width, height);
    // may be left undefined
    this.bgcolor = (bgcolor === undefined ? "FFFFFF" : bgcolor);
    this.mode = "transparent";
    this.proxy = new FlashProxy(this.id, this.proxyPath + FlashApplet.PROXY_SWF);

    // register self
    this.register();

    // callbacks
    this.initialized = false;
    this.pendingProxyCalls = new Array();
    this.onError = null;

};

/**
 * make sure this applet has an ID unique across client system
 */
FlashApplet.prototype.nextId = function () {
    return "A" + FlashApplet.NEXT_ID++;
};

/**
 * register self for lookups
 */
FlashApplet.prototype.register = function () {
    FlashApplet.ID_REGISTRY[this.id] = this;
    FlashApplet.NAME_REGISTRY[this.name] = this;
    // alert("Registering applet '" + this.name + "' with ID '" + this.id + "'");
};

/**
 * make sure path is either "" or ends in "/"
 */
FlashApplet.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 "/"
FlashApplet.prototype.setProxyPath = function (path) {
    if (!path) {
        this.proxyPath = "";
    } else if (path.lastIndexOf('/') != (path.length - 1)) {
        this.proxyPath = path + "/";
    } else {
        this.proxyPath = path;
    }
};

FlashApplet.prototype.getName = function () {
    return this.name;
};

FlashApplet.prototype.proxyCall = function (args) {
    if (this.initialized) {
        this.proxyCallInternal(args);
    } else {
        var npc = this.pendingProxyCalls.push(args);
        FlashApplet.debug("Applet " + this.name + ": adding pending call #" + npc + ": '" + args[0] + "', '" + args[1] + "'");
    }
};

FlashApplet.prototype.proxyCallInternal = function (args) {
    FlashApplet.debug("Applet " + this.name + ": proxy.call: '" + args[0] + "', '" + args[1] + "'");
    this.proxy.call.apply(this.proxy, args);
};

FlashApplet.prototype.setInitialized = function (val) {
    // don't do anything if not changing value
    if (this.initialized == val) { return; }
    this.initialized = val;
    // alert(this.name + ": setload " + val);
    if (val && this.pendingProxyCalls) {
        var npc = this.pendingProxyCalls.length;
        FlashApplet.debug("Applet " + this.name + ": " + npc + " pending proxy calls.");
        for (var i = 0; i < npc; i++) {
            var proxyArgs = this.pendingProxyCalls[i];
            this.proxyCallInternal(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.
 */
FlashApplet.prototype.setSize = function (width, height) {
    this.width = (width === undefined ? "100%" : width);
    this.height = (height === undefined ? "100%" : height);
};

/**
 * Build HTML object/embed code
 */
FlashApplet.prototype.constructTag = function () {
    var tag = new FlashTag(this.getAppletSwfUrl(), this.width, this.height);
    tag.setFlashvars(this.getAppletVarString());
    FlashApplet.debug("Applet " + this.name + ": Flashvars = '" + this.getAppletVarString() + "'");
    tag.setBgcolor(this.bgcolor);
    tag.setMode(this.mode);
    return tag;
};


FlashApplet.prototype.onWriteTag = function () {
    // base class does nothing here
};

/**
 * The standard way of inserting the applet into the document: call this
 * during rendering in a script tag in the location where the applet
 * should be rendered.
 */
FlashApplet.prototype.writeTag = function (doc) {
    var tag = this.constructTag();
    if (!doc) { doc = document; }
    tag.write(doc);
    this.onWriteTag();
};

/**
 * An alternative way of inserting the applet: wait till loaded and
 * then inject it into the element with id <code>elementId</code>.
 */
FlashApplet.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.  Returns true iff the function is defined
 */
FlashApplet.prototype.callback = function (fn) {
    if (typeof fn == "function") {
        fn.call(null, this);
        return true;
    }
    return false;
};

FlashApplet.prototype.getAppletSwfUrl = function () {
    // abstract: must override
};

FlashApplet.prototype.getAppletVarString = function () {
    return "lcId=" + this.id;
};

// Error handler: if no callback, default is to show error to user
FlashApplet.prototype.handleError = function (code, message) {
    if (this.onError) {
        this.errCode = code;
        this.errMessage = message;
        this.callback(this.onError);
    } else {
        alert("ERROR CODE " + code + ": " + message);
    }
};

// COMMON STATUS CODES
// 01 = ready/stopped
FlashApplet.prototype.handleEvent = function (code, message) {
    if (code === undefined) {
        return;
    }
    code += ""; // force to be a string
    if (code == "01") {
        // no error here
        handled = true;
        this.setInitialized(true);
    } else {
        // unhandled event code
        this.handleError(code, message);
    }
};

// ------------------------------- Callbacks

FlashApplet.prototype.setOnError = function (fn) {
    this.onError = fn;
};


/******************************************************************
 * STATIC HACKS DUE TO HARD-CODED CALLBACKS FROM FLASH
 *****************************************************************/

/**
 * status callback called by flash code:
 *
 * HACK: may use either appletID (audio applets) or number (agents) to
 * identify self.
 */
function updateAppletStatus(appletID, statusCode, statusText, number) {
    var applet = null;
    // HACK: agents callback doesn't actually use appletID
    if (number !== undefined) {
        applet = FlashApplet.lookupNumber(number);
        if (!applet) {
            alert("Flash applet number unknown: " + number);
            FlashApplet.debug("Applet #" + number + ": " + statusCode + ": " + statusText);
            return;
        }
    } else if (appletID !== undefined) {
        applet = FlashApplet.lookupID(appletID);
        if (!applet) {
            alert("Flash applet ID unknown: " + agentID);
            FlashApplet.debug("Applet with ID '" + appletID + "': " + statusCode + ": " + statusText);
            return;
        }
    } else {
        alert("Flash updateAppletStatus with undefined ID and number: '" + statusCode + "', '" + statusText + "'");
        return;
    }
    // applet found
    FlashApplet.debug("Applet " + applet.name + ": " + statusCode + ": " + statusText);
    // abort if undef status code
    if (statusCode === undefined) {
		alert("No status code reported for " + applet);
        return;
    }
    // now also call user callback
    if (typeof FlashApplet.STATUS_CALLBACK == "function") {
        FlashApplet.STATUS_CALLBACK.call(null, applet, statusCode, statusText);
    }
    // finally let the applet itself actually handle the event
    applet.handleEvent(statusCode, statusText);

}

