const _parent = Symbol("parent");

/**
 * TaskUI组件基类
 */
export class Component {
  constructor(__tfp, typeName, dataModel, parent) {
    if(!__tfp) {
      throw new Error("请提供tfp对象！");
      return;
    }
    if(!typeName) {
      console.log(dataModel);
      throw new Error("请提供组件类型！");
      return;
    }
    
    this._tfp = __tfp;
    this.level = 0;  //组件相对页面组件的层次
    this.index = 0;  //组件在父组件中的索引，主要用来排序
                     //目前如果某个兄弟组件删除了，不会重置所有剩余的兄弟组件的索引

    //如果没有提供数据模型，则进行模型初始化
    if(!dataModel) {
      let metadata = this._tfp.type(typeName);
      if(!metadata) {
        throw new Error("请提供正确的组件类型！");
        return;
      }
      this.dataModel = {
        type: typeName
      };
      this.dataModel.id = this.dataModel.type.substr(0, 1).toLowerCase()
        +this.dataModel.type.substr(1)+this._tfp.getNewCptIndex(this.dataModel.type);
      let attrs = this.attrTypes;
      //设置组件默认属性
      for(let i=0; i<attrs.length; i++) {
        let attr = attrs[i];
        if(attr.default || attr.default==0 || attr.default==false) {
          if(attr.type.toLowerCase()=="string") {
            this.dataModel[attr.name] = attr.default.replace("{id}", this.id);
          } else {
            this.dataModel[attr.name] = attr.default;
          }
        }
      }
      //设置可视组件数据模型的默认样式
      if(metadata.defaultStyles) {
        this.dataModel.styles = {};
        for (let style in metadata.defaultStyles) {
          this.dataModel.styles[style] = metadata.defaultStyles[style];
        }
      }
    } else {  //否则用传入的数据模型
      this.dataModel = dataModel;
      if(!this.dataModel.type) this.dataModel.type = typeName;
    }

    //设置组件数据模型的默认ID
    if(!this.dataModel.id) {
      this.dataModel.id = this.dataModel.type.substr(0, 1).toLowerCase()
        +this.dataModel.type.substr(1)+this._tfp.getNewCptIndex(this.dataModel.type);
    }
    this._tfp.components[this.dataModel.id] = this;
    if(this._tfp.isRuntime) window[this.dataModel.id] = this;
    
    //设置组件的父组件
    if(parent) {
      this[_parent] = parent;
      this.level = parent.level+1;
      if(!this._tfp.isLoadingPage) {
        if(!parent.dataModel.components) parent.dataModel.components = [];
        let cptExists = false;
        for(var i=0;i<parent.dataModel.components.length;i++) {
          let cdmTmp = parent.dataModel.components[i];
          if(cdmTmp.id == this.dataModel.id) {
            cptExists = true;
            continue;
          }
          if(cdmTmp.index>=this.index) this.index = cdmTmp.index + 1;
        }
        if(!cptExists) parent.dataModel.components.push(this.dataModel);
      }
    } else {
      //TODO 如果创建组件时没有提供父组件，则表示是临时组件，后续可以设置父组件
    }
  }

  //组件ID，每个组件的ID是唯一的，不能重复
  get id() { return this.dataModel.id }
  set id(value) { 
    if(!value) return;
    let oldId = this.dataModel.id;
    if(oldId==value) return;
    for(let cptId in this._tfp.components) {
      if(cptId==value) {
        throw new Error("ID为的"+value+"组件已存在！");
        return;
      }
    }
    this.dataModel.id = value;
    delete this._tfp.components[oldId];
    this._tfp.components[this.dataModel.id] = this;
    if(this._tfp.isRuntime) {
      delete window[oldId];
      window[this.dataModel.id] = this;
    }
    if($("#"+oldId).length>0) {
      $("#"+oldId).attr("id", this.dataModel.id);
    }
  }

  //组件类型，组件一旦创建，不允许再修改类型
  get type() { return this.dataModel.type }
  set type(value) {}

  //组件类型元数据，组件一旦创建，不允许再修改
  get metadata() { return this._tfp.type(this.dataModel.type) }
  set metadata(value) {}

