/**
 * @fileoverview
 * This script defines a widget for finding a batch of D. mel genes via Flybase.
 * @version $Revision:539 $ on $Date: 2008-08-13 16:09:30 +0100 (Wed, 13 Aug 2008) $ by $Author: aliman $
 * @requires YAHOO.log
 * @requires YAHOO.lang.JSON
 * @requires YAHOO.util.Dom
 * @requires YAHOO.util.Connect
 * @requires YAHOO.util.CustomEvent
 * @requires flyui.util
 * @requires flyui.flybase
 * TODO license terms
 */
 
flykit.namespace("flykit.genefinder");




/*
 * ----------------------------------------------------------------
 *                             WIDGET
 * ----------------------------------------------------------------
 */





/**
 * @class
 */
flykit.genefinder.BatchWidget = function( service, renderer ) {
    var _context = "flykit.genefinder.BatchWidget";
    try {
        
        flykit.debug("call private constructor", _context);
        this.__init__(service, renderer);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/** @private */
flykit.genefinder.BatchWidget.prototype._controller = null;

/**
 * @private
 * @type flykit.mvcutils.GenericModel2
 */ 
flykit.genefinder.BatchWidget.prototype._model = null;

/**
 * @private
 */
flykit.genefinder.BatchWidget.prototype._renderer = null;

/**
 * @private
 */
flykit.genefinder.BatchWidget.prototype._service = null;

/**
 * @private
 */
flykit.genefinder.BatchWidget.prototype._genesFoundEvent = null;





/**
 * @private
 */
flykit.genefinder.BatchWidget.prototype.__init__ = function( service, renderer ) {
    var _context = "flykit.genefinder.BatchWidget.prototype.__init__";
    try {
        
        this._service = service;
        this._renderer = renderer;
        
        flykit.debug("create events", _context);
        this._genesFoundEvent = new YAHOO.util.CustomEvent("GENESFOUND", this);
        
        flykit.debug("create a model", _context);
        this._model = new flykit.mvcutils.GenericModel2();
        this._model.setDefinition(flykit.genefinder.BatchWidget.modelDefinition);
        
        flykit.debug("create a controller", _context);
        this._controller = new flykit.genefinder.BatchWidget.Controller(this._model, service, this);
        
        flykit.debug("connect the renderer to the model", _context);
        this._renderer.connect(this._model);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @public
 */
flykit.genefinder.BatchWidget.prototype.subscribe = function( type, listener, obj ) {
    var _context = "flykit.genefinder.BatchWidget.prototype.subscribe";
    try {
        
        if (type == "GENESFOUND") {
            this._genesFoundEvent.subscribe(listener, obj);
        }
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
    
};





/**
 * @public
 * @param {Array<String>} names
 */
flykit.genefinder.BatchWidget.prototype.findGenesByAnyNameBatch = function( names ) {
    var _context = "flykit.genefinder.BatchWidget.prototype.findGenesByAnyNameBatch";
    try {
        
        flykit.debug("pass ["+names+"] through to controller", _context);
        this._controller.findGenesByAnyNameBatch(names);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/*
 * ----------------------------------------------------------------
 *                             CONTROLLER
 * ----------------------------------------------------------------
 */





/**
 * @class
 */
flykit.genefinder.BatchWidget.Controller = function( model, service, controllee ) {
    var _context = "flykit.genefinder.BatchWidget.Controller";
    try {
        
        this._model = null;
        this._service = null;
        this._controllee = null;
        
        flykit.debug("call private constructor", _context);
        this.__init__(model, service, controllee);        
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @private
 */
flykit.genefinder.BatchWidget.Controller.prototype.__init__ = function( model, service, controllee ) {
    var _context = "flykit.genefinder.BatchWidget.Controller.prototype.__init__";
    try {
        
        this._model = model;
        this._service = service;
        this._controllee = controllee;
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @public
 */
flykit.genefinder.BatchWidget.Controller.prototype.findGenesByAnyNameBatch = function( names ) {
    var _context = "flykit.genefinder.BatchWidget.Controller.prototype.findGenesByAnyNameBatch";
    try {
        
        flykit.info("findGenesByAnyNameBatch: "+names, _context);
        flykit.debug("pass through to private implementation", _context);
        this._findGenesByAnyNameBatch(names, this._findGenesByAnyNameBatchSuccess(), this._findGenesByAnyNameBatchFailure());
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @private
 */
flykit.genefinder.BatchWidget.Controller.prototype._findGenesByAnyNameBatch = function( names, success, failure ) {
    var _context = "flykit.genefinder.BatchWidget.Controller.prototype._findGenesByAnyNameBatch";
    try {
        
        flykit.debug("names: "+names, _context);
        
        flykit.debug("set state pending", _context);
        this._model.set("STATE", "PENDING");
        
        flykit.debug("set model property query", _context);
        this._model.set("QUERY", names);

        flykit.debug("set result null", _context);
        this._model.set("RESULTS", null);
                
        flykit.debug("set message null", _context);
        this._model.set("MESSAGE", null);
                
        flykit.debug("kick off request", _context);
        this._service.findGenesByAnyNameBatch(names, success, failure);        
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @private
 */
flykit.genefinder.BatchWidget.Controller.prototype._findGenesByAnyNameBatchSuccess = function() {
    var _context = "flykit.genefinder.BatchWidget.Controller.prototype._findGenesByAnyNameBatchSuccess";
    var self = this;
    /**
     * @param {Object<String,<Array<Gene>>} results a map from names in the query to arrays of gene objects
     */
    return function( results ) {
        try {
            
            flykit.info("request success", _context);
            
            flykit.debug("set model results", _context);
            self._model.set("RESULTS", results);
            
            flykit.debug("set model state", _context);
            self._model.set("STATE", "READY");
            
            flykit.debug("fire event", _context);
            self._controllee._genesFoundEvent.fire(self._model.get("QUERY"), results);
            
        } catch (unexpected) {
            flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
            throw unexpected;    
        }
    };
};





/**
 * @private
 */
flykit.genefinder.BatchWidget.Controller.prototype._findGenesByAnyNameBatchFailure = function() {
    var _context = "flykit.genefinder.BatchWidget.Controller.prototype._findGenesByAnyNameBatchFailure";
    var self = this;
    /**
     * @param {ResponseObject} response
     */
    return function( response ) {
        try {
            
            flykit.err("request failed: "+response.status+" "+response.statusText, _context);
            self._model.set("MESSAGE", "there was an error retrieving data from the server, see the logs for more info");
            self._model.set("STATE", "SERVERERROR");
            
        } catch (unexpected) {
            flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
            throw unexpected;    
        }
    };
};





/*
 * ----------------------------------------------------------------
 *                             MODEL DEFINITION
 * ----------------------------------------------------------------
 */





/** 
 * @type Object
 */
flykit.genefinder.BatchWidget.modelDefinition = {

    properties : [ "STATE", "RESULTS", "MESSAGE", "QUERY" ],
    
    values : {
        "STATE" : [ "READY", "PENDING", "SERVERERROR", "UNEXPECTEDERROR" ]
    },
    
    initialize : function( data ) {
        data["STATE"] = "READY";
        data["RESULTS"] = null;
        data["MESSAGE"] = null;
        data["QUERY"] = null;
    }

};





/*
 * ----------------------------------------------------------------
 *                             DEFAULT RENDERER
 * ----------------------------------------------------------------
 */


flykit.genefinder.BatchWidget.DefaultRenderer = function() {};


/** 
 * @private 
 * @type Element
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._canvas = null;

/**
 * @private 
 * @type Element
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._pendingPane = null;

/**
 * @private 
 * @type Element
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._resultsSummaryPane = null;

/**
 * @private 
 * @type Element
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._resultsPane = null;

/**
 * @private 
 * @type Element
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._messagePane = null;





/**
 * Set the DOM element to which this renderer applies and initialise it.
 * @param {Element} canvas
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype.setCanvas = function( canvas ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype.setCanvas";
    try {
        
        flykit.debug("set canvas", _context);
        this._canvas = $(canvas);
        this._initCanvas();
        
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * @private
 * @param {Element} canvas
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._initCanvas = function() {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype._initCanvas";
    try {
        
        flykit.debug("begin init canvas", _context);
        
        var canvas = this._canvas;
        canvas.addClass("batchGeneFinderWidget");

        var pp = $("<p class='pendingPane'>pending...</p>").hide();
        canvas.append(pp);
        this._pendingPane = pp;

        var rsp = $("<p class='resultsSummaryPane'>this text should never be displayed</p>").hide();
        canvas.append(rsp);
        this._resultsSummaryPane = rsp;
        
        var rp = $("<div class='resultsPane'></div>").hide();
        canvas.append(rp);
        this._resultsPane = rp;
        
        var mp = $("<p class='messagePane'>this text should never be displayed</p>").hide();
        canvas.append(mp);
        this._messagePane = mp;            

        flykit.debug("init canvas done", _context);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @public
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype.connect = function( model ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype.connect";
    try {
        
        flykit.debug("connect to model", _context);
        model.subscribeAll(this._onModelChanged, this);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @private
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onModelChanged = function( type, args, self ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onModelChanged";
    try {
        
        var handlers = {
            "STATE":"_onStateChanged",
            "QUERY":"_onQueryChanged",
            "RESULTS":"_onResultsChanged",
            "MESSAGE":"_onMessageChanged", 
            "MODE":"_onModeChanged"
        };
        var handler = handlers[type];
        flykit.debug("call model changed handler: "+handler, _context);
        self[handler](args[0], args[1], args[2]);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * @private
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onQueryChanged = function( from, to, get ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onQueryChanged";
    try {
        
        // do nothing, we will access the value later
        flykit.debug("query changed from "+from+" to "+to, _context);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * @private
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onStateChanged = function( from, to, get ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onStateChanged";
    try {
        
        flykit.debug("state changed from "+from+" to "+to, _context);

        if ( to == "PENDING" ) {
            this._pendingPane.show();
            this._messagePane.hide();
            this._resultsSummaryPane.hide();
            this._resultsPane.hide();
        }
        else if ( to == "READY" ) {
            this._pendingPane.hide();
            this._messagePane.hide(); // hide for now, not needed yet
            this._resultsSummaryPane.show();
            this._resultsPane.show();         
        } 
        else if ( to == "SERVERERROR" || to == "UNEXPECTEDERROR" ) {
            this._pendingPane.hide();
            this._messagePane.show();
            this._resultsSummaryPane.hide();
            this._resultsPane.hide();         
        } 
        else {
            // this should never happen
            throw {message:"invalid state: "+to};
        }
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * @private
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onResultsChanged = function( from, to, get ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onResultsChanged";
    try {
        
        flykit.debug("results changed to: "+to, _context);
        
        if (to == null) {
            flykit.debug("empty results summary pane", _context);
            this._resultsSummaryPane.empty();
            flykit.debug("empty results pane", _context);
            this._resultsPane.empty();
        } 
        
        else {

            flykit.debug("render results summary", _context);
            this._renderResultsSummary(to);
    
            flykit.debug("render results", _context);
            this._renderResults(to);        
            
        }
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};







/**
 * @private
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onMessageChanged = function( from, to, get ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype._onMessageChanged";
    try {
        
        flykit.debug("message changed to: "+to, _context);
        
        if (to == null) {
            flykit.debug("empty messsage pane", _context);
            this._messagePane.empty();
        } 
        
        else {

            this._messagePane.html(to);
            
        }
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};







/**
 * @private
 * @param {Object<String,<Array<Gene>>} results a map from names in the query to arrays of gene objects
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._renderResultsSummary = function( results ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype._renderResultsSummary";
    try {
        
        flykit.debug("render results summary", _context);
        var ok = 0;
        var notfound = 0;
        var ambiguous = 0;
        
        for (var key in results) {
            flykit.debug("inspecting key: "+key, _context);
            var genes = results[key];
            if (genes.length == 0) {
                notfound++;
            }
            else if (genes.length == 1) {
                ok++;
            }
            else {
                ambiguous++;
            }
        }
        
        var content = "found "+ok+" OK, "+ambiguous+" ambiguous, "+notfound+" not found from <a href='http://flybase.org'>flybase.org</a> (FB2011_01) ...";
        this._resultsSummaryPane.html(content);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};








/**
 * @private
 * @param {Object<String,<Array<Gene>>} results a map from names in the query to arrays of gene objects
 */
flykit.genefinder.BatchWidget.DefaultRenderer.prototype._renderResults = function( results ) {
    var _context = "flykit.genefinder.BatchWidget.DefaultRenderer.prototype._renderResults";
    try {
        
        var renderFlybaseLink = function(gene) {
            return "<a href='http://flybase.org/reports/"+gene.flybaseID+".html'>"+gene.flybaseID+"</a>";
        }
        
        flykit.debug("render results", _context);
        var found = "<strong>found:</strong> ";
        var notfound = "<strong>not found:</strong> ";
        var ambiguous = "<strong>ambiguous:</strong> ";
        
        var foundcount = 0;
        var notfoundcount = 0;
        var ambiguouscount = 0;
        
        for (var key in results) {
            flykit.debug("inspecting key: "+key, _context);
            var genes = results[key];
            if (genes.length == 0) {
                notfoundcount++;
                notfound += key + "; ";
            }
            else if (genes.length == 1) {
                foundcount++;
                found += key + " (" + renderFlybaseLink(genes[0]) + "); ";
            }
            else {
                ambiguouscount++;
                ambiguous += key +" (";
                for (var i=0; i<genes.length; i++) {
                    ambiguous += renderFlybaseLink(genes[i]);
                    if (i<genes.length-1) {
                        ambiguous += ", ";
                    }
                }
                ambiguous += "); ";
            }
        }
        
        var content = "";
        if (foundcount > 0) content += "<p>"+found+"</p>";
        if (ambiguouscount > 0) content += "<p>"+ambiguous+"</p>";
        if (notfoundcount > 0) content += "<p>"+notfound+"</p>";
        this._resultsPane.html(content);
        
    } catch (unexpected) {
        flykit.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};









