/*  $Id: markers.js 10550 2009-09-08 20:29:29Z rallj $
 * ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Authors:  Vlad Lebedev
 *
 * File Description: Visual markers
 *
 */
 
 
var m_MarkersDlg;
var m_MarkerStart = 1; // marker indexes
var m_AllMarkers = {};
var m_AllMarkersRefs = [];
var m_MarkerColors = ['blue', 'red', 'black', 'green', 'cyan', '#FF00FF']; var m_MarkerColorIdx = 0;



function Marker(config, seq_pos, marker_name, lock) { // from sequence position
   this.width = 8;
   this.obj_coords = null;
   this.obj_coords_format = null;
   this.deleted = false;
   this.lock = lock;
   
   this.color = m_MarkerColors[m_MarkerColorIdx++];
   if (m_MarkerColorIdx > m_MarkerColors.length-1) m_MarkerColorIdx = 0;
   
   this.marker_num = m_MarkerStart;
   m_MarkerStart++;
   this.marker_name = marker_name ? marker_name : "Marker " + this.marker_num;

   //
   this.seq_pos = seq_pos;
   //
   
   this.marker_in_views = [];
   this.lines_in_views = [];
   this.renderInAllViews();
}


Marker.getNextName = function() {
   return "Marker " + m_MarkerStart;
}

Marker.prototype.renderInAllViews = function() {
   for (i = 0; i != m_GViews.length; i++) {
      config = m_GViews[i];
      if (config) this.addView(config);
   }
   
   if (m_TextLocator) SeqApp.onSeqViewAddMarker(this);//.marker_num, this.color, this.seq_pos, this.marker_name); // text view   
}

Marker.prototype.addView = function(config) {   
   pan_view = config['type']=='panorama';
   aln_view = config['type']=='alignment';
   
   the_parent = Ext.get(config['the_id']);
   elem_id = 'marker_' + config['idx'] + '_' + this.marker_num;
   
   this.marker_name_trimed = this.marker_name.trimToPix(100);
   this.marker_name_back = this.marker_name_trimed.visualLength() + 2;
   this.marker_name_back_lock = this.marker_name_back + 16;
   
   if (pan_view) {
       tpl = new Ext.Template(
           '<div id="{id}" ext:qtip="{qtip}" class="marker_ball {color}"><div id="{id}_line" class="marker_line" style="border-right:1px solid {color};"></div>',
           '<div ext:qtip="Locked" id="the_{id}_lock" style="display:{lock};" class="marker_locked_ov"></div></div>'
       );
   } else {
       tpl = new Ext.Template(
           '<div id="{id}" ext:qtip="{qtip}" class="marker_half_ball {color}"><div id="{id}_line" class="marker_line" style="border-right:1px solid {color};"></div>',
           '<div id="the_{id}_back" class="marker_label_back" style="{font_size}background-color:{color};width:{label_length}px;"></div>',
           '<div id="{id}_label" ext:qtip="{qtip}" class="marker_label">{trimed_label}',
           ' <img id="the_{id}_lock" style="margin-top:1px;display:{lock};" ext:qtip="Locked" src="{prefix}images/lock.png"></div></div>'
       );
   }
   
   options = {id:elem_id, qtip:this.marker_name, color:this.color, 
      num:this.marker_num, trimed_label:this.marker_name_trimed, label_length:this.lock?this.marker_name_back_lock:this.marker_name_back,
      font_size:m_Portal?'font-size:0.85em;' : '', lock:this.lock?'inline':'none', prefix:m_CGIPrefix
   };
   
   
   if (pan_view) marker = tpl.append(the_parent, options, true);
   else marker = tpl.insertFirst(the_parent, options, true);
   
   if (aln_view) marker.setVisible(false);
   
   if (pan_view) marker.setTop(m_PanHolderSize + 3); // 3 pix from top
   m_AllMarkers[elem_id] = this;

   marker_line = Ext.get(elem_id+'_line');
   if (config['from_cgi']) marker_line.setHeight( config['from_cgi']['h'] - 18 ); // ball size

   pix_pos = SeqApp.seq2PixG(config, config['vis_len_seq'] < 150 ? this.seq_pos+0.5 : this.seq_pos); // direct strand only (graphical view renders +0.5 bases at low resolution)
   pix_pos = pix_pos + config['scroll_pix'] - 1;
   

   marker.setLeft(pix_pos - this.width);
   this.lines_in_views[config['idx']] = marker_line;
   this.marker_in_views[config['idx']] = marker;
}


