/********************************************************************** 
 * License information ( the MIT license)
 *
 * Copyright (c) 2006 Catherine Beauheim; Stanford University
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *********************************************************************/

/** 
  * Widget.js  Give the widget 5 parameters:  Label, ontology id, ontology root, title, uid. 
  * the result is a label and a input field which is linked to an ontology.  If
  * the root is null, then we start looking in this ontology at the root.  Otherwise
  * we start the search & display at the root node.  The search button, next to the 
  * input area, will show the ontology in a tree form starting at the root, showing the
 **/


   var lookAheadTyping=null;
   var sleeping = 0;

   /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    * Structure of this form:  create (div) is where on the page everything will
    * hang from.  myform is a fieldset with a legend which has a title.  Inside the
    * myform is a table which has one row and 3 cells.  
    * the first cell is a label.
    * the second cell is the text area where the user types in the lookahead terms.
    * the third cell is the searchButton.
    * myform has a div for the dropdown list of type-in term suggestions.
    * below the form, a child of the create(div) is a div for where the ontology
    * tree will appear.
    * Above this form and not part of the Widget is the BrowseOntology List button,
    * the drop down list of available ontologies 
    * The search ontology list button has the id
    * of 'button'.  The ontology drop downlist has the id of 'dropdownlist'.  The SMDService
    * is called with the div of 'dropdownlist' which is where the ontology drop down list
    * is placed and the  --- no more namespace
    * Below the dropdownlist div is a div for the widget with id of 'widget'.  Below the
    * widget is the div for the debugging console.
    *
    * *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
   function  Widget (label, id, ontName, rootId, title, uid) {
       this.mylabel = label;
       this.ontologyId = id;
       this.ontologyName = ontName;
       this.legendTitle = title;
       this.selectedIndex = 0;
       this.treeBrowser = new OntologyTreeBrowser(uid);
       this.uid = uid;
       this.rootNodeId = rootId; 
       this.divrootNode = "rootNodeId" + uid;
       //this.tooltip = new Tooltip();
    }

    Widget.prototype.createHTML = function (div) {

       var sb = new Array();

       this.domEle = div;
       this.domEle.widget = this;
       //make these things unique based on the widget's div
       this.lookAheadTA = "TA" + this.uid;
       this.searchButton = "buttonId" + this.uid;
       this.labelId = "labelId" +this.uid;
       this.legendId = "legendId" + this.uid;
       this.selectList = 'selectList' +  this.uid;
       this.treedivId = "treediv" + this.uid;


       // a table with one row and two columns.  each td contains a fieldset.
       sb[sb.length] = ' <table border="etched" cellpadding=4 >';
       sb[sb.length] = '<tr>';
       sb[sb.length] = '<td width="100%">'; 
       sb[sb.length] = ' <div id="' + this.divrootNode+ '" value="' +this.rootNodeId + '">&nbsp;</div>';
       sb[sb.length] = '<fieldset>';
       sb[sb.length] = '<legend id="' + this.legendTitle + '"> ' + this.legendTitle + '</legend>';


/****  outertable ***/
       sb[sb.length] = ' <table cellpadding=6>';
       sb[sb.length] = ' <tr>';
    //   sb[sb.length] = ' <td id="' + this.labelId + '">'+ this.mylabel + '</td>' ; 


       sb[sb.length]= ' <td width="50%">' ;
       sb[sb.length]= ' <input type="text" size="45" maxlength="120" id="'+this.lookAheadTA+'" termId="0" ';
       sb[sb.length]= ' value="" ';
       sb[sb.length]= ' onkeyup="return  Widget.prototype.tfKeyUpCallback(\'' + this.uid +'\','+ this.ontologyId+')"> ';
       sb[sb.length]= ' </input></td>';
       sb[sb.length]= ' <td width="10%">';
       sb[sb.length]= ' <input type="button" value ="Search" id="' +this.searchButton + '" ';
       sb[sb.length]= ' onclick= "javascript: Widget.prototype.searchClickCallback(\'' +this.uid + '\','+ this.ontologyId+')">';
       sb[sb.length]= ' </input>';
       sb[sb.length]= ' </td>';
       sb[sb.length]= ' </tr>';
       //create another row for the termcompleteList.  it occupies the middle cell
       sb[sb.length]= ' <tr>';
       //sb[sb.length]= ' <td>&nbsp;</td>'; 
       sb[sb.length]= ' <td>'; 
       sb[sb.length]= ' <div id="selectDiv' + this.uid + '"></div>';
       sb[sb.length]= ' </td>';
       sb[sb.length]= ' <td>&nbsp;</td>'; 
       sb[sb.length]= ' </tr>';
       sb[sb.length]= ' </table>';
       sb[sb.length]= ' </fieldset>';
       sb[sb.length]= ' </td>';
       sb[sb.length]= ' </tr>';


       //create a third row that will span across all the columns and will hold
       // the tree searchr.
        sb[sb.length] = ' <tr>';
        sb[sb.length] = ' <td colspan="3" >';
        sb[sb.length] = ' <fieldset>';
        sb[sb.length] = ' <legend >  Search Ontology  </legend>';
        sb[sb.length] = ' <div id="' + this.treedivId + '" ' ;
        sb[sb.length] = ' treeId="' + this.treedivId +'">';
        sb[sb.length] = ' </div>';
        sb[sb.length] = ' <div id="highlight'+this.uid + '" ';
        sb[sb.length] = ' value="0" >';
        sb[sb.length] = ' </div >';
        sb[sb.length] = ' </fieldset>';
        sb[sb.length] = ' </td>';
        sb[sb.length] = ' </tr>'
        sb[sb.length] = ' </table>'

       //create another row or just add a div or a span so that the select list can appear

       //the right side which reserves a space for the tree browser
       // add the hidden values
          
       getElement('ontology' + this.uid).setAttribute ('value', this.ontologyName);
       getElement('label' + this.uid).setAttribute ('value', this.mylabel );


     return sb.join("") ;

    }


    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *
     * react to typing in the text input area.
     * *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
     Widget.prototype.tfKeyUpCallback = function (uid, ontologyId) {
        
        var inputText = getElement('TA'+uid);
         
        if (inputText == null )
              return;

        //var this.tooltip = getElement ('Tooltip' + uid );
        /**
        if (this.tooltip == null ) {
           this.tooltip = new Tooltip();
        }**/
        if (inputText.length == 0 ) {
            sleeping = 0;
            Widget.prototype.clearTypeAheadList (uid);
            SMDService.prototype.clearCache (uid);
            /**
            if (this.tooltip ) {
              this.tooltip.hide();
            } ***/
        }
        else {
            if (sleeping == 1 ) {
               return;
            }
            sleeping = 1;
            //this.tooltip.show ("Sometimes querying takes awhile", getX (inputText), getY (inputText)-20);
            lookAheadTyping = inputText.value ;
            var rootNodeId = getElement ('rootNodeId'+uid).getAttribute ('value');
            Widget.prototype.waitForBusySignal(ontologyId, uid, rootNodeId);
        }
       
     }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
    /* getX and getY are functions to calculate the position of elements on the screen.
     * I am using it for the tooltip.  Does not account for css 'overflow'.  see
     * proquest.safaribooksonline.com/0596101996/jscript5-CHP-16-SECT-2#jscript5-CHP-16-SECT-2.3
    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
       getX = function (ele ) {
           var x = 0;
           while (ele) {
              x += ele.offsetLeft;
              ele = ele.offsetParent;
           }
           return x;
       }
    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
       getY = function (ele ) {
           var y = 0;
           while (ele) {
              y += ele.offsetTop;
              ele = ele.offsetParent;
           }
           return y;
       }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
       Widget.prototype.waitForBusySignal  = function (id , uid, rid) {
             setTimeout ('checkSleepingFlag('+id+', '+uid+', '+rid +')', 50);
       }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
       checkSleepingFlag = function (id, uid, rid) {

        sleeping = 0;
        var ta = getElement('TA' + uid);
        if (lookAheadTyping == ta.value) {
             if (lookAheadTyping.length == 0 ) {
                 Widget.prototype.clearTypeAheadList (uid);
             }
             else {
               var ret = smdService.typeInTermRequest (id, lookAheadTyping, uid, rid);
               if (ret == 1   )   { 
                 if (ta.value.length == lookAheadTyping.length ) 
                    Widget.prototype.waitForBusySignal (id, uid, rid);
               }
             }
        }
        else {
             lookAheadTyping = ta.value;
             sleeping = 1;
             Widget.prototype.waitForBusySignal(id, uid, rid );
         }
       }
    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *
       clear the type ahead list & the text area term id
     * *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
       Widget.prototype.clearTypeAheadList  = function (uid) {

           var ta = getElement('TA' + uid );
           
           
           var list = getElement( 'selectList' + uid );
           if (list) {
               for (var i = list.length-1; i >=0; i--) {
                   list.remove(i);
               }
               list.setAttribute ('size', 1);
               var rootNodeId = getElement ('rootNodeId'+uid).getAttribute ('value');
               ta.setAttribute ('termId', rootNodeId );
               ta.setAttribute ('value', "");
           }
       }
     
    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
       // the term typing in thing needs to clear.  the drop down list needs
       // to clear and the tree browser needs to clear.
    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
       resetWidget = function ( value, uid) {

          var legend = getElement('legendId' + uid);
          this.legendTitle = value;
          legend.innerHTML = this.legendTitle;

          // these are for namespace change
          var eleLabel = getElement('labelId'+uid);
          eleLabel.innerHTML = value;
          var ele = getElement('buttonId'+uid);
          ele.setAttribute ('value', "Browse " + value);

          // in either case we change the lookAheadType area and the drop down list
          var ta = getElement ('TA' + uid);
          ta.value = "";
          var rootNodeId = getElement ('rootNodeId'+uid).getAttribute ('value');
          ta.setAttribute ('termId', rootNodeId);

          var list = getElement('selectList'+uid);
          if (list ) {
              for (var i = list.length-1; i >= 0; i--)
                   list.remove(i);
          list.setAttribute ('size', 0);
          }
          OntologyTreeBrowser.prototype.clearTree(uid);
          

       }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** * 
     * called when a label in the tree is clicked to update the textarea.      * 
     * *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */

     Widget.prototype.updateHiddenAreas = function (data, uid ) {

       try {
          var elterm = getElement('termName'+ uid);
          if (elterm)
             elterm.setAttribute('value', data.name);
          var elacc = getElement('accession'+ uid);
          if (elacc)
             elacc.setAttribute('value', data.accession);
          var ta = getElement('TA' + uid );
          ta.setAttribute ('termId', data.id) ;
          ta.setAttribute ('value', data.name + " " + data.accession) ;
       } catch (e) {
          doDebug ("123   update hidden areas "  + e );
          }
     }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
     Widget.prototype.typeAheadResponse = function (terms, uid) {
       
       var max = 51;
       var showing = 30;
       if (terms.length < max)
          max = terms.length+1;
       if (terms.length < showing )
          showing = terms.length+1;

        var listdiv = getElement ('selectDiv'+uid );
if (listdiv == null )
doDebug ("type ahread response is there a listdiv " + uid);

       //create one blank line, due to how Safari makes default selection
       var sb = new Array();
       sb[sb.length]= '<select id="selectList'+ uid  +'" ';
       sb[sb.length]= ' size="' + showing + '"';
       sb[sb.length]= ' onchange="Widget.prototype.listOnChange(\'' + uid + '\')" >';
       sb[sb.length] = ' <option id ="0">';

       //for (var i = 0; i < terms.length; i++ ) {
       for (var i = 0; i < max-1; i++ ) {
           sb[sb.length] = ' <option id="' + terms[i].id + '"';
           sb[sb.length] = ' accession="' + terms[i].accession + '">' + terms[i].name;
       }
       sb[sb.length]= '</select>';
       listdiv.innerHTML =sb.join("");

     }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
     Widget.prototype.listOnChange = function (uid ) {

        var list = getElement ('selectList'+uid );
        /***
        if (this.tooltip)
             this.tooltip.hide();
        else
            doDebug (" tooltip is null or something " );
        ***/
        var index = list.selectedIndex;
        if (index >= 50 && list.options[index].text == "More terms available...") 
            return;
        var TA = getElement ('TA'+uid);
        var prevId = TA.getAttribute ('termId');
        OntologyTreeBrowser.prototype.updateHighlighting ( list.options[index].getAttribute('id'), uid);
        OntologyTreeBrowser.prototype.getTree (uid).draw();

        var name = list.options[index].text;
        var accession = list.options[index].getAttribute ('accession');
        if (accession != null && accession != 'undefined') {
            name += " ";
            name +=  list.options[index].getAttribute ('accession');
        }
        TA.value = name;
        TA.setAttribute ('termId', list.options[index].getAttribute('id') );
        TA.setAttribute ('accession', list.options[index].getAttribute('accession'));
        list.setAttribute ('size', 1);
        //fill in the hidden fields 
        var el = getElement ('termName' +uid);
        if (el)   
           el.setAttribute  ('value', list.options[index].text);
        else  {
           doDebug ("error in updating the hidden area for term name");
        }
        var ele = getElement ('accession'+uid);
        if (ele)
           ele.setAttribute ('value', list.options[index].getAttribute('accession'));
        //else
         //  doDebug (" 1111 accession element not found for " + list.options[index].getAttribute('accession') + " " + uid);

     }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
     Widget.prototype.searchClickCallback = function(uid, ontologyId) {

         
         //if there is no term, then start at the root, else start with the term
         // with this ontology id.
         
         var TA = getElement ('TA'+uid);
         var termId = TA.getAttribute ('termId') ;
         var value = TA.getAttribute ('value') ;
         var rootNodeId = getElement ('rootNodeId'+uid).getAttribute ('value');

         var query;

         var treediv = getElement('treediv'+uid);
         treediv.setAttribute ('ontId', ontologyId);

         OntologyTreeBrowser.prototype.clearTree(uid);

         if (termId == 0 && rootNodeId == 0 ) {
            query = "?func=getRootTerms&ontologyId=" + ontologyId+"&uid="+uid;
            smdService.sendRequest ( query, "GET", smdService.buildTreeFromRootsResponse);
         }
         else if (termId == 0 && rootNodeId > 0) {
             query = "?func=getChildren&ontologyId=" + ontologyId + "&termId="+ rootNodeId + "&uid="+ uid;
            smdService.sendRequest (query, "GET", smdService.buildTreeFromRootsResponse );

         }
         else if (termId > 0 && rootNodeId == 0 ) {
             query = "?func=getAncestors&termId="+termId + "&uid="+uid;
            smdService.sendRequest (query, "GET", smdService.getAncestorQueryResponse );
         }  
         else if (termId > 0 && rootNodeId > 0 ) {
           /*  HMMMMMM  just in case, do an ancestor query from the term to the root node.  then
            *  do a children query of the immediate parent, which may or may not be the
            *  same as this rootNodeId */

            query = "?func=getAncestors&termId="+termId+"&uid="+uid;
            smdService.sendRequest (query, "GET", smdService.getAncestorQueryResponse );

         }

     }
     

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *
     * create a select list and put it as a child of where                 *
     * called from SMDService.  Creates a drop down list which is inserted
     * in the Element location of "where".  list is an array of whatever
     * be which "assumes that the objects have "toString()" implemented, in order
     * to create the string in the option list.
     * *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */

    Widget.prototype.createSelectList = function (where, showhere, list) {

         this.mylist = document.createElement('select');
         this.mylist.widget = this;
         this.ontologyList = list;
         for (var i=0; i < list.length; i++) {
             option = document.createElement('option');
             option.setAttribute ('id', list[i].id);
             option.innerHTML = list[i].toString() ;
             this.mylist.appendChild (option);
         }
         this.mylist.selectedIndex = 0;  // default
         //where is an element id 
         where.appendChild (this.mylist);
         // this is the initial
         this.ontologyId = this.mylist.options[this.mylist.selectedIndex].id;
     
         this.mylist.onchange = function (){   //list of ontology objects?
             var widget = this.widget;                 
             if (widget.ontologyId == this[this.selectedIndex].id )
                  return;
             
             widget.ontologyId = this[this.selectedIndex].id;
             var node = getElement('TA' + this.widget.uid);
             node.value = this.options[this.selectedIndex].text;

             node.setAttribute ('termId', this.options[this.selectedIndex].id );
             node.setAttribute ('value', this.options[this.selectedIndex].value );
             resetWidget(node.value, this.widget.uid);   
          
         }
         
    }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *
       Widget.prototype.getOntologyId = function () {

           return this.mylist.options[this.mylist.selectedIndex].id;
       }

    /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *
     * *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
    Widget.prototype.showSelectedOntology = function (showhere, list ) {
        showhere.text = list.options[list.selectedIndex].value;
        showhere.value = list.options[list.selectedIndex].value;
        //doDebug (" selected ? " + list.options[list.selectedIndex].value );
    }

