import {
  textTraits,
  textDefaultTraits,
  echartsBasicTraits,
  echartsBasicDefaultTraits
} from './feature/traits/common';
import globalEventListeners from './feature/managers/globalEvents';
import {i18n} from "./configuration/editor";
import _ from "underscore";

const load = (ComponentDefine, category) => {
  const {
    type = 'default',
    viewStyle = () => "",
    viewTemplate,
    name: ComponentName,
    blockOptions,
    onModelScript = () => ({}),
    componentOptions = () => ({}),
    spOptions = () => (null),
    onViewInit = (elment, model) => {/**/
    },
    onViewRender = ({editor, el, model}) => {
    },
    onViewRemoved = () => {
    },
    isComponent,
    onViewExtend = () => ({}),
    viewMethods = () => ({}),
    ...methods
  } = ComponentDefine;
  if (!ComponentName) {
    console.error("未定义组件name", ComponentDefine)
    return;
  }
  if (!viewTemplate) {
    console.error("未定义组件 viewTemplate", ComponentDefine)
    return;
  }
  // if (!blockOptions) {
  //   console.error("未定义组件 blockOptions", ComponentDefine)
  //   return;
  // }

  function setComponentEnable(componentModel, enable) {
    componentModel.set('selectable', enable)
    componentModel.set('layerable', enable)
    // m.set('highlightable', enable)
    componentModel.set("draggable", enable);
    componentModel.set('editable', enable)
    componentModel.set('hoverable', enable)
  }

  function disableEditSubComponents(component, enableCallback) {
    component.view.model.set('childEditDisabled', true)
    component.view.model.components().models.forEach((m) => {
      setComponentEnable(m, false)
      // 内部的，只要不是在model里面配置了disableChildEdit属性为true的就会禁用下层元素
      if (!m.view.model.get('disableChildEdit')) {
        disableEditSubComponents(m, enableCallback)
      }
    })
  }

  function enableEditSubComponents(component, allSub) {
    component.view.model.set('childEditDisabled', true)
    component.view.model.components().models.forEach((m) => {
      setComponentEnable(m, true)
      if (allSub) {
        enableEditSubComponents(m, allSub)
      }

    })
  }

  function handleChildrenStatic(selector) {
    selector.components().models.forEach(function (m) {
      m.set("draggable", false);
      if (m.view.model && m.view.model.components().length) {
        handleChildrenStatic(m.view.model)
      }
    })
  }

  const componentGenerator = (editor, opts = {}) => {
    editor.on('selector:add', (selector) => {
      if (selector.getFullName() === ComponentName) {
        selector.set({private: 1});
      }
    });

    editor.on('component:create', (model) => {
      if (model.get('isFormItem')) {
        if (model.parent() && (model.parent().get('type') == 'form-panel-group' || model.parent().get('type') == 'form-panel')) {
          model.attributes.attributes = {};
        }
      }
    });

    editor.on('component:mount', (model) => {
      var _type = model.get('type');

      if (model.get('disableChildEdit')) {
        model.addClass('no-events');
        disableEditSubComponents(model, (model) => {
          enableEditSubComponents(model, true)
          editor.select(model)
        })
      }
      if (model.get('childrenStatic')) {
        handleChildrenStatic(model)
      }
      if (_type == 'modal-comp') {
        model.set("draggable", false);
        model.components().models.forEach(function (m) {
          m.set("draggable", false);
          m.set("selectable", false);
          m.set("droppable", false);
          m.set("hoverable", false);
          if (m.getView().$el.hasClass('modal-comp-wrap')) {
            m.components().models.forEach(model => {
              if (model.getView().$el.hasClass('modal-comp-content')) {
                model.components().models.forEach(child => {
                  child.set("draggable", false);
                  if (!child.getView().$el.hasClass('modal-comp-body')) {
                    child.components().models.forEach(el => {
                      el.set("draggable", false);
                    });
                  }
                })
              }
            })
          }
        });
      }

      if (_type == 'form-select' || _type == 'form-input') {
        const item = model.components().models[1];
        if (item) {
          item.set("selectable", false);
          item.set("draggable", false);
          item.set("hoverable", false);
          disableEditSubComponents(item, (model) => {
            enableEditSubComponents(item, true)
            editor.select(model)
          })
        }
      }
      if (model.get('isFormItem')) {
        if (model.parent() && (model.parent().get('type') == 'form-panel-group' || model.parent().get('type') == 'form-panel')) {
          model.attributes.attributes = {};
        }
      }

      if (_type == 'label') {
        model.set('editable', true)
        // model.set('draggable', false)
      }

      if (_type == 'wrapper') {
        model.set('hoverable', false)
        model.set('resizable', false)
        model.set('draggable', false)
        model.addStyle({
          position: 'static',
          padding: '0',
          'overflow-x': 'auto',
          'overflow-y': 'auto'
        });
      }

      // 不显示name角标
      model.set('badgable', false);
    });

    // 修改画布中的组件时，更新图层元素
    // let layerTimer = null;
    // const delayRenderLayer = function() {
    //   if (layerTimer) {
    //     clearTimeout(layerTimer);
    //     layerTimer = null;
    //   }
    //
    //   layerTimer = setTimeout(() => editor.LayerManager.render(), 500);
    // }
    //
    // editor.on('component:add', delayRenderLayer);
    // editor.on('component:remove', delayRenderLayer);

    let dragEnd;
    let dragStartData;
    editor.on("component:drag:start", function ({target, parent}) {
      if (!dragStartData && target.getStyle().position && parent) {
        const tsty = target.getStyle();
        const t_left = tsty.left ? Number(tsty.left.replace(/px|%|vw/g, '')) : 0;
        const t_top = tsty.top ? Number(tsty.top.replace(/px|%|vh/g, '')) : 0;
        const psty = parent.getStyle();
        const p_left = psty.left ? Number(psty.left.replace(/px|%|vw/g, '')) : 0;
        const p_top = psty.top ? Number(psty.top.replace(/px|%|vh/g, '')) : 0;
        const left = p_left + t_left;
        const top = p_top + t_top;
        dragStartData = {
          parent: {id: parent.getId(), left: p_left, top: p_top},
          left,
          top
        };
      }
    });
    editor.on("component:drag:end", function ({target, parent, index = 0}) {
      if (!dragEnd && target.getStyle().position && parent && dragStartData && dragStartData.parent && parent.getId() !== dragStartData.parent.id) {
        dragEnd = requestAnimationFrame(function () {
          const targetStyle = target.getStyle();
          const {position} = targetStyle;
          if (position == 'absolute') {
            let {left, top} = dragStartData;
            if (parent.getId() == 'wrapper') {
              left = `${left}px`;
              top = `${top}px`;
              target.setStyle({...targetStyle, left, top, position});
            } else {
              const _ = parent.getStyle();
              const _left = _.left ? Number(_.left.replace(/px|%|vw/g, '')) : 0;
              const _top = _.top ? Number(_.top.replace(/px|%|vw/g, '')) : 0;
              left = `${left - _left}px`;
              top = `${top - _top}px`;
              target.setStyle({...targetStyle, left, top, position});
            }
          }
          cancelAnimationFrame(dragEnd);
          dragEnd = null;
          dragStartData = null;
        });
      } else {
        dragStartData = null;
      }

      if (target) {
        const tarEl = target.getEl();
        const parent = tarEl.parentNode;

        // place target
        tarEl.remove();
        parent.insertBefore(tarEl, parent.children[index]);
      }
    });

    // add block
    if (blockOptions) {
      const bm = editor.BlockManager;
      let defaultBlockOptions = blockOptions()
      bm.remove(ComponentName);
      bm.add(ComponentName, {
        category: category,
        ...defaultBlockOptions,
        content: `${viewTemplate()}${viewStyle()}`,
        render: function ({el, model, className, prefix: ppfx}) {
          return `
            ${model.get('media') ? `<div class="${className}__media">${model.get('media')}</div>` : ''}
            <i class="spicon icon-shoucang-bai favorite-icon" data-block-type="${model.id}"></i>
            <div class="${className}-label">${model.get('label')}</div>
          `
        }
      });

      // // 浮窗组件的筛选器
      // let mapFiltered = ["下钻热力弹框平面地图","弹框3D地图","3D地图","散点地图","热力图地图坐标系"]
      // for(let i=0;i<blocks.length;i++){
      //   if(mapFiltered.includes(blocks.models[i].attributes.label)){
      //     var mapBlocks = blocks[i]
      //   }
      // }
      // if(mapBlocks){
      //   const newBlocksEl = bm.render(mapBlocks, { external: true });
      //   document.getElementById('float-blocks').appendChild(newBlocksEl);
      // }

    }

    let defaultCmpOptions = componentOptions();
    let defaultSpOptions = spOptions();

    defaultCmpOptions.traits = defaultCmpOptions.traits || [];

    if (type == 'text') {
      defaultCmpOptions.traits = defaultCmpOptions.traits ? textTraits.concat(defaultCmpOptions.traits) : textTraits;
      defaultCmpOptions = {...textDefaultTraits, ...defaultCmpOptions}
    }
    if (ComponentName.indexOf('Chart') > 0) {
      defaultCmpOptions.traits = defaultCmpOptions.traits ? echartsBasicTraits.concat(defaultCmpOptions.traits) : echartsBasicTraits;
      defaultCmpOptions = {...echartsBasicDefaultTraits, ...defaultCmpOptions}
    }

    if (defaultSpOptions) {
      if (defaultCmpOptions.traits) {

        if (defaultSpOptions.inPorts || defaultSpOptions.outPorts) {
          defaultCmpOptions.traits.unshift({
            name: 'backNode',
            label: '后面板节点',
            type: 'switch',
            changeProp: 1,
          });
        }

        defaultCmpOptions.backNode = defaultSpOptions.inPorts && !!defaultSpOptions.inPorts.length || defaultSpOptions.outPorts && !!defaultSpOptions.outPorts.length;

        if (defaultSpOptions.docUrl) {
          defaultCmpOptions.traits.unshift({
            name: 'docUrl',
            label: '文档',
            type: 'link',
            href: defaultSpOptions.docUrl
          })
        }

        if (!!defaultSpOptions.author || !!defaultSpOptions.email) {
          defaultCmpOptions.traits.unshift({
            name: 'authorInfo',
            label: '开发者',
            type: 'authorInfo',
            author: defaultSpOptions.author,
            email: defaultSpOptions.email
          })
        }

        defaultCmpOptions.traits.unshift({
          type: 'sp-component-name',
          label: '组件名称',
          name: 'name',
          value: defaultSpOptions.label
        })
      }
    }

    // add component
    const dc = editor.DomComponents;
    const defaultType = dc.getType(type);
    const defaultModel = defaultType.model;
    const defaultView = defaultType.view;
    const scriptContent = onModelScript.toString();

    const notifyRendered = () => {
      return `
        var globalEventDOMId = this.getAttribute('id');
        var eventId = 'call-' + globalEventDOMId;
        setTimeout(function() {
          if (window.ee) {
            window.ee.emit('front-component-loaded', eventId);
          }

          ${(function () {
        return globalEventListeners.dashboardComponentLoad
      })()}

          ${(function () {
        return globalEventListeners.resizeOnLoad
      })()}
        }, 0);
      `
    };

    const modelUpdatedHandlers = {
      attributes: function (newValue, oldValue) {
        if (newValue.name !== oldValue.name) {
          if (!newValue.sp) {
            return;
          }

          newValue.sp.metadata.label = newValue.name;
          editor.trigger('sp:node:metadata', newValue.id, newValue.sp.metadata)
        }
      }
    }
    const handleModelUpdated = function (type, newValue, oldValue) {
      modelUpdatedHandlers[type] && modelUpdatedHandlers[type].call(this, newValue, oldValue)
    }

    const script = `
      try {
        ${scriptContent.slice(scriptContent.indexOf('{') + 1, scriptContent.lastIndexOf('}'))}

        ${notifyRendered()}
      } catch (e) {
        console.log('错误组件: ' + this.getAttribute('id'), e)
      }
    `
    const _view = {
      init: onViewInit,
      onRender({editor, el, model}) {
        onViewRender.call(this, editor, el, model)
      },
      removed() {
        onViewRemoved.apply(this)
      },
      onViewExtend: onViewExtend,
      ...viewMethods()
    }
    if (defaultCmpOptions.childrenSelectable == false) {
      _view.events = {
        'dblclick': 'elDblclick'
      };
      _view.elDblclick = function (ev) {
        ev.stopPropagation();
        if (this.model.get('childrenSelectable') == false) {
          if (this.$el.hasClass('no-events')) {
            this.model.components().forEach(m => {
              m.set('selectable', true);
              m.set('editable', true);
              m.set('removable', false);
              m.set('copyable', false);
            });
            this.$el.removeClass("no-events")
          } else {
            this.model.components().forEach(m => {
              m.set('selectable', false);
              m.set('editable', false);
            });
            this.$el.addClass("no-events")
          }
        }
      }
    }
    dc.addType(ComponentName, {
      model: defaultModel.extend({
          defaults: {
            ...defaultModel.prototype.defaults,
            name: _.get(i18n, `zh_CN.domComponents.names.${ComponentName}`, ComponentName),
            ...defaultCmpOptions,
            // 构建一个单独的方法
            script
          },
          init() {
            // 设置在后面板的显示参数
            if (defaultSpOptions && (defaultSpOptions.inPorts || defaultSpOptions.outPorts)) {
              const {
                label = "未定义名称",
                icon,
                docUrl = "",
                author = "",
                email = "",
                inPorts = [],
                outPorts = []
              } = defaultSpOptions;
              let ports = inPorts.map((d) => ({
                ...d,
                type: 'data',
                display: true,
                ioType: 'in'
              })).concat(outPorts.map((d) => ({
                ...d,
                type: 'data',
                display: true,
                ioType: 'out'
              })));

              let spLabel = label + `${this.ccid}`;
              if (defaultCmpOptions.traits) {
                const name = defaultCmpOptions.traits.find(({name}) => name === 'name')
                if (name) {
                  spLabel = name.value;
                }
              }
              this.addAttributes({
                sp: {
                  metadata: {
                    label: spLabel,
                    icon: icon,
                    def: {
                      type: 22,
                      docUrl: docUrl,
                      author: author,
                      email: email,
                      params: {},
                      ports: ports
                    },
                    ui: {
                      tabs: {}
                    }
                  }
                }
              });
            }
          },
          updated: handleModelUpdated,
          ...methods
        },
        {
          isComponent(el) {
            //组件自定义组件识别方式
            if (isComponent) {
              return isComponent(el)
            }
            if (el.getAttribute && el.getAttribute('data-gjs-type') === ComponentName) {
              return {type: ComponentName};
            }
          }
        }
      ),
      view: defaultView.extend(_view)
    });

  };

  return componentGenerator
};

const loadFromList = (componentList, category, editor, options) => {
  componentList.map((subComponentDefine) => load(subComponentDefine, category)(editor, options));
}

export default {loadFromList}