Marker.prototype.removeView = function(config) {
   elem_id = 'marker_' + config['idx'] + '_' + this.marker_num;
   this.marker_in_views[config['idx']].remove();
   delete m_AllMarkers[elem_id];   
}

Marker.prototype.zoomSeqMarker = function(idx) {
   config = m_GViews[idx];
   config['prev_cgi'] = null; // clear cache
   config['next_cgi'] = null;
   new_len  = 100; //config['vis_len_seq'] / 2000;
   new_from = this.seq_pos - new_len / 2;

   SeqApp.loadGraphicalImage(config, new_from, new_len);
}

Marker.prototype.showSequence = function() {
   from_seq = Math.max(1, this.seq_pos - 949);
   to_seq = Math.min(m_TextViewSize, m_SeqLength)
   
   from_seq = Math.round(from_seq);
   to_seq = Math.round(from_seq + to_seq);
   
   if (Ext.get('seq-dlg')) seqDlg.close(); // close and reopen
   SeqApp.displaySequenceTextDlg(from_seq, to_seq);
}


Marker.prototype.lockMarker = function() {
   this.lock = !this.lock;
   
   // update views
   for (i = 0; i != m_GViews.length; i++) {
      config = m_GViews[i];
      if (!config) continue;
      elem_id = 'the_marker_' + config['idx'] + '_' + this.marker_num + '_lock';
      back_id = 'the_marker_' + config['idx'] + '_' + this.marker_num + '_back';
      the_marker = Ext.get(elem_id);
      the_back   = Ext.get(back_id);
      
      // it's a bit tricky. Marker backgroung is transparent and the label is opaque
      // this means we have to use absolute positioning and resize the background manually to put/remove the lock icon
      if (the_marker) {
          cur_display = the_marker.getStyle('display');
          the_marker.setStyle('display', cur_display == 'none' ? 'inline' : 'none');
          if (config['type'] != 'panorama') {
              the_back.setStyle('width', '' + (this.lock ? this.marker_name_back_lock : this.marker_name_back) + 'px');
          }
      }
   }
   // sequence panel
   seq_marker = Ext.get('seqtext-marker_'+this.marker_num+'_lock');
   if (seq_marker) seq_marker.setStyle('display', this.lock ? 'inline' : 'none');
}

Marker.prototype.deleteMarker = function() {
   for (i = 0; i != m_GViews.length; i++) {
      config = m_GViews[i];
      if (!config) continue;
      elem_id = 'marker_' + config['idx'] + '_' + this.marker_num;
      this.marker_in_views[config['idx']].remove();
      delete m_AllMarkers[elem_id];
   }
   // remove from sequence panel
   seq_marker = Ext.get('seqtext-marker_'+this.marker_num);
   if (seq_marker) seq_marker.remove();
   
   this.deleted = true;
   //SeqApp.markerDS.loadData( SeqApp.getMarkerData() ); // update the datasource
   SeqApp.updateMarkersInfo();
}

