import pkg from '../../../package.json';
import { defaultSectors, i18n, defaultOptions } from './configuration/editor';
import _ from 'lodash';
import grapesjs from 'grapesjs';

import bootstrapBlocks from '../../../libs/grapesjs/grapesjs-blocks-bootstrap4/index';
import basicBlocks from '../../../libs/grapesjs/grapesjs.basic.blocks';

import customCode from 'grapesjs-custom-code';
import tabs from '../../../libs/grapesjs/grapejs-tabs/grapesjs-tabs.min';

import gpParser from './gpParser';

const html2canvas = require('../../../libs/html2canvas');

import { CATEGORIES } from './configuration/constants';

class SPFrontPanelEditor {
  grapesjs = grapesjs;
  plugins: any[] = [];
  version: string;
  sectors: any;
  zh_CN: any;

  editor: grapesjs.Editor;
  $canvas: JQuery;
  $iframe: JQuery;
  ossPublic: string;
  $search: any;

  addBlock(editor: any, options: any, blockOptions: any, category: any) {
    gpParser.loadFromList([blockOptions], category, editor, options);
  }

  addPlugin(name: any, callback: any) {
    this.grapesjs.plugins.add(name, callback);
    this.plugins.push(name);
  }

  getAllPlugins() {
    return this.plugins;
  }

  getCanvasTpl() {
    return `
      <div class="front-panel">
        <div class="left-section">
          <div id="block-container" class="block-container panels">
            <div class="block-header" ng-init="filtable=false;">
              <p class="panel-title">内置组件</p>
            </div>
            <div id="blocks"></div>
          </div>
          <div id="layers-container" class="layers-container panels">
            <p class="panel-title">图层元素</p>
            <div id="layers"></div>
          </div>
        </div>
        <div class="main-section">
          <div id="editor-container" class="editor-container">
            <div id="editor-viewport" class="editor-viewport" ng-style="viewportZoom">
              <div id="frontpanel-canvas" class="frontpanel-canvas"></div>
            </div>
          </div>
        </div>
        <div class="right-section">
          <div id="style-container" class="style-container panels">
            <div id="traits" class="traits-manager"></div>
            <div id="styles" class="style-manager"></div>
          </div>
        </div>
      </div>
      <div class="screenshot-wrapper"></div>
    `;
  }

  init(opts: any) {
    this.sectors = defaultSectors;

    this.zh_CN = i18n.zh_CN;

    this.plugins = [...this.plugins, ...Object.keys(grapesjs.plugins.getAll())];
    const { storageManager, styleManager } = defaultOptions;

    const options = Object.assign(
      {},
      {
        ...defaultOptions,
        ...opts,
        storageManager: {
          ...storageManager,
          stepsBeforeSave: 6,
          ...(opts.storageManager ? opts.storageManager : {}),
        },

        plugins: this.plugins,

        i18n: _.extend(defaultOptions.i18n, opts.i18n),

        styleManager: _.extend(styleManager, opts.styleManager),
        pluginsOpts: opts.pluginsOpts || {},
      }
    );

    const containerId = options.container;
    const $canvasContainer = document.querySelector(`${containerId}`);

    if ($canvasContainer) {
      $canvasContainer.innerHTML = this.getCanvasTpl();
    }

    const $blockContainer = $canvasContainer.querySelector('#block-container');
    const $layerContainer = $canvasContainer.querySelector('#layers-container');

    $blockContainer
      .querySelector('.panel-title')
      .addEventListener('click', () => {
        $layerContainer.classList.remove('open');
      });
    $layerContainer
      .querySelector('.panel-title')
      .addEventListener('click', () => {
        $layerContainer.classList.toggle('open');
      });

    options.container = '#frontpanel-canvas';
    options.layerManager = Object.assign({}, options.layerManager, {
      appendTo: '#layers',
    });
    options.blockManager = Object.assign({}, options.blockManager, {
      appendTo: '#blocks',
    });
    options.newBlocksEl = Object.assign({}, options.newBlocksEl, {
      appendTo: '#float-blocks',
    });
    options.traitManager = Object.assign({}, options.traitManager, {
      appendTo: '#traits',
    });
    options.styleManager = Object.assign({}, options.styleManager, {
      appendTo: '#styles',
    });

    const editor = grapesjs.init(options) as any;
    const layerManager = editor.LayerManager.getAll();

    layerManager.opt.isCountable = (model: any) => {
      return model.get('type') !== 'sp-code';
    };

    // 组件列表上方搜索栏
    this.renderBlockSearch(options.blockManager.appendTo);

    editor.Panels.removeButton('options', 'sw-visibility');
    editor.Panels.removeButton('views', 'open-tm');
    editor.stopCommand('sw-visibility');

    [
      [
        customCode,
        {
          blockCustomCode: {
            label: `
              <svg viewBox="0 0 24 24">
                <path d="M14.6 16.6l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4m-5.2 0L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4z"></path>
              </svg>
              <div>自定义代码</div>
            `,
            category: CATEGORIES.OTHER,
          },
        },
      ],
      [
        tabs,
        {
          tabsBlock: {
            label: `
              <svg class="gjs-block-svg" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                <g fill-rule="evenodd">
                  <path d="M22 9.3c0-.8-.5-1.3-1.3-1.3H3.4C2.5 8 2 8.5 2 9.3v7.4c0 .8.5 1.3 1.3 1.3h17.4c.8 0 1.3-.5 1.3-1.3V9.3zM21 17H3V9h18v8z" fill-rule="nonzero"/><rect x="3" y="5" width="4" height="2" rx=".5"/><rect x="8" y="5" width="4" height="2" rx=".5"/><rect x="13" y="5" width="4" height="2" rx=".5"/>
                </g>
              </svg>
              <div class="gjs-block-label">标签页</div>
            `,
            category: CATEGORIES.CONTROL,
          },
        },
      ],
      [bootstrapBlocks, {}],
      [
        basicBlocks,
        {
          blocks: ['link', 'input', 'checkbox'],
          category: CATEGORIES.CONTROL,
          labelLink: '链接',
          labelInput: '输入框',
          labelCheckBox: '复选框',
        },
      ],
    ].forEach(([plugin, opts]) => plugin(editor, opts));

    this.editor = editor;
    this.$canvas = $(editor.Canvas.getElement());
    this.$iframe = $(editor.Canvas.getFrameEl());
    this.ossPublic = this.getOssPublicUrl();
    this.$canvas.css({
      'max-width': '100%',
      'max-height': '100%',
    });

    return Object.assign({}, this.editor, {
      destroy: this.destroy.bind(this),
    });
  }

