// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var CodeView = function(divID, PR, sourceText, sourcePosition, broker) {
  "use strict";
  var view = this;

  view.divElement = document.getElementById(divID);
  view.broker = broker;
  view.codeSelection = null;
  view.allSpans = [];

  var selectionHandler = {
    clear: function() {
      broker.clear(selectionHandler);
    },
    select: function(items, selected) {
      var handler = this;
      var divElement = view.divElement;
      var broker = view.broker;
      for (let span of items) {
        if (selected) {
          span.classList.add("selected");
        } else {
          span.classList.remove("selected");
        }
      }
      var ranges = [];
      for (var span of items) {
        ranges.push([span.start, span.end, null]);
      }
      broker.select(selectionHandler, ranges, selected);
    },
    selectionDifference: function(span1, inclusive1, span2, inclusive2) {
      var pos1 = span1.start;
      var pos2 = span2.start;
      var result = [];
      var lineListDiv = view.divElement.firstChild.firstChild.childNodes;
      for (var i=0; i < lineListDiv.length; i++) {
        var currentLineElement = lineListDiv[i];
        var spans = currentLineElement.childNodes;
        for (var j=0; j < spans.length; ++j) {
          var currentSpan = spans[j];
          if (currentSpan.start > pos1 || (inclusive1 && currentSpan.start == pos1)) {
            if (currentSpan.start < pos2 || (inclusive2 && currentSpan.start == pos2)) {
              result.push(currentSpan);
            }
          }
        }
      }
      return result;
    },
    brokeredSelect: function(ranges, selected) {
      var firstSelect = view.codeSelection.isEmpty();
      for (var range of ranges) {
        var start = range[0];
        var end = range[1];
        var lower = 0;
        var upper = view.allSpans.length;
        if (upper > 0) {
          while ((upper - lower) > 1) {
            var middle = Math.floor((upper + lower) / 2);
            var lineStart = view.allSpans[middle].start;
            if (lineStart < start) {
              lower = middle;
            } else if (lineStart > start) {
              upper = middle;
            } else {
              lower = middle;
              break;
            }
          }
          var currentSpan = view.allSpans[lower];
          var currentLineElement = currentSpan.parentNode;
          if ((currentSpan.start <= start && start < currentSpan.end) ||
              (currentSpan.start <= end && end < currentSpan.end)) {
            if (firstSelect) {
              makeContainerPosVisible(view.divElement, currentLineElement.offsetTop);
              firstSelect = false;
            }
            view.codeSelection.select(currentSpan, selected);
          }
        }
      }
    },
    brokeredClear: function() {
      view.codeSelection.clear();
    },
  };

  view.codeSelection = new Selection(selectionHandler);
  broker.addSelectionHandler(selectionHandler);

  var mouseDown = false;

  this.handleSpanMouseDown = function(e) {
    e.stopPropagation();
    if (!e.shiftKey) {
      view.codeSelection.clear();
    }
    view.codeSelection.select(this, true);
    mouseDown = true;
  }

  this.handleSpanMouseMove = function(e) {
    if (mouseDown) {
      view.codeSelection.extendTo(this);
    }
  }

  this.handleCodeMouseDown = function(e) {
    view.codeSelection.clear();
  }

  document.addEventListener('mouseup', function(e){
    mouseDown = false;
  }, false);

  this.initializeCode(sourceText, sourcePosition);
}

CodeView.prototype.initializeCode = function(sourceText, sourcePosition) {
  var view = this;
  if (sourceText == "") {
    var newHtml = "<pre class=\"prettyprint\"</pre>";
    view.divElement.innerHTML = newHtml;
  } else {
    var newHtml = "<pre class=\"prettyprint linenums\">"
      + sourceText + "</pre>";
    view.divElement.innerHTML = newHtml;
    try {
      // Wrap in try to work when offline.
      PR.prettyPrint();
    } catch (e) {
    }

    view.divElement.onmousedown = this.handleCodeMouseDown;

    var base = sourcePosition;
    var current = 0;
    var lineListDiv = view.divElement.firstChild.firstChild.childNodes;
    for (i=0; i < lineListDiv.length; i++) {
      var currentLineElement = lineListDiv[i];
      currentLineElement.id = "li" + i;
      var pos = base + current;
      currentLineElement.pos = pos;
      var spans = currentLineElement.childNodes;
      for (j=0; j < spans.length; ++j) {
        var currentSpan = spans[j];
        if (currentSpan.nodeType == 1) {
          currentSpan.start = pos;
          currentSpan.end = pos + currentSpan.textContent.length;
          currentSpan.onmousedown = this.handleSpanMouseDown;
          currentSpan.onmousemove = this.handleSpanMouseMove;
          view.allSpans.push(currentSpan);
        }
        current += currentSpan.textContent.length;
        pos = base + current;
      }
      while ((current < sourceText.length) && (
        sourceText[current] == '\n' ||
          sourceText[current] == '\r')) {
        ++current;
      }
    }
  }

  this.resizeToParent();
}

CodeView.prototype.resizeToParent = function() {
  var view = this;
  var documentElement = document.documentElement;
  var y = view.divElement.parentNode.clientHeight || documentElement.clientHeight;
  view.divElement.style.height = y + "px";
}