Marker.prototype.setCursor = function(elem, cursor) {
   this.getMarker(elem).setStyle('cursor', cursor); 
}
Marker.prototype.getMarker = function(elem) {
   s=elem.split('_');
   return this.marker_in_views[s[1]];
}
Marker.prototype.getSeqPos = function(elem, pix_pos) {
   s = elem.split('_');
   config = m_GViews[s[1]];
   pix = Math.abs(config['scroll_pix']) + pix_pos;
   
   return Math.round( SeqApp.pix2SeqG(config, pix) );
}
Marker.prototype.setName = function(name) {
   this.marker_name = name;
}
Marker.prototype.setSeqPos = function(pos, reload) {
   this.seq_pos = pos;
   
   for (i = 0; i != m_GViews.length; i++) {
      config = m_GViews[i];
      if (!config) continue;
      
      pix_pos = SeqApp.seq2PixG(config, config['vis_len_seq'] < 150 ? this.seq_pos+0.5 : this.seq_pos);
      //pix_pos = SeqApp.seq2PixG(config, this.seq_pos);
      pix_pos = pix_pos + config['scroll_pix'] - 1;
      
      this.marker_in_views[config['idx']].setLeft(pix_pos - this.width);
   }
   if (reload) SeqApp.updateMarkersInfo();
   
   if (reload && m_TextLocator) { // text view
     text_marker = Ext.get('seqtext-marker_'+this.marker_num);
     SeqApp.x_SetSeqTextMarker(text_marker, this.seq_pos);
   }
}

Marker.prototype.setTop = function(h) { this.marker.setTop(h); }


Marker.prototype.movePix = function(elem, delta, validate) {
   marker = this.getMarker(elem);
   new_left = marker.getLeft(true) - delta;
   if (validate) { // validate only when dragging with mouse
      new_left = Math.max(-this.width, new_left);
      new_left = Math.min(m_PanoramaWidth - 2 - marker.getWidth() + this.width, new_left);
   }
   
   this.seq_pos = this.getSeqPos(elem, new_left + this.width)
   marker.setLeft(new_left);
   
   if (validate) { // syncronize other markers
      //SeqApp.markerDS.loadData( SeqApp.getMarkerData() );
      for (i = 0; i != m_GViews.length; i++) {
         config = m_GViews[i];
         if (!config) continue;
         
         m = this.marker_in_views[ config['idx'] ];
         if (m != marker) {
            pix_pos = SeqApp.seq2PixG(config, this.seq_pos);
            pix_pos = pix_pos + config['scroll_pix'] - 1;
            m.setLeft(pix_pos - this.width);
         }
      } 
      
      if (m_TextLocator) { // text view
        text_marker = Ext.get('seqtext-marker_'+this.marker_num);
        if (text_marker) SeqApp.x_SetSeqTextMarker(text_marker, this.seq_pos);
        else SeqApp.onSeqViewAddMarker(this);//.marker_num, this.color, this.seq_pos, this.marker_name);
      }
   }
   //console.log('seq pos: ' + this.seq_pos);
}

Marker.prototype.scrollPix = function(config, delta) {
   marker = this.marker_in_views[ config['idx'] ];
   new_left = marker.getLeft(true) - delta;
   marker.setLeft(new_left);
}

Marker.prototype.updateMarkerSize = function(config) {
   idx = config['idx'];
   if (this.lines_in_views[idx]!=null) {
      marker_line = this.lines_in_views[idx];
   } else {
      this.addView(config);
      marker_line = this.lines_in_views[idx];
   }
   marker_line.setHeight(config['from_cgi']['h'] - 18);
}

Marker.prototype.updateMarkerPos = function(config) {
   marker = this.marker_in_views[ config['idx'] ];
   //pix_pos = SeqApp.seq2PixG(config, this.seq_pos + (config['flip'] ? 0 : 0.5));
   pix_pos = 0;
   if (config['flip']) pix_pos = SeqApp.seq2PixG(config, this.seq_pos - 0.5);
   pix_pos = SeqApp.seq2PixG(config, this.seq_pos + 0.5);
   
   pix_pos = pix_pos + config['scroll_pix'] - 1;
   marker.setLeft(pix_pos - this.width);
}



Marker.prototype.centerInView = function(view_idx) {
   SeqApp.onCenterPos(this.seq_pos, view_idx);
} 