  onBlockSearch = (ev: KeyboardEvent) => {
    const bm = this.editor.BlockManager;
    let keyword = this.$search.children('input').val();
    const $close = this.$search.children('.icon-close');
    if (keyword && $close.is(':hidden')) {
      $close.css('display', 'block');
    } else if (!keyword && $close.is(':visible')) {
      $close.css('display', 'none');
    }
    if (
      (ev.type === 'keyup' && (ev.key === 'Enter' || !keyword)) ||
      ev.type === 'click'
    ) {
      if ($(ev.currentTarget).hasClass('icon-close')) {
        this.$search.children('input').val('');
        $close.css('display', 'none');
        keyword = '';
      }
      if (keyword) {
        const blocks = bm.getAll();
        if (blocks.length) {
          const filtered = blocks.filter(function (block) {
            let label = block.get('label');
            const labelIndex = label.indexOf('gjs-block-label');
            if (labelIndex > 0) {
              const _s = labelIndex + 'gjs-block-label'.length + 2;
              const _e = label.lastIndexOf('</div>');
              label = label.slice(_s, _e);
            }
            return (
              (block.get('category').attributes &&
                _.toLower(block.get('category').attributes.label).indexOf(
                  _.toLower(keyword)
                ) > -1) ||
              _.toLower(label).indexOf(_.toLower(keyword)) > -1
            );
          });
          filtered.forEach((block) => block.get('category').set('open', true));
          bm.render(filtered);
        }
      } else {
        bm.render();
      }
    }
  };

  renderBlockSearch(blocksId: string) {
    const $search = (this.$search = $(
      '<div class="block-search"><input type="text" /><i class="spicon icon-search"></i><i class="spicon icon-close"></i></div>'
    ));
    const $close = this.$search.children('.icon-close');
    $(blocksId).before($search);
    $search.on('keyup', 'input',  this.onBlockSearch);
    $search.on('click', '.icon-search',  this.onBlockSearch);
    $search.on('click', '.icon-close',  () => {
      this.$search.children('input').val('');
      $close.css('display', 'none');
      this.editor.BlockManager.render();
    });
  }

  destroy() {
    // window.removeEventListener("wheel", this.handleZoom);

    const Keymaps = this.editor.Keymaps;
    const modules = this.editor.getModel().get('modules');
    const CanvasResizer =
      modules && modules.find((item: any) => item.name === 'CanvasResizer');

    Keymaps.removeAll();
    CanvasResizer && CanvasResizer.destroy();

    this.editor.destroy();

    this.plugins = [];
  }

  getDefaultOptions() {}

  setSize(width: number, height: number) {
    this.$iframe.width(width).height(height);
  }

  getOssHostUrl(endpoint: string, bucket: string) {
    let arr = endpoint.split('//');
    return `${arr[0]}//${bucket}.${arr[1]}`;
  }

  getOssPublicUrl() {
    const _window = window as any;
    if (!_window.appConfig) {
      return '';
    }
    const endpoint = _window.appConfig.oss.endpoint;
    const bucket = _window.appConfig.oss.bucket;
    if (_window.appConfig.ossType === 'minio') {
      return `${endpoint}/${bucket}`;
    } else {
      return `${this.getOssHostUrl(endpoint, bucket)}`;
    }
  }

  async getScreenshot(opt = {}) {
    const body = this.editor.Canvas.getBody();
    const width = body.offsetWidth;
    const height = body.offsetHeight;
    const imgEls = body.getElementsByTagName('img');
    if (imgEls.length > 0) {
      for (let i = 0; i < imgEls.length; i++) {
        let src = imgEls[i].getAttribute('src');
        if (src.indexOf('/oss_public') > -1) {
          src = this.ossPublic + src.substr(11);
          imgEls[i].setAttribute('src', src);
        }
      }
    }
    const styles = body.getElementsByTagName('style');
    if (styles.length > 0) {
      for (let i = 0; i < styles.length; i++) {
        let inner = styles[i].innerHTML;
        if (inner.indexOf('/oss_public') > -1) {
          inner = inner.replace('/oss_public', this.ossPublic);
          styles[i].innerHTML = inner;
        }
      }
    }
    const option = {
      useCORS: true,
      tainttest: true,
      width: width,
      height: height,
      ...opt,
    };
    return await html2canvas(body, option);
  }
}

const spFrontPanelEditor = new SPFrontPanelEditor();
spFrontPanelEditor.version = pkg.version;

export default spFrontPanelEditor;
