import _ from 'underscore';
import { v4 as uuidv4 } from 'uuid';

import { GRAPESJS_PPFX } from '../../../configuration/constants';
import './multiselect.less';

import alingLeftSvg from '../../../assets/align-left.svg';
import alingCenterSvg from '../../../assets/align-center.svg';
import alingRightSvg from '../../../assets/align-right.svg';
import alingTopSvg from '../../../assets/align-top.svg';
import alingMiddleSvg from '../../../assets/align-middle.svg';
import alingBottomSvg from '../../../assets/align-bottom.svg';
import combinationSvg from '../../../assets/combination.svg';

import './multiselect.less';

class Group {
  constructor(editor) {
    _.bindAll(this, [
      'createGroup',
      'deleteGroup'
    ]);

    this.editor = editor;
    this.store = {};

    this.groups = [];
    this.compIdToGroupMap = {};

    editor.on('component:selected', (comp) => {
      if (comp.get('type') === 'wrapper') {
        return
      }

      this.selectedGroup = this.findGroupByCid(comp.cid);
      if (!_.isEmpty(this.selectedGroup)) {
        this.editor.selectAdd(this.findComponentsByCid(this.selectedGroup.components));
      }
    })
  }

  createGroup() {
    const selAll = this.editor.getSelectedAll();
    const group = { id: uuidv4(), components: [] };
    const { components } = group;

    selAll.forEach(sel => {
      if (components.indexOf(sel.cid) === -1) {
        components.push(sel.cid);
      }

      const compId = sel.cid;
      if (this.compIdToGroupMap[compId]) {
        const grp = this.findGroupByCid(compId);

        if (!_.isEmpty(grp) && !grp.toDelete) {
          grp.components.forEach((cid) => {
            this.compIdToGroupMap[cid] = group.id;

            if (cid !== compId) {
              if (components.indexOf(cid) === -1) {
                components.push(cid);
              }
            }
          });
        }

        grp.toDelete = true;
      }
      this.compIdToGroupMap[compId] = group.id;
    });

    this.groups = this.groups.filter(grp => !grp.toDelete);
    this.groups.push(group);
    this.selectedGroup = group;

    this.save();

    return group;
  }

  deleteGroup() {
    const group = this.selectedGroup;

    if (group) {
      group.components.forEach(cid => {
        delete this.compIdToGroupMap[cid];
      })
      this.groups = this.groups.filter(grp => grp.id !== group.id);
    }
    this.save();
  }

  findGroupByCid(compId) {
    if (!_.isArray(compId)) {
      compId = [compId];
    }

    const groupIds = compId.map(cid => this.compIdToGroupMap[cid]);
    return _.find(this.groups, (grp) => groupIds.indexOf(grp.id) !== -1) || {};
  }

  findComponentsByCid(cid) {
    if (!_.isArray(cid)) {
      cid = [cid];
    }

    return this.editor.getComponents().filter(comp => cid.indexOf(comp.cid) !== -1);
  }

  save() {
    this.editor.store();
  }
}

export default class MultipleSelector {
  constructor(editor, opts) {
    _.bindAll(this, [
      'destroy',
      'getGroups',
      'loadGroups',
      'handleContextMenu',
      'handleMouseDown',
      'handleMouseMove',
      'handleMouseUp',
      'handleAlignCenter',
      'handleAlignMiddle',
      'handleAlignLeft',
      'handleAlignRight',
      'handleAlignTop',
      'handleAlignBottom',
    ]);

    this.name = 'MultiSelector';

    this.editor = editor;
    this.em = this.editor.getModel();
    this.$multiSelector = null;
    this.multiSelected = false;
    this.selectRectangleBox = { rectleft: Infinity, recttop: Infinity, rectright: -Infinity, rectbottom: -Infinity };

    this.groupHandler = new Group(editor);

    editor.on('component:selected', comp => {
      const selAll = editor.getSelectedAll();
      if (comp.get("type") !== "cell") {
        if (selAll.length > 1) {
          this.activate();
          this.multiSelected = true;
        } else {
          this.deactivate();
          this.multiSelected = false;
        }
      }
    });
  }