SeqApp.getMarkerData = function() {
   markerData = [];
   for (i=0;i!=m_AllMarkersRefs.length;i++) {
      m = m_AllMarkersRefs[i];
      if (!m.deleted) markerData.push([m, m.marker_num, m.marker_name, m_Origin !=0 ? m.seq_pos-m_Origin+1 : m.seq_pos+1, m.color, m.obj_coords_format]);
   }
   return markerData;
}


SeqApp.renameMarker = function(num) {
    var m = null;
    for (var i = 0;  i != m_AllMarkersRefs.length;  i++) {
         tmp = m_AllMarkersRefs[i];
         if (tmp.marker_num == num) m = tmp;
    }
    if (!m) return; // marker not found
    
    Ext.MessageBox.prompt('Marker', 'Please enter new marker name:', function(btn, text) { 
        if (btn!='ok' || text.length==0) return; 
        m.marker_name = text;
        // change all tooltips and background sizes
        m.marker_name_trimed = m.marker_name.trimToPix(100);
        m.marker_name_back = m.marker_name_trimed.visualLength();
        m.marker_name_back_lock = m.marker_name_back + 16;

        for (i = 0; i != m_GViews.length; i++) {
           config = m_GViews[i];
           if (!config) continue;

           elem_id = 'marker_' + config['idx'] + '_' + m.marker_num;
           back_id = 'the_marker_' + config['idx'] + '_' + m.marker_num + '_back';
           elem_label_id = 'marker_' + config['idx'] + '_' + m.marker_num + '_label';

           marker_div = Ext.get(elem_id);
           the_back   = Ext.get(back_id);
           marker_label_div = Ext.get(elem_label_id);

           marker_div.dom.setAttribute('ext:qtip', m.marker_name);

           if (marker_label_div) {// no labels in overview
              lock_html = marker_label_div.dom.innerHTML;
              lock_html = lock_html.slice(lock_html.indexOf('<'), lock_html.length);

              marker_label_div.update(m.marker_name_trimed + ' ' + lock_html);
              marker_label_div.dom.setAttribute('ext:qtip', m.marker_name );
              the_back.setStyle('width', '' + (m.lock ? m.marker_name_back_lock : m.marker_name_back) + 'px');
           }
        }

        // sequence
        marker_seq = Ext.get('seqtext-marker_'+m.marker_num);
        if (marker_seq) marker_seq.dom.setAttribute('ext:qtip', m.marker_name);

        SeqApp.updateMarkersInfo();
    }, this, false, m.marker_name );
}

SeqApp.setMarkerPosition = function(num) {
    var m = null;
    for (var i = 0;  i != m_AllMarkersRefs.length;  i++) {
        tmp = m_AllMarkersRefs[i];
        if (tmp.marker_num == num) m = tmp;
    }
    if (!m) return; // marker not found
    
    Ext.MessageBox.prompt(m.marker_name, 'Please enter new sequence position:', function(btn, text) { 
       if (btn!='ok' || text.length==0) return; 
       pos = text.replace(/k/, '000').replace(/m/, '000000');
       m.setSeqPos(m_Origin != 0 ? parseInt(pos) + m_Origin : pos - 1, true); 
       SeqApp.updateMarkersInfo();
    }, this, false, m_Origin != 0 ? m.seq_pos-m_Origin : m.seq_pos+1 );
}


SeqApp.deleteMarker = function(num) {
  for (var i = 0;  i != m_AllMarkersRefs.length;  i++) {
      m = m_AllMarkersRefs[i];
      if (m.marker_num == num) {
          m.deleteMarker();
          SeqApp.updateMarkersInfo();
      }
  }
}

