/*  $Id: sequence_text.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: Sequence View Panel
 *
 */
 
 
var m_TextViewRows = 100; // Max of 100 rows in the sequence text panels
var m_FromSequence = 0;
var m_ShowTranslation = true;
var m_ToSequence = 0;
var m_SequenceCols = Ext.isWindows ? 90 : 100; // 10 rows, 10 bases in each row. 100 bases per line (configurable)
var m_SequenceStarts = [];
var m_TextLocator;
var seqtext_xy = [];
var m_SeqFlipStrand = false;
var m_TextViewSize = m_SequenceCols * m_TextViewRows; // load sequence size (for text views)

SeqApp.sequenceViewTmpl = new Ext.Template(
   '<table width="{width}" class="fasta-table" style="border-style:solid;border-width:{border}px;" ><tbody><tr bordercolor="#ffffff"><td id="left_sequence" width=50 valign="top" align="right"><br><em>{left_nums}</em></td>',
   '<td valign="top"><span id="top_sequence"><em>{top_nums}</em></span><br/><div id="text_sequence">{sequence}</div></td>',
   '</tr></tbody></table>'
);
SeqApp.getSeqTextURL = function() {
   return theSequencCGI + '&from=' + m_FromSequence + '&to=' + m_ToSequence + '&col=' + 
            m_SequenceCols + '&translation=' + (m_ShowTranslation ? 'true' : 'false') +
            '&reverse=' + (m_SeqFlipStrand ? 'true' : 'false');
   
}


SeqApp.reloadSeqTextView = function(config) {
   m_FromSequence = Math.round( SeqApp.toSeqP( m_TextLocator.getElement().getLeft(true) ) ) + 1;
   m_ToSequence   = Math.min(m_FromSequence+m_TextViewSize, m_SeqLength);
   Ext.getCmp('seq-panel').getUpdater().update({url:SeqApp.getSeqTextURL(), callback:SeqApp.seqLoadCallback});
}


SeqApp.IsNumeric = function(sText) {
   ValidChars = "-0123456789";
   IsNumber=true;
   for (i = 0; i < sText.length && IsNumber == true; i++) { 
      if (ValidChars.indexOf( sText.charAt(i) ) == -1) IsNumber = false;
   }
   return IsNumber;
}



SeqApp.showHideTranslation = function() {
   m_ShowTranslation = !m_ShowTranslation;
   Ext.getCmp('seq-panel').getUpdater().update({url:SeqApp.getSeqTextURL(), callback:SeqApp.seqLoadCallback});
}

SeqApp.gotoSequenceNext = function() {
   m_FromSequence = Math.min(m_FromSequence + m_TextViewSize, m_SeqLength - m_TextViewSize);
   //m_FromSequence = Math.round(m_FromSequence / 10) * 10 + 1;
   m_FromSequence = Math.max(1, m_FromSequence);
   m_ToSequence   = Math.min(m_FromSequence+m_TextViewSize, m_SeqLength);
   Ext.getCmp('seq-panel').getUpdater().update({url:SeqApp.getSeqTextURL(), callback:SeqApp.seqLoadCallback});
}

SeqApp.gotoSequencePrev = function() {
   m_FromSequence = Math.max(1, m_FromSequence - m_TextViewSize);
   //m_FromSequence = Math.round(m_FromSequence / 10) * 10 + 1;
   m_ToSequence   = Math.min(m_FromSequence+m_TextViewSize, m_SeqLength);
   Ext.getCmp('seq-panel').getUpdater().update({url:SeqApp.getSeqTextURL(), callback:SeqApp.seqLoadCallback});
}

SeqApp.sequenceShowGap = function() {
   m_ShowGap = !m_ShowGap;
   Ext.getCmp('seq-panel').getUpdater().refresh(SeqApp.seqLoadCallback);
}

