 
// create a namespace if not already defined
flykit.namespace("flykit.flyted");


/*
 * --------------------------------------------------
 * WIDGET
 * --------------------------------------------------
 */


/**
 * Create a flyted image widget
 * @class
 * A widget for displaying images from www.fly-ted.org
 * @constructor
 * @param {flykit.flyted.Service} service the service to use to fetch data
 * @param {flykit.flyted.DefaultRenderer} renderer the renderer to use
 */
flykit.flyted.ImageWidget = function( service, renderer ) {

	try {
		var that = this;
		
		/** @private */
		this._controller = null;
		
		/** @private */
		this._renderer = renderer;
		
		this._model = null;
		
		this._renderer = null;
    
    	this._service = null;
		
		this._init = function() {
			// create a model
			var model = new flykit.mvcutils.GenericModel2();
			model.setDefinition(flykit.flyted.ImageWidget.modelDefinition);
			
			// instantiate the controller
			this._controller = new flykit.flyted.ImageWidget.Controller(model, service, this);
			
			// connect the renderer to the model
			renderer.connect(model);
		};
				
    	// do initialisation
    	this._init(service, renderer);
    } catch (error) {
        throw new flykit.UnexpectedException("flykit.flyted.ImageWidget", error);
    }	
};


/**
 * Find images by gene names used within fly-ted
 * @param {String} flytedGeneName a flyted gene name, see http://www.fly-ted.org/view/geneid/
 */
flykit.flyted.ImageWidget.prototype.findImagesByFlytedGeneName = function( flytedGeneName ) {
	// pass through to controller
	try {
		this._controller.findImagesByFlytedGeneName(flytedGeneName);
	}catch (error) {
        throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.prototype.findImagesByFlytedGeneName", error);
    }
};


/**
 * Find images by flybase gene ID, using gene name translation data
 * @param {String} flybaseGeneID a flybase gene ID, e.g. FBgn123456
 */
flykit.flyted.ImageWidget.prototype.findImagesByFlybaseGeneID = function( flybaseGeneID ) {
	try {
		// pass through to controller
		this._controller.findImagesByFlybaseGeneID(flybaseGeneID);
	}catch (error) {
        throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.prototype.findImagesByFlybaseGeneID", error);
    }
};


/**
 * Find images by a gene object, translating gene names as needed.
 * @param {flykit.flybase.Gene} gene
 */
flykit.flyted.ImageWidget.prototype.findImagesByGene = function( gene ) {
	try {
		// pass through to controller
		this._controller.findImagesByGene(gene);
	}catch (error) {
        throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.prototype.findImagesByGene", error);
    }
};


/*
 * --------------------------------------------------
 * CONTROLLER
 * --------------------------------------------------
 */


/**
 * Create a controller for a flyted image widget.
 * @class
 * A controller class for the flyted image widget internal MVC.
 * @constructor
 * @param {flykit.mvcutils.GenericModel2} model the model to store widget state data
 * @param {flykit.flyted.Service} service the service to use to fetch data
 * @param {flykit.flyted.ImageWidget} widget the widget to control
 */
flykit.flyted.ImageWidget.Controller = function( model, service, widget ) {
	
	var that = this;
	
	/**
	 * @private
	 */
	this._model = model;
	
	/**
	 * @private
	 */
	this._service = service;
	
	/**
	 * @private
	 */
	this._parent = widget;
	
	
	/**
	 * @private
	 * Success case callback.
	 * @param {Array<flykit.flyted.Image>} images
	 */	
	this._findImagesSuccess = function( images ) {
		try {
			flykit.info("request success");
		
			// set the results
			that._model.set("RESULTS", images);
			
			// set the state
			that._model.set("STATE", "READY");
		}catch (error) {
        	throw new flykit.UnexpectedException("_findImagesSuccess", error);
    	}

	};
	
	
	/**
	 * @private
	 * Failure case callback.
	 * @param {Object} response the HTTP response (YUI)
	 */	
	this._findImagesFailure = function( response ) {
		try {
			flykit.err("request failed, status "+response.status+" "+response.statusText);
			
			// set an error message
			var msg = "There was an error retrieving data from FlyTED, see the logs for more info. The server may be busy or down, please try again later. If this message persists, please contact the Image Bioinformatics Research Group at bioimage@mail.ontonet.org.";		
	
			that._model.set("ERRORMESSAGE", msg);
	
			// set the state
			that._model.set("STATE", "SERVERERROR");
		}catch (error) {
        	throw new flykit.UnexpectedException("_findImagesFailure", error);
    	}		
	};
	
};