  //父组件
  get parent() { return this[_parent] }
  set parent(value) {
    //如果没有设置父组件
    if(!this[_parent]) {
      this[_parent] = value;
      if(!this[_parent].dataModel.components) this[_parent].dataModel.components = [];
      this[_parent].dataModel.components.push(this.dataModel);
    } else {
      if(this[_parent].id!=value.id) {  //如果已经设置了父组件，但新设置的父组件和原来的父组件不一样
        this[_parent].dataModel.components.remove(this.dataModel);
        this[_parent] = value;
        if(!this[_parent].dataModel.components) this[_parent].dataModel.components = [];
        this[_parent].dataModel.components.push(this.dataModel);
      } else {
        //如果父组件没有变化，则不需要执行后续操作
        return;
      }
    }
    //设置当前组件的层级和索引
    this.level = this[_parent].level+1;
    for(var i=0;i<this[_parent].dataModel.components.length;i++) {
      let cdmTmp = this[_parent].dataModel.components[i];
      if(cdmTmp.index>=this.index) this.index = cdmTmp.index + 1;
    }
  }

  get attrTypes() {
    let attrs = [];
    let metadata = this._tfp.type(this.type);
    if(metadata.attrs) {
      for(let i=0; i<metadata.attrs.length; i++) {
        let attr = metadata.attrs[i];
        if(attr.type=="group" || attr.items) {
          for(let j=0;j<attr.items.length;j++) {
            attrs.push(attr.items[j]);
          }
        } else {
          attrs.push(attr);
        }
      }
    }
    return attrs;
  }
  set attrTypes(value) {}

  attr(attrName, attrValue) {
    if(arguments.length==0) return;
    //获取样式值
    if(arguments.length==1) {
      return this[attrName];
    }
    this[attrName] = attrValue;
  }

  /**
   * 获得属性定义信息
   * @param  {[type]} attrName [description]
   * @return {[type]}          [description]
   */
  getAttrTypeInfo(attrName) {
    if(!this.metadata) {
      throw new Error("没有找到组件类型定义信息，请先引用类型信息！");
      return;
    }
    if(!this.metadata.attrs) {
      //throw new Error("没有找到组件的属性定义信息！");
      return null;
    }
    for(var i=0;i<this.metadata.attrs.length;i++) {
      let attrInfo = this.metadata.attrs[i];
      if(attrInfo.type=="group" || attrInfo.items) {
        for(var j=0;j<attrInfo.items.length;j++) {
          if(attrInfo.items[j].name==attrName) {
            return attrInfo.items[j];
          }
        }
      } else {
        if(this.metadata.attrs[i].name==attrName) {
          return this.metadata.attrs[i];
        }
      }
    }
    return null;
  }

  /**
   * 检查属性选项是否是组件类型定义中设置的选项
   * @param  {[type]} attrName [description]
   * @param  {[type]} attrVal  [description]
   * @return {[type]}          [description]
   */
  checkAttrOption(attrName, attrVal) {
    let attr = this.getAttrTypeInfo(attrName);
    if(!attr) return false;
    let options = attr.options;
    if(!options) return false;
    for(var i=0;i<options.length;i++) {
      if(options[i].value==attrVal) return true;
    }
    return false;
  }

  /**
   * 执行事件处理函数
   * @param  {[type]} eventName [description]
   * @return {[type]}           [description]
   */
  exeEventHandler(eventName) {
    //设计时不执行任何事件处理函数
    if(this._tfp.isDesigning) return;
    let ret;
    if(this[eventName] && typeof this[eventName] == "function") {
      ret = this[eventName](arguments);
    } else if(this.dataModel[eventName]) {
      ret = eval(this.dataModel[eventName]);
    }
    if(ret) return ret;
  }
}

export class InvisibleComponent extends Component{

  constructor(__tfp, typeName, dataModel, parent) {
    super(__tfp, typeName, dataModel, parent);
  }

  get isInvisible() { return true }
  set isInvisible(value) {}

  render() {
    //如果是不可视组件，只有在设计时需要渲染
    if(this._tfp.isDesigning) {
      window.parent.uiDesigner.addInvisibleComponent(this);
      this._tfp.initCptDesignSetting(this);
    }
  }
}

export class VisibleComponent extends Component{

  constructor(__tfp, typeName, dataModel, parent) {
    super(__tfp, typeName, dataModel, parent);

    this._jqObj = null;
    this.el = null;
    this.isRendered = false;
  }

  get isInvisible() { return false }
  set isInvisible(value) {}

  get isContainer() { return false }
  set isContainer(value) {}

  get styles() { return this.dataModel.styles; }
  set styles(value) {}

  get style() { return this.dataModel.style; }
  set style(value) {
    if(!value) value = "";
    this.dataModel.style = value;
    if(this._jqObj) {
      let style = value.trim();
      if(style!="" && !style.endsWith(";")) style += ";";
      if(this.dataModel.styles) {
        for(let styleTmp in this.dataModel.styles) {
          style += styleTmp +": "+this.dataModel.styles[styleTmp]+";";
        }
      }
      //要保留设计时的外边框
      let outline = this._jqObj.css("outline");
      this._jqObj.attr("style", style);
      this._jqObj.css("outline", outline);
    }
  }