SeqApp.gotoSeqPositionDlg = function() {
   Ext.MessageBox.prompt('Go to position', 'Please enter sequence position:', function(btn, text) {
      if (btn!='ok'  || text.length==0) return;
      if (!SeqApp.IsNumeric(text)) 
         Ext.MessageBox.alert('Go to position', 'Invalid position.');
      else {
         if (m_Origin != 0) text = parseInt(text) + m_Origin;
         m_FromSequence = Math.min(text, m_SeqLength - m_TextViewSize);
         m_FromSequence = Math.max(1, m_FromSequence);
         m_ToSequence   = Math.min(m_FromSequence+m_TextViewSize, m_SeqLength);
         Ext.getCmp('seq-panel').getUpdater().update({url:SeqApp.getSeqTextURL(), callback:SeqApp.seqLoadCallback});
      }
   }, this, false); 
}

SeqApp.seqLoadCallback = function(el, success, res, options) {
   from_cgi = Ext.decode(res.responseText);

   starts = from_cgi['starts']
   fromindex = 0
   m_SequenceStarts = [];
   while (true) { // build vertical positions array
      newindex = starts.indexOf("<", fromindex)
      if (newindex == -1) break;
      a_char = starts.substr(newindex + 1, 1)
      if (a_char=="d") {
         m_SequenceStarts.push(-1);
         fromindex = newindex + 6;
      } else if (a_char=="b") {
         m_SequenceStarts.push( parseInt( starts.substr(fromindex, newindex - fromindex) ) );
         fromindex = newindex + 4;
      } else if (a_char=="/") {
         fromindex = newindex + 6;
      } else {
         break;
      }
   }
   

   top_nums = "";
   for (i = 0; i != m_SequenceCols / 10; i++) {
       if (m_SeqFlipStrand) top_nums += '<span>0</span>987654321';
       else top_nums += '123456789<span>0</span>';
   }
   
   html = SeqApp.sequenceViewTmpl.apply({border:0, width:'95%', left_nums:starts, top_nums:top_nums, sequence:from_cgi['sequence']});
   el.update(html);
   
   // Add tooltips
   idx = 1;
   while (true) {
      tip_elem = Ext.get('ttl-id'+idx);
      if (!tip_elem) break;
      idx_array = tip_elem.dom.getAttribute('rel').split(',');
      feat_idx = idx_array[idx_array.length-1];
      sig = "";
      for (j=0; j!=from_cgi['features'].length; j++) {
         feat = from_cgi['features'][j];
         if (feat['id']==feat_idx) {sig=feat['object_id']; break; }
      }
      if (sig.length > 0) {
         qtip = new Ext.ToolTip({
              target:tip_elem, trackMouse:false, 
              autoWidth:true, autoHide:true,
              autoLoad:{url: theObjInfoCGI+'?id='+sig, callback:function(el, success, res, options) {
                 objs = Ext.decode(res.responseText);
                 obj = objs[0];
                 el.update(obj['text']);
              }},
              dismissDelay:5000
         });
      }
      idx++;
   } // while

   
   //m_FromSequence = from_seq; length
   //m_ToSequence = to_seq;
   
   Reflection.reCreateReflections(); // show reflections
   
   // add markers
   for (m_idx=0;  m_idx != m_AllMarkersRefs.length; m_idx++) {
      m = m_AllMarkersRefs[m_idx];
      if (!m.deleted) SeqApp.onSeqViewAddMarker(m);//.marker_num, m.color, m.seq_pos, m.marker_name);
   }

   // add events
   Ext.get('seq-dlg').on({ 'mousedown':SeqApp.onSeqViewMouseDown, 'mouseup':SeqApp.onSeqViewMouseUp, 'mousemove':SeqApp.onSeqViewMouseMove, 'contextmenu':SeqApp.onSeqViewContextMenu});
   
   // adjust locator
   m_TextLocator.getElement().setLeft( SeqApp.toPixP(m_FromSequence) );
   m_TextLocator.getElement().setWidth( SeqApp.toPixP(m_ToSequence - m_FromSequence)-2 ); // 2px if inner width (inclusive range)
   m_TextLocator.getElement().setHeight(m_PanoramaHeight-2);
} // SeqApp.seqLoadCallback


