/**
 * 选集
 * @file
 * @module UE.dom
 * @class Selection
 * @since 1.2.6.1
 */

/**
 * 选区集合
 * @unfile
 * @module UE.dom
 * @class Selection
 */
;(function() {
  function getBoundaryInformation(range, start) {
    var getIndex = domUtils.getNodeIndex;
    range = range.duplicate();
    range.collapse(start);
    var parent = range.parentElement();
    //如果节点里没有子节点，直接退出
    if (!parent.hasChildNodes()) {
      return { container: parent, offset: 0 };
    }
    var siblings = parent.children,
      child,
      testRange = range.duplicate(),
      startIndex = 0,
      endIndex = siblings.length - 1,
      index = -1,
      distance;
    while (startIndex <= endIndex) {
      index = Math.floor((startIndex + endIndex) / 2);
      child = siblings[index];
      testRange.moveToElementText(child);
      var position = testRange.compareEndPoints("StartToStart", range);
      if (position > 0) {
        endIndex = index - 1;
      } else if (position < 0) {
        startIndex = index + 1;
      } else {
        //trace:1043
        return { container: parent, offset: getIndex(child) };
      }
    }
    if (index == -1) {
      testRange.moveToElementText(parent);
      testRange.setEndPoint("StartToStart", range);
      distance = testRange.text.replace(/(\r\n|\r)/g, "\n").length;
      siblings = parent.childNodes;
      if (!distance) {
        child = siblings[siblings.length - 1];
        return { container: child, offset: child.nodeValue.length };
      }

      var i = siblings.length;
      while (distance > 0) {
        distance -= siblings[--i].nodeValue.length;
      }
      return { container: siblings[i], offset: -distance };
    }
    testRange.collapse(position > 0);
    testRange.setEndPoint(position > 0 ? "StartToStart" : "EndToStart", range);
    distance = testRange.text.replace(/(\r\n|\r)/g, "\n").length;
    if (!distance) {
      return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName]
        ? {
            container: parent,
            offset: getIndex(child) + (position > 0 ? 0 : 1)
          }
        : {
            container: child,
            offset: position > 0 ? 0 : child.childNodes.length
          };
    }
    while (distance > 0) {
      try {
        var pre = child;
        child = child[position > 0 ? "previousSibling" : "nextSibling"];
        distance -= child.nodeValue.length;
      } catch (e) {
        return { container: parent, offset: getIndex(pre) };
      }
    }
    return {
      container: child,
      offset: position > 0 ? -distance : child.nodeValue.length + distance
    };
  }

  /**
     * 将ieRange转换为Range对象
     * @param {Range}   ieRange    ieRange对象
     * @param {Range}   range      Range对象
     * @return  {Range}  range       返回转换后的Range对象
     */
  function transformIERangeToRange(ieRange, range) {
    if (ieRange.item) {
      range.selectNode(ieRange.item(0));
    } else {
      var bi = getBoundaryInformation(ieRange, true);
      range.setStart(bi.container, bi.offset);
      if (ieRange.compareEndPoints("StartToEnd", ieRange) != 0) {
        bi = getBoundaryInformation(ieRange, false);
        range.setEnd(bi.container, bi.offset);
      }
    }
    return range;
  }

  /**
     * 获得ieRange
     * @param {Selection} sel    Selection对象
     * @return {ieRange}    得到ieRange
     */
  function _getIERange(sel) {
    var ieRange;
    //ie下有可能报错
    try {
      ieRange = sel.getNative().createRange();
    } catch (e) {
      return null;
    }
    var el = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
    if ((el.ownerDocument || el) === sel.document) {
      return ieRange;
    }
    return null;
  }

  var Selection = (dom.Selection = function(doc) {
    var me = this,
      iframe;
    me.document = doc;
    if (browser.ie9below) {
      iframe = domUtils.getWindow(doc).frameElement;
      domUtils.on(iframe, "beforedeactivate", function() {
        me._bakIERange = me.getIERange();
      });
      domUtils.on(iframe, "activate", function() {
        try {
          if (!_getIERange(me) && me._bakIERange) {
            me._bakIERange.select();
          }
        } catch (ex) {}
        me._bakIERange = null;
      });
    }
    iframe = doc = null;
  });

  Selection.prototype = {
    rangeInBody: function(rng, txtRange) {
      var node = browser.ie9below || txtRange
        ? rng.item ? rng.item() : rng.parentElement()
        : rng.startContainer;

      return node === this.document.body || domUtils.inDoc(node, this.document);
    },

    /**
         * 获取原生seleciton对象
         * @method getNative
         * @return { Object } 获得selection对象
         * @example
         * ```javascript
         * editor.selection.getNative();
         * ```
         */
    getNative: function() {
      var doc = this.document;
      try {
        return !doc
          ? null
          : browser.ie9below
            ? doc.selection
            : domUtils.getWindow(doc).getSelection();
      } catch (e) {
        return null;
      }
    },

    /**
         * 获得ieRange
         * @method getIERange
         * @return { Object } 返回ie原生的Range
         * @example
         * ```javascript
         * editor.selection.getIERange();
         * ```
         */
    getIERange: function() {
      var ieRange = _getIERange(this);
      if (!ieRange) {
        if (this._bakIERange) {
          return this._bakIERange;
        }
      }
      return ieRange;
    },

    /**
         * 缓存当前选区的range和选区的开始节点
         * @method cache
         */
    cache: function() {
      this.clear();
      this._cachedRange = this.getRange();
      this._cachedStartElement = this.getStart();
      this._cachedStartElementPath = this.getStartElementPath();
    },

    /**
         * 获取选区开始位置的父节点到body
         * @method getStartElementPath
         * @return { Array } 返回父节点集合
         * @example
         * ```javascript
         * editor.selection.getStartElementPath();
         * ```
         */
    getStartElementPath: function() {
      if (this._cachedStartElementPath) {
        return this._cachedStartElementPath;
      }
      var start = this.getStart();
      if (start) {
        return domUtils.findParents(start, true, null, true);
      }
      return [];
    },

    /**
         * 清空缓存
         * @method clear
         */
    clear: function() {
      this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
    },

    /**
         * 编辑器是否得到了选区
         * @method isFocus
         */
    isFocus: function() {
      try {
        if (browser.ie9below) {
          var nativeRange = _getIERange(this);
          return !!(nativeRange && this.rangeInBody(nativeRange));
        } else {
          return !!this.getNative().rangeCount;
        }
      } catch (e) {
        return false;
      }
    },

    /**
         * 获取选区对应的Range
         * @method getRange
         * @return { Object } 得到Range对象
         * @example
         * ```javascript
         * editor.selection.getRange();
         * ```
         */
    getRange: function() {
      var me = this;
      function optimze(range) {
        var child = me.document.body.firstChild,
          collapsed = range.collapsed;
        while (child && child.firstChild) {
          range.setStart(child, 0);
          child = child.firstChild;
        }
        if (!range.startContainer) {
          range.setStart(me.document.body, 0);
        }
        if (collapsed) {
          range.collapse(true);
        }
      }

      if (me._cachedRange != null) {
        return this._cachedRange;
      }
      var range = new baidu.editor.dom.Range(me.document);

      if (browser.ie9below) {
        var nativeRange = me.getIERange();
        if (nativeRange) {
          //备份的_bakIERange可能已经实效了，dom树发生了变化比如从源码模式切回来，所以try一下，实效就放到body开始位置
          try {
            transformIERangeToRange(nativeRange, range);
          } catch (e) {
            optimze(range);
          }
        } else {
          optimze(range);
        }
      } else {
        var sel = me.getNative();
        if (sel && sel.rangeCount) {
          var firstRange = sel.getRangeAt(0);
          var lastRange = sel.getRangeAt(sel.rangeCount - 1);
          range
            .setStart(firstRange.startContainer, firstRange.startOffset)
            .setEnd(lastRange.endContainer, lastRange.endOffset);
          if (
            range.collapsed &&
            domUtils.isBody(range.startContainer) &&
            !range.startOffset
          ) {
            optimze(range);
          }
        } else {
          //trace:1734 有可能已经不在dom树上了，标识的节点
          if (
            this._bakRange &&
            domUtils.inDoc(this._bakRange.startContainer, this.document)
          ) {
            return this._bakRange;
          }
          optimze(range);
        }
      }
      return (this._bakRange = range);
    },

    /**
         * 获取开始元素，用于状态反射
         * @method getStart
         * @return { Element } 获得开始元素
         * @example
         * ```javascript
         * editor.selection.getStart();
         * ```
         */
    getStart: function() {
      if (this._cachedStartElement) {
        return this._cachedStartElement;
      }
      var range = browser.ie9below ? this.getIERange() : this.getRange(),
        tmpRange,
        start,
        tmp,
        parent;
      if (browser.ie9below) {
        if (!range) {
          //todo 给第一个值可能会有问题
          return this.document.body.firstChild;
        }
        //control元素
        if (range.item) {
          return range.item(0);
        }
        tmpRange = range.duplicate();
        //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
        tmpRange.text.length > 0 && tmpRange.moveStart("character", 1);
        tmpRange.collapse(1);
        start = tmpRange.parentElement();
        parent = tmp = range.parentElement();
        while ((tmp = tmp.parentNode)) {
          if (tmp == start) {
            start = parent;
            break;
          }
        }
      } else {
        range.shrinkBoundary();
        start = range.startContainer;
        if (start.nodeType == 1 && start.hasChildNodes()) {
          start =
            start.childNodes[
              Math.min(start.childNodes.length - 1, range.startOffset)
            ];
        }
        if (start.nodeType == 3) {
          return start.parentNode;
        }
      }
      return start;
    },

    /**
         * 得到选区中的文本
         * @method getText
         * @return { String } 选区中包含的文本
         * @example
         * ```javascript
         * editor.selection.getText();
         * ```
         */
    getText: function() {
      var nativeSel, nativeRange;
      if (this.isFocus() && (nativeSel = this.getNative())) {
        nativeRange = browser.ie9below
          ? nativeSel.createRange()
          : nativeSel.getRangeAt(0);
        return browser.ie9below ? nativeRange.text : nativeRange.toString();
      }
      return "";
    },

    /**
         * 清除选区
         * @method clearRange
         * @example
         * ```javascript
         * editor.selection.clearRange();
         * ```
         */
    clearRange: function() {
      this.getNative()[browser.ie9below ? "empty" : "removeAllRanges"]();
    }
  };
})();