/**
 * Find images by flyted gene name
 * @param {String} flytedGeneName a flyted gene name, see http://www.fly-ted.org/view/geneid/
 */
flykit.flyted.ImageWidget.Controller.prototype.findImagesByFlytedGeneName = function( flytedGeneName ) {
	try {
		// pass through to private implementation
		this._findImagesByFlytedGeneName(flytedGeneName, this._findImagesSuccess, this._findImagesFailure);
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.Controller.prototype.findImagesByFlytedGeneName", error);
    }
};


/**
 * @private
 * Implement find images by flyted gene name, allowing callback injection for testing
 * @param {String} flytedGeneName a flyted gene name, see http://www.fly-ted.org/view/geneid/
 * @param {Function} success success case callback
 * @param {Function} failure failure case callback
 */
flykit.flyted.ImageWidget.Controller.prototype._findImagesByFlytedGeneName = function( flytedGeneName, success, failure ) {
	try {
		flykit.info("flykit.flyted.ImageWidget.Controller._findImagesByFlytedGeneName :: request: "+flytedGeneName);
		
		// set the model pending
		this._model.set("STATE", "PENDING");
		
		// set the query property
		this._model.set("QUERY", flytedGeneName);
		
		// kick off the request
		this._service.findImagesByFlytedProbeName(flytedGeneName, success, failure);
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.Controller.prototype._findImagesByFlytedGeneName", error);
    }
};


/**
 * Find images by flybase gene ID
 * @param {String} flybaseGeneID a flybase gene ID, e.g. FBgn123456
 */
flykit.flyted.ImageWidget.Controller.prototype.findImagesByFlybaseGeneID = function( flybaseGeneID ) {
	try {
		// pass through to private implementation
		this._findImagesByFlybaseGeneID(flybaseGeneID, this._findImagesSuccess, this._findImagesFailure);
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.Controller.prototype.findImagesByFlybaseGeneID", error);
    }
};


/**
 * @private
 * Implement find images by flybase gene ID, with callback injection for testing.
 * @param {String} flybaseGeneID a flybase gene ID, e.g. FBgn123456
 * @param {Function} success success case callback
 * @param {Function} failure failure case callback
 */
flykit.flyted.ImageWidget.Controller.prototype._findImagesByFlybaseGeneID = function( flybaseGeneID, success, failure ) {
	try {
		flykit.info("flykit.flyted.ImageWidget.Controller._findImagesByFlybaseGeneID :: request: "+flybaseGeneID);
		
		// set the model pending
		this._model.set("STATE", "PENDING");
		
		// set the query property
		this._model.set("QUERY", flybaseGeneID);
		
		// kick off the request
		this._service.findImagesByFlybaseGeneID(flybaseGeneID, success, failure);
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.Controller.prototype._findImagesByFlybaseGeneID", error);
    }
};


/**
 * Find images by flybase gene object
 * @param {flykit.flybase.Gene} gene 
 */
flykit.flyted.ImageWidget.Controller.prototype.findImagesByGene = function( gene ) {
	try {
		// pass through to private implementation
		this._findImagesByGene(gene, this._findImagesSuccess, this._findImagesFailure);
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.Controller.prototype.findImagesByGene", error);
    }
};