  activate() {
    const selAll = this.editor.getSelectedAll();
    selAll.forEach(sel => {
      this.computeSelectRectangleBox(sel);
    });

    if (!this.$multiSelector) {
      this.createMultiSelector();
    }

    this.show();
  }

  computeSelectRectangleBox(sel) {
    const wrapper = this.editor.getWrapper();
    const { rectleft, recttop, rectright, rectbottom } = this.selectRectangleBox;
    if (sel.ccid !== wrapper.ccid) {
      const $el = $(sel.getEl());
      const { left, top } = $el.position();

      this.selectRectangleBox = {
        rectleft: Math.min(left, rectleft),
        recttop: Math.min(top, recttop),
        rectright: Math.max(left + $el.outerWidth(), rectright),
        rectbottom: Math.max(top + $el.outerHeight(), rectbottom)
      }
    }
  }

  createMultiSelector() {
    this.$multiSelector = $(`<div class="${GRAPESJS_PPFX}multiple-selector"></div>`);
    this.$alignment = $(`<div class="${GRAPESJS_PPFX}multiple-selector-toolbar">
                           <div class="alignment-icons" data-align="Left">${alingLeftSvg}</div>
                           <div class="alignment-icons" data-align="Center">${alingCenterSvg}</div>
                           <div class="alignment-icons" data-align="Right">${alingRightSvg}</div>
                           <div class="alignment-icons" data-align="Top">${alingTopSvg}</div>
                           <div class="alignment-icons" data-align="Middle">${alingMiddleSvg}</div>
                           <div class="alignment-icons" data-align="Bottom">${alingBottomSvg}</div>
                           <div class="combination-icons" data-combination="create">${combinationSvg}</div>
                           <div class="combination-icons" data-combination="delete">${combinationSvg}</div>
                         </div>`);
    $('.gjs-cv-canvas__frames').append(this.$multiSelector);

    const _this = this;
    this.$alignment.on('mousedown', (e) => {
      e.stopPropagation();
    })
    this.$alignment.on('click', '.alignment-icons', function (e) {
      e.stopPropagation();
      const alignType = this.getAttribute('data-align');

      _this.handleAlign(alignType);
    });

    this.$alignment.on('click', '.combination-icons', function (e) {
      e.stopPropagation();
      const action = this.getAttribute('data-combination');
      _this.groupHandler[`${action}Group`]();
    });

    this.$multiSelector.on('mousedown', this.handleMouseDown);
    this.$multiSelector.on('contextmenu', this.handleContextMenu);
  }

  destroy() { }

  getGroups() {
    return {
      groups: this.groupHandler.groups,
      compIdToGroupMap: this.groupHandler.compIdToGroupMap,
    }
  }

  loadGroups({ groups = [], compIdToGroupMap = {} }) {
    this.groupHandler.groups = groups;
    this.groupHandler.compIdToGroupMap = compIdToGroupMap;
  }

  handleAlignCenter({ center }) {
    const selAll = this.editor.getSelectedAll();
    // 所有选中的组件同时移动
    selAll.forEach(sel => {
      sel.addStyle({ left: `${center - sel.getEl().clientWidth * 0.5}px` });
    });
  }

  handleAlignMiddle({ middle }) {
    const selAll = this.editor.getSelectedAll();
    // 所有选中的组件同时移动
    selAll.forEach(sel => {
      sel.addStyle({ top: `${middle - sel.getEl().clientHeight * 0.5}px` });
    });
  }

  handleAlignLeft({ left }) {
    // 所有选中的组件同时移动
    this.editor.getSelectedAll().forEach(sel => {
      sel.addStyle({ left: `${left}px` });
    });
  }

  handleAlignRight({ right }) {
    const selAll = this.editor.getSelectedAll();
    // 所有选中的组件同时移动
    selAll.forEach(sel => {
      sel.addStyle({ left: `${right - sel.getEl().clientWidth}px` });
    });
  }

  handleAlignTop({ top }) {
    const selAll = this.editor.getSelectedAll();
    // 所有选中的组件同时移动
    selAll.forEach(sel => {
      sel.addStyle({ top });
    });
  }