SeqApp.onSeqViewAddMarker = function(the_marker) {//num, color, seq_pos, name) {
   seq_elem = Ext.get('text_sequence');
   if (seq_elem) {
      tmpl = new Ext.Template('<div orid_id="marker_0_{num}" id="seqtext-marker_{num}" ext:qtip="{name}" class="marker_ball {color}"><div ext:qtip="Locked" id="seqtext-marker_{num}_lock" style="display:{lock};" class="marker_locked_ov"></div> </div>');
      marker = tmpl.append(seq_elem, {num:the_marker.marker_num, 
            color:the_marker.color, name:the_marker.marker_name, lock:the_marker.lock?'inline':'none'}, true);
      SeqApp.x_SetSeqTextMarker(marker, the_marker.seq_pos);
   }
}


SeqApp.onSeqViewMouseDown = function(e) {
   if (e.button == 0) {
      elem = e.getTarget().id;
      if (elem.indexOf('seqtext-marker_') != 0) return;
      
      // check for lock
      idx = elem.split('_')[1];
      the_marker = m_AllMarkers['marker_0_' + idx];  
      if (the_marker.lock) return;
      
      // ok, ready to drag
      seqtext_xy=e.getXY(); 
      can_marker=true;
      cur_gv_target = Ext.get(elem);
      cur_gv_target.setStyle('cursor', 'move');
   }
}

SeqApp.onSeqViewMouseUp = function(e) {
   elem = e.getTarget().id;
   if (can_marker) {
      cur_gv_target.setStyle('cursor', 'pointer');
      
      seq_pos = SeqApp.x_SeqText2SeqPos(cur_gv_target.getTop(true), cur_gv_target.getLeft(true));
      SeqApp.x_SetSeqTextMarker(cur_gv_target, seq_pos);
      
      orig = cur_gv_target.dom.getAttribute('orid_id');
      marker = m_AllMarkers[orig];
      marker.setSeqPos( seq_pos, true );
   }
   can_marker = false;
}

SeqApp.onSeqViewContextMenu = function(e) {
   e.preventDefault();
   e.stopPropagation();

   elem = e.getTarget().id;
   if (elem.indexOf('seqtext-marker') == 0) {
      menu = new Ext.menu.Menu();
      idx = elem.split('_')[1];
      marker = m_AllMarkers['marker_0_' + idx];
      menu.add(marker.marker_name);
      menu.add('-');
      menu.add({text:'Set To Position...', scope:this, handler:function() { 
         Ext.MessageBox.prompt('Marker', 'Please enter new sequence position:', function(btn, text) { 
            if (btn!='ok' || text.length==0) return; 
            marker.setSeqPos(m_Origin != 0 ? parseInt(text) + m_Origin : text-1, true); 
         }, this, false, m_Origin != 0 ? marker.seq_pos-m_Origin : marker.seq_pos+1 ); }
      });      
      menu.add('-');
      menu.add({iconCls:'marker-remove', scope:this, text:'Remove Marker', handler:function() { marker.deleteMarker(); } });
      menu.add({iconCls:'markers', text:'Marker Details', handler:function() { SeqApp.showMarkersDlg(); } });

      menu.showAt(e.getXY());
   }
}

SeqApp.onSeqViewMouseMove = function(e) {
   if (can_marker) { // Markers
      delta_x = seqtext_xy[0] - e.getXY()[0];
      delta_y = seqtext_xy[1] - e.getXY()[1];
      seqtext_xy = e.getXY(); // save new values
      
      SeqApp.x_ClearBrowserSelection(); // clear selection
      
      new_left = cur_gv_target.getLeft(true) - delta_x;
      new_top = cur_gv_target.getTop(true) - delta_y;
      
      starts_width = Ext.get('left_sequence').getWidth() + 2;
      line_width = document.getElementById('top_sequence').offsetWidth;
      
      new_left = Math.min(new_left, starts_width + line_width-7);
      new_left = Math.max(new_left, starts_width);
      
      new_top = Math.min(new_top, Ext.get('text_sequence').getHeight());
      new_top = Math.max(new_top, Ext.get('top_sequence').getHeight()+1);

      seq_pos = SeqApp.x_SeqText2SeqPos(new_top, new_left);
      cur_gv_target.setLeft(new_left);
      cur_gv_target.setTop(new_top);      
         
      orig = cur_gv_target.dom.getAttribute('orid_id');
      marker = m_AllMarkers[orig];
      marker.setSeqPos( seq_pos-1, false );
   } 
}