/**
 * @private
 * Implement find images by flybase gene object, with callback injection to support testing
 * @param {flykit.flybase.Gene} gene 
 * @param {Function} success success case callback
 * @param {Function} failure failure case callback
 */
flykit.flyted.ImageWidget.Controller.prototype._findImagesByGene = function( gene, success, failure ) {
	try {
		flykit.info("flykit.flyted.ImageWidget.Controller._findImagesByGene :: request: "+gene.flybaseID);
	
		// set the model pending
		this._model.set("STATE", "PENDING");
		
		// set the query property
		this._model.set("QUERY", gene);
		
		// kick off the request
		this._service.findImagesByFlybaseGeneID(gene.flybaseID, success, failure);
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.Controller.prototype._findImagesByGene", error);
    }
};


/*
 * --------------------------------------------------
 * MODEL DEFINITION
 * --------------------------------------------------
 */


/**
 * Definition of flyted imagewidget model.
 */
flykit.flyted.ImageWidget.modelDefinition = {

	properties : [ "STATE", "RESULTS", "QUERY", "ERRORMESSAGE" ],
	
	values : {
		"STATE" : [ "PENDING", "READY", "SERVERERROR", "UNEXPECTEDERROR" ]
	},
	
	initialize : function( data ) {
		data["STATE"] = "READY";
		data["RESULTS"] = null;
		data["QUERY"] = null;
		data["ERRORMESSAGE"] = null;
	}

};


/*
 *
 *
 * --------------------------------------------------
 * DEFAULT RENDERER
 * --------------------------------------------------
 *
 *
 *
 */


/**
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer = function() {
    /** @private */
    this._canvas = null;
    
    /** @private */
    this._pendingPane = null;
    
    /** @private */
    this._resultsPane = null;
    
    /** @private */
    this._resultsSummaryPane = null;
    
    /** @private */
    this._messagePane = null;
};


/**
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype.setCanvas = function( canvas ) {
    try {
	    //this._canvas = canvas;
	    this._canvas = $(canvas);
	    this._initCanvas();
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.DefaultRenderer.prototype.setCanvas", error);
    }
};


/**
 * @private
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._initCanvas = function() {
	try {
	    flykit.debug("_initCanvas");
        
        var canvas = this._canvas;
        canvas.addClass("flytedImageWidget");
        
	    // set up the pending pane
	    var pp = $("<p class='pendingPane'>pending...</p>").hide();
        canvas.append(pp);
        this._pendingPane = pp;
       
	    // set up the message pane
	    var mp = $("<p class='messagePane'>this text should never be displayed</p>").hide();
        canvas.append(mp);
        this._messagePane = mp;    
    
	    // 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;
    
	    // setup explanation pane
	    var exp = $("<p class='explanationPane'>this should never be displayed</p>").hide();
	    canvas.append(exp);
	    this._explanationPane = exp;
	    /*this._explanationPane = document.createElement("p");
	    this._explanationPane.innerHTML = "this should never be displayed";
	    this._canvas.appendChild(this._explanationPane);
        YAHOO.util.Dom.addClass(this._explanationPane, "explanationPane");
        flykit.mvcutils.hide(this._explanationPane);*/
	    
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.DefaultRenderer.prototype._initCanvas", error);
    }
};