  get class() { return this.dataModel.class; }
  set class(value) {
    if(!value) value = ""; 
    this.dataModel.class = value;
    if(this._jqObj) {
      this._jqObj.attr("class", value);
    }
  }

  get indent() {
    var _indent = "";
    for(var i=0;i<this.level;i++) {
      _indent += "\t";
    }
    return _indent;
  }
  set indent(value) {}

  css(styleName, styleValue) {
    if(arguments.length==0) return;
    //获取样式值
    if(arguments.length==1) {
      if(!this.dataModel.styles) return null;
      return this.dataModel.styles[styleName];
    }
    if(this._jqObj) this._jqObj.css(styleName, styleValue);
    if(!this.dataModel.styles) this.dataModel.styles = {};
    if(styleValue=="" || styleValue==null) {
      delete this.dataModel.styles[styleName];
    } else {
      this.dataModel.styles[styleName] = styleValue;
    }
  }

  val(value) {
    if(arguments.length==0) return this.value;
    this.value = value;
  }

  show() {
    if(this._jqObj) this._jqObj.show();
  }

  hide() {
    if(this._jqObj) this._jqObj.hide();
  }

  toggle() {
    if(this._jqObj) this._jqObj.toggle();
  }

  focus() {
    if(this._jqObj) this._jqObj.focus();
  }

  getHtmlIndent() {
    if(this.indent) return this.indent;
    let indent = "";
    for(let i=0;i<this.level;i++) {
      indent += "\t";
    }
    return indent;
  }

  render() {
    if((!this.parent || !this.parent.containerEl) && this.type!="Page") return;
    let Render = this._tfp.renders[this.type];
    let render = new Render(this._tfp, this.dataModel, this.level);
    if(this.type=="Page") {
      if(this.dataModel.pageElId && this._tfp.isRuntime) {
        this._jqObj = $("#"+this.dataModel.pageElId);
      } else {
        this._jqObj = $("body");
      }
      this._jqObj.append(render.getHtml());
      this._tfp.curPage = this;
    } else {
      $(this.parent.containerEl).append(render.getHtml());
      this._jqObj = $("#"+this.id);
    }
    if(this._jqObj.length>0) this.el = this._jqObj.get(0);
    if(!this.isRendered && this._tfp.isDesigning) {
      this._tfp.initCptDesignSetting(this);
    }
    if (typeof window != "undefined" && !this._tfp.isDesigning) {
      window[this.id] = this;
    }
    if(this.dataModel.components) {
      for(var i=0;i<this.dataModel.components.length;i++) {
        let cdmChild = this.dataModel.components[i];
        let cptChild = this._tfp.render(cdmChild, this);
      }
    }
    this.isRendered = true;
  }

  clear() {
    if(this.dataModel.components) {
      for(var i=0;i<this.dataModel.components.length;i++) {
        let childCdm = this.dataModel.components[i];
        let child = this._tfp.get(childCdm.id);
        child.clear();
        if(child._jqObj) child._jqObj.remove();
        delete this._tfp.components[child.id];
      }
      //this.dataModel.components = [];
    }
  }
}

//容器组件
export class ContainerComponent extends VisibleComponent{

  constructor(__tfp, typeName, dataModel, parent) {
    super(__tfp, typeName, dataModel, parent);

    if(!this.dataModel.components) this.dataModel.components = [];
  }

  get isContainer() { return true }
  set isContainer(value) {}

  get containerEl() { return this.el }
  set containerEl(value) {
    this.el = value;
  }

  get components() { return this.dataModel.components }
  set components(value) {}

  /**
   * 添加子组件
   * @param {[type]} cptChild [description]
   */
  addChild(cptChild) {
    cptChild.parent = this;
  }

  /**
   * 移除子组件
   * @param  {[type]} cptId [description]
   * @return {[type]}       [description]
   */
  removeChild(cptId) {
    for(var i=0;i<this.dataModel.components.length;i++) {
      let childCdm = this.dataModel.components[i];
      if(childCdm.id==cptId) {
        let child = this._tfp.components[childCdm.id];
        child.clear();
        if(child._jqObj) child._jqObj.remove();
        if(this._tfp.isRuntime) {
          delete this._tfp.components[child.id];
          delete window[child.id];
        }
        child = null;
        childCdm = null;
        return;
      }
    }
  }

