/*

 rubber.js -- a base class for drag/rubber-band selection in gbrowse

 Sheldon McKay <mckays@cshl.edu>
 $Id$

*/

var currentSelectArea;
var selectAreaIsActive;
var lefttemp; 

// Constructor
var SelectArea = function () {
    this.background = 'yellow';
    this.unit      = 'bp';
    this.divider   = 1;
  return this;
}

// replace image or image button that will conflict with drag selection
SelectArea.prototype.replaceImage = function(image) {
  var src    = image.getAttribute('src');
  var name   = image.getAttribute('name');
  var isIE   = document.all && !window.opera; 


  var id = image.getAttribute('id');
  var width  = this.elementLocation(image,'width');
  var height = this.elementLocation(image,'height');
  var top    = this.elementLocation(image,'y1');
  var left   = this.elementLocation(image,'x1');

  var p = image.parentNode;
  p.removeChild(image);

  image = this.createAndAppend('span',p,id);
  image.setAttribute('name',name);

  // escape backslashes that may appear in the src attribute
  src = escape(src.replace(/\\/g,"\\\\"));

  image = Element.extend(image);
  image.setStyle({ background: 'url('+src+') top left no-repeat',
                   width: width+'px',
                   height: height+'px',
                   display: 'block',
                   cursor: 'text' 
  });


  if (   !document.searchform 
      || !document.searchform.drag_and_drop 
      || !document.searchform.drag_and_drop.checked) {
    var name = this.imageId+'_map';
    var map;
    
    //IE but this time it is the DOM compliant one
    if (isIE) {
      var spans  = document.getElementsByTagName('span');
      var map = new Array;
      for (var n=0;n<spans.length;n++) {
        if (spans[n].name == name) {
          map.push(spans[n]);
        }
      }
      // Why? I really don't know!
      top  = top  - 6;
      left = left - 6;
    }
    else {
      map = document.getElementsByName(name);
    }

    if (map && map.length) {
      for (var n=0;n<map.length;n++) {
        var newTop   = this.elementLocation(map[n],'y1') + top;
        var newLeft  = this.elementLocation(map[n],'x1') + left;
        map[n].setStyle({ top: newTop+'px',
                          left: newLeft+'px'
        });
      }
    }
  }

  return image;
}

SelectArea.prototype.recenter = function(event) {
  var self = currentSelectArea;
  self.loadSegmentInfo();
  var deltaPixelStart      = self.selectPixelStart - self.pixelStart;
  var deltaSequenceStart   = deltaPixelStart * self.pixelToDNA;

  var coord  = self.flip ? Math.round(self.segmentEnd - deltaSequenceStart)
                         : Math.round(self.segmentStart + deltaSequenceStart);

  var end  = self.segmentEnd;
  var span = Math.abs(TrackPan.viewable_segment_length);
  var half = Math.round(span/2);

  // don't fall off the ends
  if (coord < 0)   coord = half + 1;
  if (coord > end) coord = end - half - 1;
  var start  = coord - half;
  var end    = coord + half;

  if (start > end){ 
    var tmp = end;
    end = start;
    start = tmp;
  }
  
  if (start >= this.detailStart && end <= this.detailEnd) {
    // The segment is already loaded - just scroll to it
    var scroll_to = TrackPan.position_from_start(start);
    TrackPan.update_pan_position(scroll_to); 
  } else {
    self.currentSegment = self.ref + ':' + start + '..' + end;
    if (document.searchform) {
      document.searchform.name.value = self.currentSegment;
    }
    self.submit();
  }
}

// Cross-browser element coordinates
SelectArea.prototype.elementLocation = function(el,request) {
  var offset = $(el).cumulativeOffset();
  var dimensions = $(el).getDimensions();
  switch(request) {
    case ('y1') : return offset.top;
    case ('y2') : return offset.top + dimensions.height;
    case ('x1') : return offset.left;
    case ('x2') : return offset.left + dimensions.width;
    case ('width')  : return dimensions.width;
    case ('height') : return dimensions.height;
 }
}


// Cross-browser event coordinates
SelectArea.prototype.eventLocation = function(event,request) {
  var e = event || window.event;
  if (request == 'x') {
    return Event.pointerX(e) || Event.clientX(e) + document.body.scrollLeft + document.documentElement.scrollLeft;
   
    
  }
  else if (request == 'y') {
    return Event.pointerY(e) || Event.clientY(e) + document.body.scrollTop  + document.documentElement.scrollTop;
 
  }
  else {  
    return false;
  }
}