  handleAlignBottom({ bottom }) {
    const selAll = this.editor.getSelectedAll();
    // 所有选中的组件同时移动
    selAll.forEach(sel => {
      sel.addStyle({ top: `${bottom - sel.getEl().clientHeight}px` });
    });
  }

  handleAlign(type) {
    const left = Number(this.$multiSelector.css('left').split('px')[0]);
    const top = Number(this.$multiSelector.css('top').split('px')[0]);
    const center = left + this.$multiSelector.width() * 0.5;
    const middle = top + this.$multiSelector.height() * 0.5;
    const bottom = top + this.$multiSelector.height();
    const right = left + this.$multiSelector.width();

    this[`handleAlign${type}`]({ left, top, right, bottom, center, middle });

    this.selectRectangleBox = { rectleft: Infinity, recttop: Infinity, rectright: -Infinity, rectbottom: -Infinity }
    this.editor.getSelectedAll().forEach(sel => this.computeSelectRectangleBox(sel));
    this.show();
  }

  handleContextMenu(ev) {
    ev.preventDefault();
    ev.stopPropagation();
  }

  handleMouseDown(ev) {
    ev.stopPropagation();
    this.multipleMove = true;

    this.startPosition = { x: ev.screenX, y: ev.screenY };
    const $wrapper = this.editor.getWrapper().getEl();

    switch (ev.which) {
      case 1:
        this.$multiSelector.on('mousemove', this.handleMouseMove);
        this.$multiSelector.on('mouseup', this.handleMouseUp);
        $wrapper.addEventListener('mousemove', this.handleMouseMove);
        $wrapper.addEventListener('mouseup', this.handleMouseUp);
        break;
    }
  }

  handleMouseMove(event) {
    event.stopPropagation();
    if (this.multipleMove) {
      const selAll = this.editor.getSelectedAll();
      const delta = { x: event.screenX - this.startPosition.x, y: event.screenY - this.startPosition.y };
      this.selectRectangleBox = { rectleft: Infinity, recttop: Infinity, rectright: -Infinity, rectbottom: -Infinity }
      const canvasZoom = 100 / this.editor.Canvas.getZoom();

      // 所有选中的组件同时移动
      selAll.forEach(sel => {
        let style = sel.getStyle();
        if (style.left == '' || style.top == '') {
          style = $el.getBoundingClientRect();
        }
        sel.addStyle({ left: `${parseInt(style.left) + delta.x * canvasZoom}px`, top: `${parseInt(style.top) + delta.y * canvasZoom}px` });

        // 遍历选中的组件，计算出上下左右的边界位置
        this.computeSelectRectangleBox(sel);
      });

      // 更新初始移动位置
      this.startPosition = { x: event.screenX, y: event.screenY };

      this.show();
    }
  }

  handleMouseUp() {
    this.multipleMove = false;
    const $wrapper = this.editor.getWrapper().getEl();

    this.$multiSelector.off('mousemove', this.handleMouseMove);
    this.$multiSelector.off('mouseup', this.handleMouseUp);
    $wrapper.removeEventListener('mousemove', this.handleMouseMove);
    $wrapper.removeEventListener('mouseup', this.handleMouseUp);
  }

  deactivate() {
    this.multipleMove = false;
    const $wrapper = this.editor.getWrapper().getEl();

    this.selectRectangleBox = { rectleft: Infinity, recttop: Infinity, rectright: -Infinity, rectbottom: -Infinity }

    if (this.$multiSelector) {
      this.$multiSelector.off('mousemove', this.handleMouseMove);
      this.$multiSelector.off('mouseup', this.handleMouseUp);
      $wrapper.removeEventListener('mousemove', this.handleMouseMove);
      $wrapper.removeEventListener('mouseup', this.handleMouseUp);
      this.$multiSelector.hide();
    }
  }

  show() {
    let { rectleft, recttop, rectright, rectbottom } = this.selectRectangleBox;

    this.$multiSelector.css({ left: `${rectleft}px`, top: `${recttop}px` }).width(rectright - rectleft).height(rectbottom - recttop).show();
  }
}