SeqApp.parseObjCoords = function(obj_coords_json, marker) 
{
    var data = obj_coords_json;
    var pos  = marker.seq_pos + 1;
    html = '';
    for (var i = 0; i != data.length; i++) {
       if (data[i]['marker_pos'] != pos) continue;
       if (html.length == 0) {
         html += '<table class="objCoords" width="98%">'
         html += '<tr><th><b>Name:</b> <a href="#" onClick="SeqApp.renameMarker('+marker.marker_num+');">' + marker.marker_name;
         html += '</a></th><th colspan=3>Position: ';
         html += '<a href="#" onClick="SeqApp.setMarkerPosition('+marker.marker_num+');">' + pos;
         html += '</a> on ' + data[i]['title']+'<a href="#" onClick="SeqApp.deleteMarker('+marker.marker_num+');" class="marker_remove_link">Remove</a>'
         html += '</th></tr>';
         html +='<tr><th width="25%">Accession</th><th width="15%">Mapped</th><th width="25%">Coordinates</th><th width="35%">Sequence</th></tr>'
       }
       html += '<tr><td>';
       the_pos = data[i]['hgvs_position'];//.replace(/\//, '<br>');
       html += data[i]['title'] + '</td><td>' + data[i]['pos_mapped'] + '</td><td>' + the_pos + '</td><td><span style="font-family:monospace">' + data[i]['sequence']+'</span>';
       html += '</td></tr>';
    }
    html += '</table>';
    return html;
}



SeqApp.getMarkerInfoURL = function() {
   var url = theObjCoordsCGI;
   for (i = 0;  i != m_AllMarkersRefs.length; i++) {
     m = m_AllMarkersRefs[i];
     if (!m.deleted) url += '&pos=' + (m.seq_pos + 1);
   }
   return url;
}    


SeqApp.markerInfoLoadCallback = function(el, success, res, options) {
    var from_cgi = Ext.decode(res.responseText);
    var formated_html = '';

    for (var i = 0;  i != m_AllMarkersRefs.length; i++) {
        var m = m_AllMarkersRefs[i];
        if (m.deleted) continue;
        formated_html += SeqApp.parseObjCoords(from_cgi, m);
    }

    if (formated_html.length == 0) { // no markers to display
        formated_html = '<div style="color:gray;margin-top:10px;" align="center">No Markers To Display</div>';
    }
    el.update(formated_html);
}


SeqApp.updateMarkersInfo = function() {
    var dlg = Ext.getCmp('marker-info-panel');
    if (dlg) dlg.getUpdater().update(
        {url:SeqApp.getMarkerInfoURL(), callback:SeqApp.markerInfoLoadCallback}
    );
}


SeqApp.showMarkersDlg = function() {
   marker_data = SeqApp.getMarkerData();
   if (marker_data.length==0) { // set new marker if no markers
       SeqApp.setNewMarkerDlg(SeqApp.getPanorama(), m_PanoramaWidth/2); 
       return;
   }

   m_MarkersDlg = new Ext.Window({
         layout:'fit',
         title:'Markers',
         minWidth:480, width:600, height:350,
         constrain:true, collapsible:true,
         addMarker: function() { 
            SeqApp.setNewMarkerDlg(SeqApp.getPanorama(), m_PanoramaWidth/2); 
         },
         clearMarkers: function() {
            Ext.MessageBox.confirm('Confirm', 'Clear all markers?', function(btn) {
                if (btn!='yes') return;
                for(m in m_AllMarkers) { if (m_AllMarkers[m]) m_AllMarkers[m].deleteMarker(); }
                SeqApp.updateMarkersInfo();
            });
         },
         tbar:[
           {iconCls:'marker-add', tooltip:'Add New Marker', handler:function() { m_MarkersDlg.addMarker(); }},
           '->',
           {text:'Clear Markers', iconCls:'clear_markers', handler:function() {  m_MarkersDlg.clearMarkers(); }}
         ],
         closeAction:'close',
         items:[{
            xtype:'panel', autoScroll: true, id:'marker-info-panel', layout:'fit',
            autoLoad:{url:SeqApp.getMarkerInfoURL(), callback:SeqApp.markerInfoLoadCallback}
         }],
         buttons: [{text: 'Close', handler: function() {m_MarkersDlg.close(); } }]
   });
   m_MarkersDlg.show(this);
}