SeqApp.x_SetSeqTextMarker = function(marker_elem, seq_pos) {
   if (seq_pos < m_FromSequence-1  ||  seq_pos > m_ToSequence) {
      marker_elem.setVisible(false);
      return;
   } else {
      marker_elem.setVisible(true);
   }

   starts_width = Ext.get('left_sequence').getWidth() + 2;
   line_width = document.getElementById('top_sequence').offsetWidth;
   one_width = line_width / m_SequenceCols; 
   
   one_height_seq = SeqApp.x_GetLineHeight();
   one_height_trans = one_height_seq + 1;

   marker_y = 2; row_start = 0;
   if (m_SeqFlipStrand) {
       for (i = 0; i != m_SequenceStarts.length; i++) {
           row_start = m_SequenceStarts[i];
           marker_y += (row_start == -1 ? one_height_trans : one_height_seq);

           if (row_start == -1) continue;
           if (seq_pos >= row_start - m_SequenceCols  &&  seq_pos < row_start) break;
       }
       pos_h = row_start - seq_pos - 1;
       marker_elem.setLeft(starts_width + one_width * pos_h  - 1);
   } else {
       for (i = 0; i != m_SequenceStarts.length; i++) {
           row_start = m_SequenceStarts[i];
           marker_y += (row_start == -1 ? one_height_trans : one_height_seq);

           if (row_start == -1) continue;
           if (seq_pos >= row_start  &&  seq_pos < row_start + m_SequenceCols) break;
       }
       marker_elem.setLeft(starts_width + one_width * (seq_pos - row_start)  - 1);
   }
   marker_elem.setTop(marker_y - 2);
}


SeqApp.x_SeqText2SeqPos = function(new_top, new_left) { // sequence 2 screen (text view)
   starts_width = Ext.get('left_sequence').getWidth() + 2;
   line_width = document.getElementById('top_sequence').offsetWidth;
   one_width = line_width / m_SequenceCols; 
   
   one_height_seq = SeqApp.x_GetLineHeight();
   one_height_trans = one_height_seq + 1;
   

   seq_pos = 0;
   x = 0; y = 0;
   row_start = 0;
   new_top = new_top - (one_height_seq + 2);
   prev_row_start = -1;
      
   half = one_height_seq / 2;

   for (i = 0; i != m_SequenceStarts.length; i++) {
      row_start = m_SequenceStarts[i];
      if (row_start == -1) {
         y += one_height_trans;
         if (new_top <= y-half) break;
         else continue;
      }
      prev_row_start = row_start;
      if (new_top >= y-half  &&  new_top < y + one_height_seq-half) break;
      y += one_height_seq;
   }
   x = Math.ceil((new_left - starts_width) / one_width);
   
   if (m_SeqFlipStrand) return prev_row_start - x - 1;
   else return prev_row_start + x;
}


SeqApp.x_ClearBrowserSelection = function() {
   //if (Ext.isIE) document.selection.empty();
   //else window.getSelection().removeAllRanges();
   var sel;
   if (document.selection && document.selection.empty) {
      document.selection.empty() ;
   } else if(window.getSelection) {
       sel=window.getSelection();
       if(sel && sel.removeAllRanges) sel.removeAllRanges() ;
   }
}