// Fired when there is a mousedown between the top and bottom
// of the selectable image -- horizontal position does not matter
SelectArea.prototype.startRubber = function(self,event) {
  // only one select area is active at a time, so let the subclass take possession

  self.loadSegmentInfo();

  currentSelectArea = self;

  // suppress all popup balloon while drag-select is active
  Balloon.prototype.hideTooltip(1);  
  balloonIsSuppressed = true;

  // disable help balloon after first selection is made.
  var nullfunc = function(){return false};
  self.scalebar.onmouseover = nullfunc;
 
  // set the selectbox bgcolor
  self.setOpacity(self.selectBox,self.opacity||0.5);  

  // deal with drag/select artifacts
  self.disableSelection(self.selectLayer);

  self.selectPixelStart = self.eventLocation(event,'x') - self.elementLocation(self.selectLayer,'x1');

  self.selectBox.setStyle({ visibility: 'hidden',
                            left: self.selectPixelStart+'px',
                            width: '2px',
                            textAlign: 'center',
                            visibility: 'hidden' 
  });

  var spanReport = Element.extend(self.spanReport || self.createAndAppend('p',self.selectBox,'spanReport'));
  spanReport.setStyle({ color: self.fontColor||'black',
                        marginTop: self.marginTop||'0px',
                        background: 'transparent',
                        font: 'normal bold 14px sans-serif'
  });

  spanReport.innerHTML = ' ';
  self.spanReport = spanReport;
  selectAreaIsActive = true;
}

SelectArea.prototype.cancelRubber = function() {
  var self = currentSelectArea || new SelectArea;
  balloonIsSuppressed = false;

  if (!self.selectBox) return false;
  
  self.selectBox.setStyle({visibility:'hidden'});
  self.selectMenu.setStyle({visibility:'hidden'});
  selectAreaIsActive = false;

  if (self.originalLandmark) {
    document.searchform.name.value = self.originalLandmark;
  }
  self.moved = false;
}

SelectArea.prototype.round = function(nearest,num) {
  if (num > nearest) {
    num = Math.round(num/nearest)*nearest;
  }
  return num;
} 

SelectArea.prototype.moveRubber = function(event) {
  if (!selectAreaIsActive) return false;

  var self = currentSelectArea;
  var selectPixelStart = self.selectPixelStart;
  var selectPixelEnd   = self.eventLocation(event,'x') - self.elementLocation(self.selectLayer,'x1');
   lefttemp   = selectPixelEnd;
  var selectPixelWidth = Math.abs(selectPixelStart - selectPixelEnd);

  var rev, left;
  if (selectPixelStart > selectPixelEnd) {
    rev  = true;
    left = selectPixelEnd;
    self.selectPixelStart = left;
  }
  else {
    left = selectPixelStart;
  }

  // Coordinates of selected sequence
  var deltaPixelStart      = left - self.pixelStart;
  var deltaSequenceStart   = deltaPixelStart * self.pixelToDNA;
  self.selectSequenceStart = self.flip ? Math.round(self.segmentEnd - deltaSequenceStart) 
                                       : Math.round(self.segmentStart + deltaSequenceStart);
  var selectSequenceWidth  = Math.round(selectPixelWidth * self.pixelToDNA);
  self.selectSequenceEnd   = self.flip ? self.selectSequenceStart - selectSequenceWidth 
                                       : self.selectSequenceStart + selectSequenceWidth;

  var segmentLength = Math.abs(self.segmentEnd - self.segmentStart);
  
  // Round the sequence coordinates to the nearest appropriate 10x
  if (segmentLength > 1000000) {
    self.selectSequenceStart = self.round(10000,self.selectSequenceStart);
    self.selectSequenceEnd = self.round(10000,self.selectSequenceEnd);
  }
  else if (segmentLength > 50000) {
    self.selectSequenceStart = self.round(1000,self.selectSequenceStart);
    self.selectSequenceEnd = self.round(1000,self.selectSequenceEnd);
  }
  else if (segmentLength > 5000) {
    self.selectSequenceStart = self.round(100,self.selectSequenceStart);
    self.selectSequenceEnd = self.round(100,self.selectSequenceEnd);
  }
  else if (segmentLength > 500) {
    self.selectSequenceStart = self.round(10,self.selectSequenceStart);
    self.selectSequenceEnd = self.round(10,self.selectSequenceEnd);
  }
  
  selectSequenceWidth = Math.abs(self.selectSequenceEnd - self.selectSequenceStart);

  // Don't allow the start to be > the end
  if (self.selectSequenceStart > self.selectSequenceEnd){ 
    var tmp = self.selectSequenceEnd;
    self.selectSequenceEnd = self.selectSequenceStart;
    self.selectSequenceStart = tmp;
  }

  // reset the value of the 'name' input box
  self.currentSegment = self.ref +':'+self.selectSequenceStart+'..'+(self.selectSequenceEnd-1);
  if (document.searchform) {
    document.searchform.name.value = self.currentSegment;
  } 

  // size and appearance of the "rubber band" select box
  self.selectBox.setStyle({left: left+'px',
                           width: selectPixelWidth+'px',
                           visibility: 'visible'
  });

  // warning if max segment size exceeded
  var tooBig;
  if (self.max_segment && selectSequenceWidth > self.max_segment) {
    self.setOpacity(self.selectBox,self.opacity||0.5,'red');
    self.overrideAutoSubmit = true;
    tooBig = true;
  }
  else {
    self.setOpacity(self.selectBox,self.opacity||0.5);
    self.overrideAutoSubmit = false;
  }

  var unit     = self.unit;
  var divider  = self.divider;
  selectSequenceWidth /= divider;
  if (selectSequenceWidth > 1000 && selectSequenceWidth < 1000000) {
    selectSequenceWidth = selectSequenceWidth/1000;
    unit = 'k'+unit;
  }
  else if (selectSequenceWidth > 1000000) {
    selectSequenceWidth = selectSequenceWidth/1000000;
    unit = 'M'+unit;
  }

  if (Math.floor(selectSequenceWidth) != selectSequenceWidth) {
    selectSequenceWidth = selectSequenceWidth.toFixed(2);
  }

  if (selectPixelWidth > 20) {
    self.spanReport.innerHTML = selectSequenceWidth+' '+unit;
  }
  else {
    self.spanReport.innerHTML = ' ' ;
  }

  self.selectPixelStart = selectPixelStart;

  // Only count as moved if the pixel change is greater than 1
  // This is to make it easier to simply recenter
  if (selectPixelWidth > 1){
    self.moved = true;
  }
}