/**
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype.connect = function( model ) {
	try {
    	model.subscribeAll(this._onModelChanged, this);
    }catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.DefaultRenderer.prototype.connect", error);
    }

};
/**
 * @private
 * Main callback function for model changes.
 * @param {String} type the name of the model property changed
 * @param {Array} args the callback args
 * @param {flykit.genefinder.DefaultRenderer} self a self reference, to work around callback issues
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._onModelChanged = function(type, args, self) {
    try {
	    var handlers = {
	        "STATE":"_onStateChanged",
	        "QUERY":"_onQueryChanged",
	        "RESULTS":"_onResultsChanged",
	        "ERRORMESSAGE":"_onErrorMessageChanged"
	    };
	    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.flyted.ImageWidget.DefaultRenderer.prototype._onModelChanged", error);
    }
};


/**
 * @private
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._onStateChanged = function( from, to, get ) {
    var _context = "flykit.flyted.ImageWidget.DefaultRenderer.prototype._onStateChanged";
    try {
	    if ( to == "PENDING" ) {
	        this._pendingPane.show();
            this._messagePane.empty().hide();
            this._resultsSummaryPane.hide();
            this._resultsPane.hide();
            this._explanationPane.empty().hide();
		} 
		else if (to == "READY") {
		    this._pendingPane.hide();
            this._messagePane.show();
            this._resultsSummaryPane.show();
            var results = get("RESULTS");
            if (results.length>0) {
                this._resultsPane.show();
                this._explanationPane.show();
            }    
		}
		else if ( to == "SERVERERROR" || to == "UNEXPECTEDERROR" ) {
		    this._pendingPane.hide();
            this._messagePane.show();
            this._resultsSummaryPane.hide();
            this._resultsPane.hide();
            this._explanationPane.empty().hide();
		} 
		else {
		    // this should never happen
	    	throw {name:"flykit.flyted.ImageWidget.UnexpectedStateError", message:"Invalid state: "+newState};
		}
	} catch (error) {
        	throw new flykit.UnexpectedException(_context, error);
    }
};


/**
 * @private
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._onQueryChanged = function( from, to ) {
	try {
    	// store query
    	this._query = to;
    }catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.DefaultRenderer.prototype._onQueryChanged", error);
    }
};


/**
 * @private
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._onResultsChanged = function( from, to ) {
    var _context = "flykit.flyted.ImageWidget.DefaultRenderer.prototype._onResultsChanged";	
	try {
        flykit.debug("empty results summary and explanation panes");
        if (to == null) {
            flykit.debug("empty results summary pane");
            this._resultsSummaryPane.empty();
            flykit.debug("empty results pane");
            this._resultsPane.empty();
            flykit.debug("empty explanation pane");
            this._explanationPane.empty();
        }else{
            flykit.debug("empty results summary pane");
            this._resultsSummaryPane.empty();
            
            flykit.debug("collect probes in results", _context);
    		var probes = [];
    		for (var i=0; i<to.length; i++) {
    		    flykit.util.appendIfNotMember(probes, to[i].probe);
    		}
    		
    	    flykit.debug("render the results summary", _context);
    	    this._renderResultsSummary(this._query, to.length, probes);
    	    
    	    if (to.length > 0) {
    
    	        flykit.debug("render the results "+to.length, _context);
    	        this._resultsPane.append(this._imagesToDivHTML(to)); 
    	
    	    }
    	    
    	}
	} catch (error) {
        	throw new flykit.UnexpectedException(_context, error);
    }

};
  
  
/**
 * @private
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._onErrorMessageChanged = function( from, to) {
    try {
    	this._messagePane.append(to);
    }catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.DefaultRenderer.prototype._onErrorMessageChanged", error);
    }
};





/**
 * @private
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._renderResultsSummary = function( query, count, probes ) {
    var _context = "flykit.flyted.ImageWidget.DefaultRenderer.prototype._renderResultsSummary";
	try {
	    function linkify(l) {
	        return l.replace("/","=2F").replace(" ","_");
	    }
	    
		flykit.debug("building results summary content", _context);
        var explanation = "";
	    var content = "found <span>";
	    content += count;
	    content += "</span> matching image";
	    content += (count == 0 || count > 1) ? "s " : " ";
	    content += "from <a href='http://www.fly-ted.org'>www.fly-ted.org</a> ("+flykit.flyted.provenance+") for ";
	    
	    var labels = [];
	    for (var i=0; i<probes.length; i++) {
	        var probe = probes[i];
	        for (var j=0; j<probe.probeLabels.length; j++) {
	            flykit.util.appendIfNotMember(labels, probe.probeLabels[j]);
	        }
	    }
	    
	    if (query instanceof flykit.flybase.Gene) {
	    	flykit.debug("a flybaseid in the query: " +query, _context);
	        content += "gene <strong>"+query.symbols.join(" / ")+"</strong>";
	        
	    } else {
	    	flykit.debug("not a flybaseid in the query :"+ query, _context);
	        content +=  "query <span>'"+query+"'</span>";
	    }

        if (labels.length > 0) {
            content += " (Fly-TED report";
            content += (labels.length>1)? "s:" : ":";
            for (var i=0; i<labels.length; i++) {
                var l = labels[i];
                content += " <a href='http://www.fly-ted.org/view/geneid/"+linkify(l)+".html'>"+l+"</a>";
                if (i<labels.length-1) {
                    content += ",";
                }                
            }
            content += ") ...";

            flykit.debug("now generate explanation", _context);
            if (probes.length == 1 && probes[0].probeLabels.length==1) {
                flykit.debug("one probe, one label, no explanation needed", _context);
            }
            else if (probes.length == 1 && probes[0].probeLabels.length>1) {
                flykit.debug("deal with probe synonyms", _context);
                explanation += "N.B. the reports at www.fly-ted.org (";
                for (var i=0; i<probes[0].probeLabels.length; i++) {
                    var l = probes[0].probeLabels[i];
                    explanation += "<a href='http://www.fly-ted.org/view/geneid/"+linkify(l)+".html'>"+l+"</a>";
                    if (i<probes[0].probeLabels.length-1) {
                        explanation += ", ";
                    }
                }
                explanation += ") report data on <strong>the same probe</strong>; check probe PCR sequence to verify";
            } else if (probes.length > 1) {
                flykit.debug("deal with multiple hybridisation", _context);
                explanation += "N.B. the reports at www.fly-ted.org (";
                for (var i=0; i<probes.length; i++) {
                    var l = probes[i].probeLabels[0];
                    explanation += "<a href='http://www.fly-ted.org/view/geneid/"+linkify(l)+".html'>"+l+"</a>";
                    if (i<probes.length-1) {
                        explanation += ", ";
                    }
                }
                explanation += ") report data on <strong>different probes</strong> which both amplify transcripts of gene "+query.flybaseID+"; check probe PCR sequence to verify";
            } else {
                flykit.err("encountered unexpected probe / labels combination", _context);
            }

        }
	    this._resultsSummaryPane.append(content);
	    this._explanationPane.append(explanation);
	    
	    
	} catch (error) {
        	throw new flykit.UnexpectedException(_context, error);
    }
};

/**
 * @private
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._imagesToDivHTML = function( images ) {

    try {
	    // build the divs
	    
	    flykit.debug("build div content for images "+images.length);
	    var content = "";
	    for ( var i in images ) {
	        
	        content += this._imageToDivHTML(images[i]);
	        flykit.debug("Generate the img div" + content); 
	    }
	    
	    return content;
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.DefaultRenderer.prototype._imagesToDivHTML", error);
    }
};


/**
 * @private
 * TODO doc me
 */
flykit.flyted.ImageWidget.DefaultRenderer.prototype._imageToDivHTML = function( image ) {
  	try {
	    flykit.debug("build content for image "+image.caption);
	    
	    var alttext = "thumbnail image depicting expression of "+image.caption;
	    
	    var content =   "<div class=\"result\">";
	    content +=          "<a href=\"" + image.flytedURL + "\">";
	    content +=              "<img src=\"" + image.thumbnailURL + "\" alt=\""+alttext+"\">";
	    content +=          "</img></a>"; 
	    content +=          "<p class=\"caption\">"+image.caption+"</p>"
	    content +=      "</div>";
	    
	    return content;
	}catch (error) {
        	throw new flykit.UnexpectedException("flykit.flyted.ImageWidget.DefaultRenderer.prototype._imageToDivHTML", error);
    }
};


