// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
/**
 * CodeMirror 面板扩展（用于在编辑器上下/下方添加自定义面板）
 * 支持动态管理面板，自动调整编辑器尺寸以适配面板占用空间
 */
(function(mod) {
  // 模块化封装（兼容 CommonJS/AMD/浏览器全局环境）
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  /**
   * 定义“添加面板”扩展方法
   * @param {HTMLElement} node - 面板内容的 DOM 元素
   * @param {Object} [options] - 配置项（position: "bottom" 表示底部，默认顶部；height: 面板高度）
   * @return {Panel} 面板实例（含 clear、changed 等方法）
   */
  CodeMirror.defineExtension("addPanel", function(node, options) {
    // 若未初始化面板系统，先初始化
    if (!this.state.panels) initPanels(this);

    var info = this.state.panels;
    // 根据配置插入面板（底部或顶部）
    if (options && options.position == "bottom")
      info.wrapper.appendChild(node);
    else
      info.wrapper.insertBefore(node, info.wrapper.firstChild);
    var height = (options && options.height) || node.offsetHeight;
    this._setSize(null, info.heightLeft -= height);
    info.panels++;
    return new Panel(this, node, options, height);
  });
  /**
   * 面板类（管理单个面板的生命周期和尺寸更新）
   * @param {CodeMirror} cm - 编辑器实例
   * @param {HTMLElement} node - 面板 DOM 元素
   * @param {Object} options - 配置项
   * @param {number} height - 面板高度
   */
  function Panel(cm, node, options, height) {
    this.cm = cm;
    this.node = node;
    this.options = options;
    this.height = height;
    this.cleared = false;
  }
  /**
   * 移除面板（清除面板并恢复编辑器尺寸）
   */
  Panel.prototype.clear = function() {
    if (this.cleared) return;
    this.cleared = true;
    var info = this.cm.state.panels;
    this.cm._setSize(null, info.heightLeft += this.height);
    info.wrapper.removeChild(this.node);
    if (--info.panels == 0) removePanels(this.cm);
  };
  /**
   * 更新面板高度（动态调整面板高度并适配编辑器尺寸）
   * @param {number|null} height - 新高度（为 null 时自动获取元素高度）
   */
  Panel.prototype.changed = function(height) {
    var newHeight = height == null ? this.node.offsetHeight : height;
    var info = this.cm.state.panels;
    this.cm._setSize(null, info.height += (newHeight - this.height));
    this.height = newHeight;
  };
  /**
   * 初始化面板系统（创建面板容器，重写编辑器尺寸设置方法）
   * @param {CodeMirror} cm - 编辑器实例
   */
  function initPanels(cm) {
    var wrap = cm.getWrapperElement();
    var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
    var height = parseInt(style.height);
    var info = cm.state.panels = {
      setHeight: wrap.style.height,
      heightLeft: height,
      panels: 0,
      wrapper: document.createElement("div")
    };
    wrap.parentNode.insertBefore(info.wrapper, wrap);
    var hasFocus = cm.hasFocus();
    info.wrapper.appendChild(wrap);
    if (hasFocus) cm.focus();
    // 重写编辑器的 setSize 方法（适配面板存在时的尺寸计算）
    cm._setSize = cm.setSize;
    if (height != null) cm.setSize = function(width, newHeight) {
      if (newHeight == null) return this._setSize(width, newHeight);
      info.setHeight = newHeight;
      if (typeof newHeight != "number") {
        var px = /^(\d+\.?\d*)px$/.exec(newHeight);
        if (px) {
          newHeight = Number(px[1]);
        } else {
          info.wrapper.style.height = newHeight;
          newHeight = info.wrapper.offsetHeight;
          info.wrapper.style.height = "";
        }
      }
      cm._setSize(width, info.heightLeft += (newHeight - height));
      height = newHeight;
    };
  }
  /**
   * 销毁面板系统（移除面板容器，恢复编辑器原始状态）
   * @param {CodeMirror} cm - 编辑器实例
   */
  function removePanels(cm) {
    var info = cm.state.panels;
    cm.state.panels = null;

    var wrap = cm.getWrapperElement();
    info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
    wrap.style.height = info.setHeight;
    cm.setSize = cm._setSize;
    cm.setSize();
  }
});