// taken from http://ajaxcookbook.org/disable-text-selection/
// prevents ugly drag select side-effects
SelectArea.prototype.disableSelection = function(el) {
    el.onselectstart = function() {
        return false;
    };
    el.unselectable = "on";
    el.style.MozUserSelect = "none";
    el.style.cursor = "default";
}


// Builds the popup menu that appears when selection is complete
SelectArea.prototype.addSelectMenu = function(view) {

  var menu =  $(view+'SelectMenu'); 
  if (menu) {
    this.autoSubmit = false;
  }
  else {
    menu = this.createAndAppend('div',document.body,view+'SelectMenu');
  }

  // required style 
  menu = Element.extend(menu);
  menu.setStyle({ position: 'absolute',
                  display: 'block',
                  zIndex: '101',
                  visibility: 'hidden'
  });

  // optional style -- check if a custom menu has styles set already
  var existingStyle = new String(menu.getAttribute('style'));
  if (existingStyle) {
    if (!existingStyle.match(/width/i))      menu.setStyle({ width: this.menuWidth||'200px'});
    if (!existingStyle.match(/font/i))       menu.setStyle({ font: '12px sans-serif'});
    if (!existingStyle.match(/background/i)) menu.setStyle({ background: 'lightyellow'});
    if (!existingStyle.match(/border/i))     menu.setStyle({ border: '1px solid #003366'});
  }

  this.selectMenu = menu;
  this.formatMenu();
}

// Initial creation of the select box
SelectArea.prototype.addSelectBox = function(view) {
  
  var supportsTouch = ('createTouch' in document);

  if (this.selectBox) return false;
 
  var box = this.createAndAppend('div',this.selectLayer,view+'selectBox');
  box     = Element.extend(box);
  box.setStyle({ position: 'absolute',
                 visibility: 'hidden',
                 top: '0px',
                 height: '100%',
                 left: '0px',
                 zIndex: 100,
                 border: this.border||'none' });
// 
 /* // click on scalebar initializes selection
  this.scalebar.onmousedown      = this.startSelection;

  // drag and mouseup on details panel fires menu
  this.selectLayer.onmousemove   = this.moveRubber;
  this.selectLayer.onmouseup     = this.stopRubber;  

  // allows drag-back*/
 

/* this.scalebar[supportsTouch ? 'ontouchmove' : 'onmousedown'] = this.startSelection;
 
 this.selectLayer[supportsTouch ? 'ontouchmove' : 'onmousemove'] = this.moveRubber;
 
 this.selectLayer[supportsTouch ? 'ontouchend' : 'onmouseup'] = this.stopRubber;
 */
 if ('createTouch' in document) {
  this.scalebar.ontouchstart = this.startSelection;
  event.preventDefault();
  
  // drag and mouseup on details panel fires menu
  this.selectLayer.ontouchmove   = this.moveRubber;
  this.selectLayer.ontouchend     = this.stopRubber;  
  } else {
    this.scalebar.onmousedown = this.startSelection;

  // drag and mouseup on details panel fires menu
  this.selectLayer.onmousemove   = this.moveRubber;
  this.selectLayer.onmouseup   = this.stopRubber;
    
  }


  // 'esc' key aborts
  var abort = function(event){
    var evt = event || window.event;
    if (evt.keyCode == 27) SelectArea.prototype.cancelRubber();
    return true;
  }
  document.onkeydown        = abort;

  this.selectBox = box;
}