  /**
   * 清空子组件
   * @return {[type]} [description]
   */
  clearChildren() {
    for(var i=0;i<this.dataModel.components.length;i++) {
      let childCdm = this.dataModel.components[i];
      let child = this._tfp.components[childCdm.id];
      child.clear();
      if(child._jqObj) child._jqObj.remove();
      delete this._tfp.components[child.id];
      if(this._tfp.isRuntime) delete window[child.id];
      child = null;
      childCdm = null;
    }
    this.dataModel.components = [];
  }
}

//表单输入项组件
export class FormInput extends VisibleComponent{

  constructor(__tfp, typeName, dataModel, parent) {
    super(__tfp, typeName, dataModel, parent);
  }

  get isFormInput() { return true }

  //数据绑定格式
  get dataBindingFormat() { return this.dataModel.dataBindingFormat }
  set dataBindingFormat(value) {this.dataModel.dataBindingFormat = value}

  //是否必填
  get required() { return this.dataModel.required }
  set required(value) {this.dataModel.required = value ? true : false}

  //是否只读
  get readonly() { return this.dataModel.readonly }
  set readonly(value) {
    this.dataModel.readonly = value ? true : false;
    if(!this.dataModel.readonly) delete this.dataModel.readonly;
    if(this._jqObj && this._jqObj.length>0 && !this._tfp.isDesigning) {
      let el = this._jqObj.get(0);
      if(el.tagName=="INPUT" || el.tagName=="SELECT" || el.tagName=="TEXTAREA") {
        el.readonly = this.dataModel.readonly;
      } else if(el.tagName=="DIV") {
        this._jqObj.find("input").each(function(){
          $(this).get(0).readonly = this.dataModel.readonly;
        });
      }
    }
  }

  //是否禁用
  get disabled() { return this.dataModel.disabled }
  set disabled(value) { 
    this.dataModel.disabled = value ? true : false;
    if(!this.dataModel.disabled) delete this.dataModel.disabled;
    if(this._jqObj && this._jqObj.length>0 && !this._tfp.isDesigning) {
      let el = this._jqObj.get(0);
      if(el.tagName=="INPUT" || el.tagName=="SELECT" || el.tagName=="TEXTAREA") {
        el.disabled = this.dataModel.disabled;
      } else if(el.tagName=="DIV") {
        this._jqObj.find("input").each(function(){
          $(this).get(0).disabled = this.dataModel.disabled;
        });
      }
    }
  }

  //自动计算表达式
  get formula() { return this.dataModel.formula }
  set formula(value) {
    if(isNull(value)) {
      delete this.dataModel["formula"];
      return;
    }
    this.dataModel.formula = value;
  }

  /**
   * 当值发生变化时
   * @return {[type]} [description]
   */
  valueOnChange() {
    this._tfp.iptValueOnChange(this);
  }

  /**
   * 执行计算表达式
   * @return {[type]} [description]
   */
  exeFormula() {
    if(!this.dataModel.formula) return;
    let val = this.dataModel.formula;
    let ipts = this.dataModel.formula.match(/\$\{[\w]+\}/g);
    for(let i=0;i<ipts.length;i++) {
      let iptId = ipts[i].substr(2, ipts[i].length-3);
      let ipt = this._tfp.components[iptId];
      if(!ipt) continue;
      let iptVal = ipt.value;
      if(ipt.type=="Text" && isNull(iptVal)) {
        //如果是单行输入框，且数据类型不是文本，则值为空时，将值设置为0，以便计算
        if(ipt.dataType=="int" || ipt.dataType=="float" || ipt.dataType=="money") {
          iptVal = "0";
        }
      } else if(ipt.type=="DataSet") {
        iptVal = ipt.id;
      }
      val = val.replaceAll("\$\{"+iptId+"\}", iptVal);
    }
    try {
      this.value = eval(val);
    } catch(err) {
      console.error("执行计算表达式出错："+err.message);
    }
  }

  /**
   * 设置输入项的选项
   * @param {[type]} value [description]
   */
  setOptions(value) {
    if(!value) return;
    let options = [];
    if(typeof(value)=="string") {
      let arr = value.split(",");
      for(var i=0;i<arr.length;i++) {
        let str = arr[i];
        if(str.trim()!="") {
          options.push({
            value: str,
            text: str
          });
        }
      }
    } else if(Array.isArray(value)) {
      for(var i=0;i<value.length;i++) {
        let val = value[i];
        if(Object.prototype.toString.call(val) === '[object Object]') {
          options.push(val);
        } else {
          options.push({
            value: val,
            text: val
          });
        }
      }
    }
    this.dataModel.options = options;
  }
}