/**
 * @fileoverview
 * This script defines a service class for retrieving data from flyatlas.
 * @author <a href="http://purl.org/net/aliman">Alistair Miles</a> and Jun Zhao
 * @version $Revision:124 $ on $Date: 2009-11-17 10:11:41 +0100 (Tue, 17 Nov 2009) $ by $Author: zhaoj $
 * @requires flykit.util
 * @requires flykit.sparql.Service
 * @requires flykit.flyatlas.Service
 * For license terms see http://code.google.com/p/open-biomed/
 */


flykit.namespace("flykit.flyatlas");


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


/**
 * @class
 * @param {flykit.flyatlas.Service} service
 * @param {flykit.flyatlas.Widget.DefaultRenderer} renderer
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget = function( service, renderer ) {

    /** 
     * @private 
     * @type flykit.flyatlas.Widget.Controller
     */
    this._controller = null;
    
    /**
     * @private
     * @type flykit.mvcutils.GenericModel2
     */ 
    this._model = null;
    
    /**
     * @private
     * @type flykit.flyatlas.Widget.DefaultRenderer
     */
    this._renderer = null;
    
    /**
     * @private
     * @type flykit.flyatlas.Service
     */
    this._service = null;

    // do initialisation
    this._init(service, renderer);
    
};

/**
 * Do initialisation of a widget.
 * @private
 * @param {flykit.flyatlas.Service} service
 * @param {flykit.flyatlas.Widget.DefaultRenderer} renderer
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.prototype._init = function(service, renderer) {
    try {

        this._service = service;
        this._renderer = renderer;
        
        // create a model
        this._model = new flykit.mvcutils.GenericModel2();
        this._model.setDefinition(flykit.flyatlas.Widget.modelDefinition);
        
        // instantiate the controller
        this._controller = new flykit.flyatlas.Widget.Controller(this._model, service, this);
        
        // connect the renderer to the model
        this._renderer.connect(this._model);
        
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.prototype._init", error);
    }
};


/**
 * @param {String} id affy probe id
 */
flykit.flyatlas.Widget.prototype.getAssaysByAffyProbeId = function( id ) {
    try {
        // pass through to controller
        this._controller.getAssaysByAffyProbeId(id);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.prototype.getAssaysByAffyProbeId", error);
    }
}


/**
 * @param {flykit.flybase.Gene} gene
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.prototype.getAssaysByGene = function( gene ) {
    try {
    	// pass through to controller
    	this._controller.getAssaysByGene(gene);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.prototype.getAssaysByGene", error);
    }
}


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


/**
 * @class
 * @param {flykit.mvcutils.GenericModel2} model
 * @param {flykit.flyatlas.Service} service
 * @param {flykit.flyatlas.Widget} controllee
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.Controller = function( model, service, controllee ) {

    /**
     * @private
     * @type flykit.mvcutils.GenericModel2
     */
    this._model = null;
    
    
    /**
     * @private
     * @type flykit.flyatlas.Service
     */
    this._service = null;
    
    
    /**
     * @private
     * @type flykit.flyatlas.Widget
     */
    this._controllee = null;

    // do initialisation
    this._init(model, service, controllee);
};


/**
 * @param {flykit.mvcutils.GenericModel2} model
 * @param {flykit.flyatlas.Service} service
 * @param {flykit.flyatlas.Widget} controllee
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.Controller.prototype._init = function( model, service, controllee ) {
    this._model = model;
    this._service = service;
    this._controllee = controllee;
};


/**
 * @param {String} affyId
 */
flykit.flyatlas.Widget.Controller.prototype.getAssaysByAffyProbeId = function( affyId ) {
    try {
        // pass through to private implementation
        this._getAssaysByAffyProbeId(affyId, this._getAssaysSuccess(), this._getAssaysFailure());
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.Controller.prototype.getAssaysByAffyProbeId", error);
    }
};


/**
 * @param {String} flybaseId
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.Controller.prototype.getAssaysByGene = function( gene ) {
    try {
        // pass through to private implementation
        this._getAssaysByGene(gene, this._getAssaysSuccess(), this._getAssaysFailure());
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.Controller.prototype.getAssaysByGene", error);
    }
};


/**
 * @private
 * @param {String} affyId
 * @param {Function} success
 * @param {Function} failure
 */
flykit.flyatlas.Widget.Controller.prototype._getAssaysByAffyProbeId = function( affyId, success, failure ) {
    try {
        flykit.debug("in _getAssaysByProbeId, affyId: "+affyId);
        this._model.set("RESULTS", null);
        this._model.set("QUERY", affyId);
        this._model.set("MODE", "PROBE");
        this._model.set("STATE", "PENDING");
        this._service.getAssaysByAffyProbeId(affyId, success, failure);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.Controller.prototype._getAssaysByAffyProbeId", error);
    }
};