/////////////////////////////////////////////////////////////////////
// Create/append  elements
/////////////////////////////////////////////////////////////////////

SelectArea.prototype.createAndAppend = function(elTag,parent,id) {
  var node = this.justCreate(elTag);
  this.justAppend(node,parent);
  if (id) node.setAttribute('id',id);
  return node;
}

SelectArea.prototype.justCreate = function(elTag) {
  var tag = elTag || 'div';
  var node = document.createElement(tag);
  return node;
}

SelectArea.prototype.justAppend = function(child,parent) {
  var parentNode = parent || document.body;
  parentNode.appendChild(child);
}
  

SelectArea.prototype.stopRubber = function(event) {
  balloonIsSuppressed = false;
  if (!selectAreaIsActive) return false;
  var self = currentSelectArea;
  if (!self.moved) {
    self.cancelRubber();
    self.recenter();
    return false;
  }

  selectAreaIsActive = false;
  self.moved = false;

  // autoSubmit option will bypass the menu
  if (self.autoSubmit && !self.overrideAutoSubmit) {
    SelectArea.prototype.cancelRubber();
    //document.searchform.submit();
    self.submit();
  }
  else {
    self.showMenu(event);
  }
}

SelectArea.prototype.showMenu = function(event) {
  var self = currentSelectArea;
  var menu = self.selectMenu;
  menu.innerHTML = self.menuHTML.replace(/SELECTION/g,self.currentSegment);

  var pageWidth  =
  document.viewport.getWidth();
  var menuWidth  = self.elementLocation(menu,'width');
  var menuHeight = self.elementLocation(menu,'height');
  var menuYHalf  = Math.round(menuHeight/2); 
  
  if ('createTouch' in document){
  var left = lefttemp+50;
  var top = 440;
  } else {
  var left = self.eventLocation(event,'x') + 20;
  if ((left+menuWidth) > pageWidth) left -= menuWidth + 10;
  var top  = self.eventLocation(event,'y')+30;
  }
  menu = Element.extend(menu);
  menu.setStyle({ top:  top+'px' }); 
  menu.setStyle({ left: left+'px' });

  // Abort if there is no selection
  if (self.selectBox.getStyle('visibility') == 'hidden') {
    self.cancelRubber;
    return false;
  }

  menu.setStyle({ visibility: 'visible' });

}

SelectArea.prototype.hideMenu = function() {
  var self = currentSelectArea;

  self.selectBox.setStyle({ width: 1,
                            visibility: 'hidden'
  });
  self.selectMenu.setStyle({visibility: 'hidden'});
}

SelectArea.prototype.clearAndSubmit = function(plugin,action) {
  this.hideMenu();
  if (plugin) {
    action = action || 'Go';
    var url = window.location;
    url += '?plugin='+plugin+';plugin_do='+action; 
    document.location = url;
  }
  else {
    this.submit();
  }
}

SelectArea.prototype.clearAndRecenter = function() {
  this.hideMenu();
  var self = currentSelectArea;
  self.loadSegmentInfo();
  var start   = self.detailStart+'';
  start   = start.replace(/\D+/,'') * 1;
  var end   = self.detailEnd+'';
  end   = end.replace(/\D+/,'') * 1;
  var half    = Math.round(Math.abs((end - start)/2));
  var middle  = Math.round((self.selectSequenceStart + self.selectSequenceEnd)/2);
  var newName = self.ref+':'+(middle-half)+'..'+(middle+half);
  self.currentSegment = newName;
  self.submit();
}

// Make best effort to set the opacity of the selectbox
// background color
SelectArea.prototype.setOpacity = function(el,opc,bgColor) {
  var self = currentSelectArea;
  if (!bgColor) {
    bgColor = self.background;
  }
  
  if (!(el && opc)) return false;

  opc = parseFloat(opc);
  el.setStyle({ background: bgColor||'#BABABA',
                opacity: opc 
             });
}


SelectArea.prototype.submit = function() {
  var self = currentSelectArea;
  if (Controller.gbrowse_syn) {
    Controller.update_coordinates(self.currentSegment);
  }
  else if (self.currentSegment) {
    Controller.update_coordinates("set segment " + self.currentSegment);
  }  
}