# Source: views/AnnotationView.js

    /*global define */
    define(['jquery',
        'underscore',
        'backbone',
        'text!templates/bioportalAnnotationTemplate.html',],
        function($, _, Backbone, AnnotationPopoverTemplate) {
        'use strict';

        var AnnotationView = Backbone.View.extend(
          /** @lends AnnotationView.prototype */{
            className: 'annotation-view',
            annotationPopoverTemplate: _.template(AnnotationPopoverTemplate),

            el: null,
            context: null,
            propertyLabel: null,
            propertyURI: null,
            valueLabel: null,
            valueURI: null,
            valueDefinition: null,
            valueOntology: null,
            valueOntologyName: null,

            // Stores a reference to the child .annotation el which is handy but
            // I'm no sure it's needed
            // TODO: Rename to popoverReference or something or remove entirley
            popoverSource: null,

            // Helps track visibility of the popover so we know when it's safe to
            // destroy it and update it with new content
            visible: null,

            // Stores whether we successfully looked and did or did not find the
            // definition of the annotation value, either from cache or from
            // Bioportal
            valueResolved: null,

            events: {
                "click" : "onClick",
                "click .annotation-popover-findmore" : "findMore",
                "click .annotation-findmore" : "findMore"
            },

            initialize: function () {
                this.context = this.$el.data('context');
                this.propertyLabel = this.$el.data('propertyLabel');
                this.propertyURI = this.$el.data('propertyUri');
                this.valueLabel = this.$el.data('valueLabel');
                this.valueURI = this.$el.data('valueUri');

                // Decode HTML tags in the context string, which is passed in as
                // an HTML attribute from the XSLT so it needs encoding of some sort
                // Note: Only supports < and > at this point
                if( this.context ){
                  this.context = this.context.replace("&lt;", "<").replace("&gt;", ">");
                }

                this.valueResolved = false;
            },

            render: function () {

              //If there is no value URI, then there is probably no annotation
              // metadata to render, so exit the function.
              if( typeof this.valueURI == "undefined" ){
                return this;
              }

              this.createPopover();

              return this;
            },

            // Helps us fetch data from the API on first interaction
            onClick: function () {
                this.queryAndUpdateValue();
            },

            /**
             * Find a definition for the value URI either from cache or from
             * Bioportal. Updates the popover if necessary.
             */
            queryAndUpdateValue: function () {
                if (this.valueResolved) {
                    return;
                }

                var viewRef = this,
                    cache = MetacatUI.appModel.get("bioportalLookupCache"),
                    token = MetacatUI.appModel.get("bioportalAPIKey");

                // Attempt to grab from cache first

                if (cache && cache[this.valueURI]) {
                    this.valueDefinition = cache[this.valueURI].definition;
                    this.valueOntology = cache[this.valueURI].links.ontology;

                    // Try to get a simpler name for the ontology, rather than just
                    // using the ontology URI, which is all Bioportal gives back
                    this.valueOntologyName = this.getFriendlyOntologyName(cache[this.valueURI].links.ontology);

                    this.updatePopover();
                    this.valueResolved = true;

                    return;
                }

                // Verify token before moving on
                if (typeof token !== "string") {
                    this.valueResolved = true;

                    return;
                }

                // Query the API and handle the response
                // TODO: Looks like we should proxy this so the token doesn't leak
                var url = MetacatUI.appModel.get("bioportalSearchUrl") +
                    "?q=" + encodeURIComponent(this.valueURI) +
                    "&apikey=" +
                    token;

                $.get(url, function (data) {
                    var match = null;

                    // Verify response structure before trusting it
                    if (!data.collection ||
                        !data.collection.length ||
                        !data.collection.length > 0) {
                        return;
                    }

                    // Find the first match by URI
                    match = _.find(data.collection, function(result) {
                        return result["@id"] && result["@id"] === viewRef.valueURI;
                    });

                    // Verify structure of response looks right and bail out if it
                    // doesn't
                    if (!match ||
                        !match.definition ||
                        !match.definition.length ||
                        !match.definition.length > 0) {
                        viewRef.valueResolved = true;
                        viewRef.updatePopover();

                        return;
                    }

                    viewRef.valueDefinition = match.definition[0];
                    viewRef.valueOntology = match.links.ontology;

                    // Try to get a simpler name for the ontology, rather than just
                    // using the ontology URI, which is all Bioportal gives back
                    viewRef.valueOntologyName = viewRef.getFriendlyOntologyName(match.links.ontology);

                    viewRef.valueResolved = true;
                    viewRef.updatePopover();
                    viewRef.updateCache(viewRef.valueURI, match);
                });
            },

            /**
             * Create the Popover for the annotation
             *
             * Note: Has a side-effect of updating this.popoverSource;
             */
            createPopover: function () {
                var new_content = this.annotationPopoverTemplate({
                    context: this.context,
                    propertyLabel: this.propertyLabel,
                    propertyURI: this.propertyURI,
                    valueLabel: this.valueLabel,
                    valueURI: this.valueURI,
                    valueDefinition: this.valueDefinition,
                    valueOntology: this.valueOntology,
                    valueOntologyName: this.valueOntologyName,
                    valueResolved: this.valueResolved
                });

                this.$el.data("content", new_content);

                this.popoverSource = this.$el.popover({
                    container: this.$el,
                    delay: 500,
                    trigger: "click"
                });
            },

            /**
             * Update the popover data and raw HTML. This is necessary because
             * we want to create the popover before we fetch the data to populate
             * it from BioPortal and Bootstrap Popovers are designed to be static.
             *
             * The main trick I had to figure out here was that I could access
             * the underlying content member of the popover with
             * popover_data.options.content which wasn't documented in the API.
             */
            updatePopover: function() {
                var popover_content = $(this.$el).find(".popover-content").first();

                var new_content = this.annotationPopoverTemplate({
                    context: this.context,
                    propertyLabel: this.propertyLabel,
                    propertyURI: this.propertyURI,
                    valueLabel: this.valueLabel,
                    valueURI: this.valueURI,
                    valueDefinition: this.valueDefinition,
                    valueOntology: this.valueOntology,
                    valueOntologyName: this.valueOntologyName,
                    valueResolved: this.valueResolved
                });

                // Update both the existing DOM and the underlying data
                // attribute in order to persist the updated content between
                // displays of the popover

                // Update the Popover first
                //
                // This is a hack to work around the fact that we're updating the
                // content of the popover after it is created. I read the source
                // for Bootstrap's Popover and it showed the popover is generated
                // from the data-popover attribute's content which has an
                // options.content member we can modify directly
                var popover_data = $(this.$el).data('popover');

                if (popover_data && popover_data.options && popover_data.options) {
                    popover_data.options.content = new_content;
                }

                $(this.$el).data('popover', popover_data);

                // Then update the DOM on the open popover
                $(popover_content).html(new_content);
            },

            /**
             * Update the cache for a given term.
             * @param term: (string) The URI
             * @param match: (object) The BioPortal match object for the term
            */
            updateCache: function(term, match) {
                var cache = MetacatUI.appModel.get("bioportalLookupCache");

                if (cache &&
                    typeof term === "string" &&
                    typeof match === "string") {
                    cache[term] = match;
                }
            },

            /**
             * Send the user to a pre-canned search for a term.
             *
             * This gets called either from the popover or from clicking on the pill
             * itself.
             */
            findMore: function(e) {
                e.preventDefault();

                var valueURI,
                    valueLabel,
                    pill = $(e.target).parents(".annotation");

                // Decide whether we clicked from the pill first
                if (pill.length == 1) {
                    valueURI = $(pill).data("value-uri");
                    valueLabel = $(pill).data("value-label");
                } else {
                    valueURI = $(e.target).data("uri");
                    valueLabel = $(e.target).text();
                }

                // Bail out if we didn't get a valueURI to search
                if (typeof valueURI === "undefined") {
                    return;
                }

                // Direct the user towards a search for the annotation
                MetacatUI.appSearchModel.clear();
                MetacatUI.appSearchModel.set('annotation', [{
                    label: valueLabel,
                    value: valueURI
                }]);
                MetacatUI.uiRouter.navigate('data', {trigger: true});
            },

            getFriendlyOntologyName: function(uri) {
                if ((typeof uri === "string")) {
                    return uri;
                }

                return uri.replace("http://data.bioontology.org/ontologies/", "");
            }
        });

        return AnnotationView;
      });