SeqApp.createSequenceText = function(parent_idx) {
   if (Ext.get('seq-dlg')) return;
   if (parent_idx == -1) { // no parent
      from_seq = Math.max(1, m_SeqLength / 2 - m_TextViewSize / 2);
      from_seq = Math.round(from_seq/10) * 10 + 1;
      to_seq = Math.min(m_TextViewSize, m_SeqLength)
   } else { 
      parent_cfg = m_GViews[parent_idx];
      m_SeqFlipStrand = parent_cfg['flip'];
      
      vis_range = SeqApp.toSeqG(parent_cfg);
      from_seq = Math.max(1, vis_range[0] + vis_range[2] / 2 - m_TextViewSize / 2);
      from_seq = Math.round(from_seq/10) * 10 + 1;
      to_seq   = Math.min(m_TextViewSize, m_SeqLength);
   }
   from_seq = Math.round(from_seq);
   to_seq = Math.round(from_seq + to_seq);

   SeqApp.displaySequenceTextDlg(from_seq, to_seq);
}


SeqApp.displaySequenceTextDlg = function(from_seq, to_seq) {
   m_FromSequence = from_seq;
   m_ToSequence = to_seq;
   m_TextLocator = new Locator('S', '000000', false); // add locator 
   
   seqDlg = new Ext.Window({
       layout:'fit', title:'Sequence View',
       minWidth:480, width:850, height:450,minHeight:200,
       constrain:false,collapsible:true, maximizable:true, hideAction:'close',
       onEsc:function() { seqDlg.close(); },
       listeners:{ 
          'resize': function(window, width, height) {
             cur_top = document.getElementById('top_sequence');
             if (!cur_top) return;
             starts_width = Ext.get('left_sequence').getWidth() + 40;
             line_width = cur_top.offsetWidth;
             one_width = line_width / m_SequenceCols;
             m_SequenceCols = Math.floor((width - starts_width) / one_width / 10) * 10;
             m_TextViewSize = m_SequenceCols * m_TextViewRows;
             m_ToSequence = m_FromSequence + m_TextViewSize;
             m_ToSequence = Math.min(m_ToSequence, m_SeqLength);
             
             Ext.getCmp('seq-panel').getUpdater().update({url:SeqApp.getSeqTextURL(), callback:SeqApp.seqLoadCallback});
          },
          'move': function(win, x, y) { if (y < 0) seqDlg.setPagePosition(x, 0); },
          'close': function() { 
             if (m_TextLocator) m_TextLocator.getElement().remove(); // remove locator form panoram
             m_TextLocator = null;
             Reflection.reCreateReflections();
          }
       },
       id:'seq-dlg',
       tbar:[
          {text:'Prev Page', iconCls:'prev', handler:SeqApp.gotoSequencePrev},'-',
          {text:'Next Page', iconCls:'next', handler:SeqApp.gotoSequenceNext},'->',
          //{text:'Show Gap', iconCls:'show_gap', handler:SeqApp.sequenceShowGap},'-',
          {tooltip:'Printer-Friendly Page', iconCls:'printer', handler:function() { SeqApp.showPrintPageDlg(); }}, 
          '-',
          {text:'Flip Strands', id:'seq-flip-button', tooltip:'Flip Sequence Strands', iconCls:'flip-strands', pressed:m_SeqFlipStrand, enableToggle:true, handler:function() {SeqApp.flipSeqPanelStrand();} },
          '-',
          {tooltip:'Show Translation', enableToggle:true, pressed:m_ShowTranslation, iconCls:'show_translation', scope:this, handler:SeqApp.showHideTranslation},
          '-',
          {text:'Go To Position', iconCls:'goto_position', handler:SeqApp.gotoSeqPositionDlg}
       ],
       modal:false,
       items:[{
         xtype:'panel', autoScroll: true, id:'seq-panel', layout:'fit',
         autoLoad:{url:SeqApp.getSeqTextURL(), callback:SeqApp.seqLoadCallback}
       }],
       buttons: [{text: 'Close', handler: function() { seqDlg.close(); } }]
   });
   seqDlg.show(this);      
}


SeqApp.flipSeqPanelStrand = function() {
    m_SeqFlipStrand = !m_SeqFlipStrand;
    Ext.getCmp('seq-panel').getUpdater().update({url:SeqApp.getSeqTextURL(), callback:SeqApp.seqLoadCallback});
}

SeqApp.x_GetLineHeight = function() {
  tm = Ext.util.TextMetrics.createInstance( Ext.get('top_sequence') );
  return tm.getHeight("A");
}