/**
 * @private
 * @param {String} flybaseId
 * @param {Function} success
 * @param {Function} failure
 */
flykit.flyatlas.Widget.Controller.prototype._getAssaysByGene = function( gene, success, failure ) {
    try {
        flykit.debug("in _getAssaysByGene, flybaseId: "+gene.flybaseId);
        this._model.set("RESULTS", null);
        this._model.set("QUERY", gene);
        this._model.set("MODE", "GENE");
        this._model.set("STATE", "PENDING");
        this._service.getAssaysByFlybaseGeneId(gene.flybaseID, success, failure);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.Controller.prototype._getAssaysByGene", error);
    }
};


/**
 * @private
 * @type Function
 */
flykit.flyatlas.Widget.Controller.prototype._getAssaysSuccess = function() {
    var self = this;
    return function( groups ) {
        try {
            self._model.set("RESULTS", groups);
            self._model.set("STATE", "READY");
        } catch (error) {
            throw new flykit.UnexpectedException("anonymous callback (from flykit.flyatlas.Widget.Controller.prototype._getAssaysSuccess)", error);
        }
    }
};


/**
 * @private
 * @type Function
 */
flykit.flyatlas.Widget.Controller.prototype._getAssaysFailure = function() {
    var self = this;
    return function( response ) {
        try {
            flykit.err("request failed: "+response.status+" "+response.statusText);
            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 (error) {
            throw new flykit.UnexpectedException("anonymous callback (from flykit.flyatlas.Widget.Controller.prototype._getAssaysFailure)", error);
        }
    }
};


/** 
 * @type Object
 */
flykit.flyatlas.Widget.modelDefinition = {

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

};


/*
 * ----------------------------------------------------------------
 *                             DEFAULT RENDERER
 * ----------------------------------------------------------------
 */
 
 
/**
 * @class
 */
flykit.flyatlas.Widget.DefaultRenderer = function() {

    /** @private */
    this._canvas = null;
    
    /** @private */
    this._pendingPane = null;
    
    /** @private */
    this._resultsPane = null;
    
    /** @private */
    this._resultsSummaryPane = null;
    
    /** @private */
    this._messagePane = null;
        
};


/**
 * @param {Element} canvas
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype.setCanvas = function( canvas ) {
    try {
//    this._canvas = canvas;
        this._canvas = $(canvas);
        this._initCanvas();
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype.setCanvas", error);
    }
};


/**
 * @private
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype._initCanvas = function() {
    try {
        flykit.debug("_initCanvas");
        
        var canvas = this._canvas;
        canvas.addClass("flyatlasWidget");
        
        // setup pending pane
        var pp = $("<p class='pendingPane'>pending...</p>").hide();
        canvas.append(pp);
        this._pendingPane = pp;
    
        // setup results summary pane
        var rsp = $("<p class='resultsSummaryPane'>this text should never be displayed</p>").hide();
        canvas.append(rsp);
        this._resultsSummaryPane = rsp;
        
        // setup results pane
        var rp = $("<div class='resultsPane'></div>").hide();
        canvas.append(rp);
        this._resultsPane = rp;
    
        // message pane
        var mp = $("<p class='messagePane'>this text should never be displayed</p>").hide();
        canvas.append(mp);
        this._messagePane = mp;    
        
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype._initCanvas", error);
    }    
};


/**
 * @param {flykit.mvcutils.GenericModel2} model
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype.connect = function( model ) {
    try {
        model.subscribeAll(this._onModelChanged, this);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype.connect", error);
    }    
};


/**
 * @private
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype._onModelChanged = function( type, args, self ) {
    try {
        var handlers = {
            "STATE":"_onStateChanged",
            "QUERY":"_onQueryChanged",
            "RESULTS":"_onResultsChanged",
            "MESSAGE":"_onMessageChanged", 
            "MODE":"_onModeChanged"
        };
        var handler = handlers[type];
        flykit.debug("handler: "+handler);
        // call the handler
        self[handler](args[0], args[1], args[2]);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype._onModelChanged", error);
    }    
};


/**
 * @private
 * @param {String} from
 * @param {String} to
 * @param {Function} get
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype._onQueryChanged = function( from, to, get ) {
    // do nothing, we will access the value later
};


/**
 * @private
 * @param {String} from
 * @param {String} to
 * @param {Function} get
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype._onModeChanged = function( from, to, get ) {
    // do nothing, we will access the value later
};


/**
 * @private
 * @param {String} from
 * @param {String} to
 * @param {Function} get
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype._onStateChanged = function( from, to, get ) {
    try {
        if ( to == "PENDING" ) {
            this._pendingPane.show();
            this._messagePane.empty().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 (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype._onStateChanged", error);
    }    
};


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

            var query = get("QUERY"); // fetch from model
            var mode = get("MODE"); // fetch from model
            
            flykit.debug("render results summary");
            this._renderResultsSummary(mode, query, to.length);
    
            if (to.length > 0) {
                flykit.debug("render results");
                this._renderResults(to);        
            }    
        }    
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype._onResultsChanged", error);
    }    
};


/**
 * @private
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype._onMessageChanged = function( from, to, get ) {
    try {
        this._messagePane.html(to);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype._onMessageChanged", error);
    }    
};


/**
 * @private
 * Render the results summary pane.
 * @param {String} query the user's query
 * @param {Number} count number of results found
 * @throws flykit.UnexpectedException
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype._renderResultsSummary = function( mode, query, count ) {
    try {
        var content = "found ";
        content += count;
        content += " matching probe";
        content += (count == 0 || count > 1) ? "s " : " ";
        content += "from <a href='http://www.flyatlas.org'>flyatlas.org</a> ("+flykit.flyatlas.provenance+") for ";
        if (mode == "PROBE") {
    		content += "probe "+query;    	
        } else if (mode == "GENE") {
        	content += "gene <strong>"+query.symbols[0]+"</strong>";
        } else {
            flykit.debug("should never be reached");
        	throw {message: "unexpected mode: "+mode};
        }
        if (count>0) {
            content += " ...";
        }
        this._resultsSummaryPane.html(content);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype._renderResultsSummary", error);
    }    
}


/**
 * @private
 * Render the results pane.
 * @param {Array<flykit.flyatlas.AssayGroup>} results
 */
flykit.flyatlas.Widget.DefaultRenderer.prototype._renderResults = function( results ) {
    try {    
        var pane = this._resultsPane;
        
        var content = "<table class='resultsTable'>";
     
    //    flykit.debug("generate header content");  
        
        content += "<thead><tr><th>probe</th><th>tissue</th><th>mRNA signal</th><th>present call</th><th>enrichment</th><th>affy call</th></thead>";
    
        for (var j=0; j<results.length; j++) {
            var result = results[j];
            content += "<tbody>";
            for (var i=0; i<flykit.flyatlas.tissues.length; i++) {
                var tissue = flykit.flyatlas.tissues[i];
    //            flykit.debug("row for tissue "+tissue);
                var assay = result[tissue];
                var classnames = "";
                if (tissue == "whole") {
                	classnames = "top";
                } else {
    	            if (assay.change == "Up") {
    	            	if (assay.ratio > 2) {
    		            	classnames = "callUp xx";
    	            	} else {
    		            	classnames = "callUp x";
    	            	}
    	            } else if (assay.change == "Down") {
    	            	if (assay.ratio < 0.5) {
    		            	classnames = "callDown xx";
    	            	} else {
    		            	classnames = "callDown x";
    	            	}
    	            } else {
    	            	classnames = "callNone";
    	            }
                }
                content += "<tr class='"+classnames+"'>";
    //            content += (tissue == "whole") ? "<tr class='top'>" : "<tr>";
    //            flykit.debug("first cell (probe id)");
                content += (tissue == "whole") ? "<td rowspan='27'><strong><a href='http://flyatlas.org/probeset.cgi?name="+result.affy_id+"'>"+result.affy_id+"</a></strong></td>" : "" ;
    //            flykit.debug("second cell (tissue)");
                content += "<td>"+tissue+"</td>" ;
    //            flykit.debug("third cell (signal)");
                content += "<td>"+assay.mean+" ± "+assay.sem+"</td>" ;
    //            flykit.debug("fourth cell (present)");
                content += "<td>"+assay.present+" of 4</td>" ;
    //            flykit.debug("fifth cell (enrichment)");
                content += (tissue != "whole") ? "<td>"+assay.ratio+"</td>" : "<td> - </td>" ;
    //            flykit.debug("sixth cell (call)");
                content += (tissue != "whole") ? "<td>"+assay.change+"</td>" : "<td> - </td>" ;
                content += "</tr>";
            }
            content += "</tbody>";                
        }    
        
        content += "</table>";
    
    //    flykit.debug(content);
        
        pane.append($(content));
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyatlas.Widget.DefaultRenderer.prototype._renderResults", error);
    }    
};


