import * as React from 'react';
import { Layer } from './layer';
import { domUtils } from '@gedit/utils/lib/browser';
import {
  EditorState,
  EditorStateConfigEntity,
  PathPointSelectionEntity,
  PlaygroundConfigEntity,
  RulerConfigEntity,
  SelectorConfigEntity,
  SnaplineConfigEntity
} from './config';
import { able, Entity, entity, TransformData } from '../../common';
import { Rectangle } from '@gedit/math';
import {
  Dragable,
  Resizable,
  ResizePayload,
  ResizeType,
  Rotatable,
  RotatePayload,
  Selectable,
  SelectControlKey,
  SelectPayload,
  SelectState
} from '../able';
import { PlaygroundDrag } from '../utils/playground-drag';
import { SelectorMultipleRenderer } from './selector/selector-multiple-renderer';
import { SelectorEntityRendererProps } from './selector/selector-entity-renderer';
import { SelectorBoundsProps } from './selector/selector-bounds';
import { PipelineLayerPriority } from '../pipeline';
import { Adsorber } from '../utils/adsorber';

function getSelectControlKeys(selectedEntities: Entity[]): SelectControlKey[] {
  const arr: SelectControlKey[][] = selectedEntities.map(e => e.getData<SelectState>(SelectState)!.controlKeys);
  if (arr.length <= 1) return arr[0] || [];
  const others = arr.slice(1);
  return arr[0].filter(key => key !== 'rotate' && !others.find(o => !o.includes(key)));
}

export class SelectorLayer extends Layer {
  @entity(PlaygroundConfigEntity) protected playgroundConfigEntity: PlaygroundConfigEntity;
  @entity(SelectorConfigEntity) protected selectorConfigEntity: SelectorConfigEntity;
  @entity(RulerConfigEntity) protected rulerConfigEntity: RulerConfigEntity;
  @entity(SnaplineConfigEntity) protected snaplineEntity: SnaplineConfigEntity;
  @entity(EditorStateConfigEntity) protected editorStateConfig: EditorStateConfigEntity;
  @entity(PathPointSelectionEntity) protected pathPointSelection: PathPointSelectionEntity;
  @able(Selectable) protected selectableNodes: Entity[];
  @able(Dragable) protected dragableNodes: Entity[];
  readonly node = domUtils.createDivWithClass('gedit-selector');
  protected selectorBounds?: Rectangle;
  protected adsorbRefs?: Rectangle[];
  protected adsorbLines?: Adsorber.Line[];
  constructor() {
    super();
    this.onControlClick = this.onControlClick.bind(this);
    this.toDispose.pushAll([
      this.selectorResize,
      this.selectorRotate
    ]);
  }
  /**
   * 选择器旋转
   */
  protected selectorRotate = new PlaygroundDrag({
    onDragStart: e => {
      this.adsorbRefs = Adsorber.getRefsFromEntities(this.entityManager, this.selectedEntities, this.config);
      this.adsorbLines = this.snaplineEntity.getAdsoberLines();
      this.selectedEntities.forEach(n => n.addAbles(Rotatable));
      this.dispatch<RotatePayload>(RotatePayload, {
        ...e,
        adsorbRefs: this.adsorbRefs,
        adsorbLines: this.adsorbLines,
      });
    },
    onDrag: e => {
      this.dispatch<RotatePayload>(RotatePayload, {
        ...e,
        adsorbRefs: this.adsorbRefs,
        adsorbLines: this.adsorbLines,
      });
    },
    onDragEnd: e => {
      this.adsorbLines = undefined;
      this.adsorbRefs = undefined;
      this.dispatch<RotatePayload>(RotatePayload, e);
      this.selectedEntities.forEach(n => n.removeAbles(Rotatable));
      this.draw();
    }
  });
  protected selectorResize = new PlaygroundDrag<ResizeType>({
    onDragStart: (e, ctx) => {
      this.selectedEntities.forEach(n => n.addAbles(Resizable));
      this.adsorbRefs = Adsorber.getRefsFromEntities(this.entityManager, this.selectedEntities, this.config);
      this.adsorbLines = this.snaplineEntity.getAdsoberLines();
      this.dispatch<ResizePayload>(ResizePayload, {
        type: ctx!,
        startPos: e.startPos,
        endPos: e.endPos,
        scale: e.scale,
        isStart: e.isStart,
        isMoving: e.isMoving,
        adsorbLines: this.adsorbLines,
        adsorbRefs: this.adsorbRefs,
      });
    },
    onDrag: (e, ctx) => {
      this.dispatch<ResizePayload>(ResizePayload, {
        type: ctx!,
        startPos: e.startPos,
        endPos: e.endPos,
        scale: e.scale,
        isStart: e.isStart,
        isMoving: e.isMoving,
        adsorbLines: this.adsorbLines,
        adsorbRefs: this.adsorbRefs,
      });
    },
    onDragEnd: (e, ctx) => {
      this.dispatch<ResizePayload>(ResizePayload, {
        type: ctx!,
        startPos: e.startPos,
        endPos: e.endPos,
        scale: e.scale,
        isStart: e.isStart,
        isMoving: e.isMoving,
      });
      this.adsorbLines = undefined;
      this.adsorbRefs = undefined;
      this.selectedEntities.forEach(n => n.removeAbles(Resizable));
      this.draw();
    }
  });

  /**
   * 数据准备后状态
   */
  onReady(): void {
    this.toDispose.pushAll([
      this.listenPlaygroundEvent('mousemove', this.onHover.bind(this), PipelineLayerPriority.TOOL_LAYER),
      this.listenPlaygroundEvent('mousedown', this.onMouseDown.bind(this), PipelineLayerPriority.TOOL_LAYER),
      this.listenPlaygroundEvent('dblclick', this.onDoubleClick.bind(this), PipelineLayerPriority.TOOL_LAYER),
      this.pathPointSelection.onSelectionChanged(() => {
        if (this.pathPointSelection.selection?.length) {
          this.clearHoveredNode();
          this.draw();
        }
      }),
    ]);
  }

  protected onDoubleClick(e: MouseEvent): boolean | undefined {
    const target = e.target as HTMLElement;
    if (this.isDisabled
      || this.isInOperation()
      || (target && target.classList.contains('gedit-selector-control'))
      // 双击选择框内容
      || !this.isSelectorBoundsContains(e)
    ) {
      return;
    };
    const selectNode = this.getNodeByMouseEvent(e, false);
    if (selectNode) {
      const zIndexHovered = Selectable.getZIndex(selectNode);
      const additional = e.metaKey || e.shiftKey || e.ctrlKey;
      this.clearHoveredNode();
      this.clearSelectedNodes();
      this.dispatch<SelectPayload>(SelectPayload, {zIndex: zIndexHovered, additional, selectionService: this.selectionService });
      return true;
    }
    // const ignoreEntities = this.selectedEntities;
  }
  /**
   * 点击控制器
   */
  protected onControlClick(key: string, e: React.MouseEvent): void {
    this.clearHoveredNode();
    e.stopPropagation();
    e.preventDefault();
    if (key === 'rotate') {
      this.selectorRotate.start(e.clientX, e.clientY, this.config);
    } else {
      this.selectorResize.start(e.clientX, e.clientY, this.config, key as ResizeType);
    }
  }

  protected isMoving: boolean = false;

  protected isInOperation(): boolean {
    return this.isMoving || this.selectorRotate.isStarted || this.selectorResize.isStarted;
  }

  protected startMoving(e: MouseEvent): void {
    this.startDrag(
      e.clientX,
      e.clientY,
      {
        entities: this.selectedEntities,
        adsorbLines: this.snaplineEntity.getAdsoberLines(),
        onDrag: () => {
          this.isMoving = true;
          // this.pipelineNode.style.cursor = 'move';
        },
        onDragEnd: dragEvent => {
          this.isMoving = false;
          // this.pipelineNode.style.cursor = 'default';
          const delta = {
            x: dragEvent.endPos.x - dragEvent.startPos.x,
            y: dragEvent.endPos.y - dragEvent.startPos.y,
          };
          if (delta.x !== 0 && delta.y !== 0) {
            this.draw();
          } else {
            // 选择
            this.tryToSelectHoverred();
          }
        },
      }
    );
  }
  protected tryToSelectHoverred(): undefined | boolean {
    if (this.isInOperation() || this.isDisabled) return;
    const hoveredEntity = this.hoveredEntity;
    if (hoveredEntity) {
      const zIndexHovered = Selectable.getZIndex(hoveredEntity);
      this.dispatch<SelectPayload>(SelectPayload, {zIndex: zIndexHovered, selectionService: this.selectionService });
    }
    return;
  }
  protected onMouseDown(e: MouseEvent): undefined | boolean {
    if (this.isDisabled) return;
    // 操作时候阻止后续执行
    if (this.isInOperation()) return true;
    // 点击hover
    if (this.hoveredEntity) {
      const additional = e.metaKey || e.shiftKey || e.ctrlKey;
      const zIndexHovered = Selectable.getZIndex(this.hoveredEntity);
      this.dispatch<SelectPayload>(SelectPayload, {zIndex: zIndexHovered, additional, selectionService: this.selectionService }, () => {
        if (!additional) {
          // 这个在选择器选中后再触发
          this.startMoving(e);
        }
      });
      return true;
    }
    // 鼠标在选择框内部
    if (this.isSelectorBoundsContains(e)) {
      this.startMoving(e);
      return true;
    }
    this.clearSelectedNodes();
    // 发送一个空的触发Select after, 用于更新全局的selectionService
    if (this.entityManager.getEntitiesByAble(Selectable).length) {
      // selectable 没注册，会导致元素坐标全部归 0;
      this.dispatch<SelectPayload>(SelectPayload, { selectionService: this.selectionService });
    }
  }
  onBlur(): void {
    // 去掉hover
    const hoveredEntity = this.hoveredEntity;
    const state = hoveredEntity ? hoveredEntity.getData<SelectState>(SelectState) : undefined;
    if (state) {
      state.hovered = false;
    } else {
      this.draw();
    }
  }
  onFocus(): void {
    this.draw();
  }

  // private hoveredTimeout?: number;
  /**
   * hover状态
   * @param e
   * @protected
   */
  protected onHover(e: MouseEvent): undefined | boolean {
    // if (this.hoveredTimeout) {
    //   window.clearTimeout(this.hoveredTimeout);
    //   this.hoveredTimeout = undefined;
    // }
    const target = e.target as HTMLElement;
    if (this.isDisabled || this.isInOperation() || (target && target.classList.contains('gedit-selector-control'))) {
      this.clearHoveredNode();
      return;
    };
    const oldHoveredEntity = this.hoveredEntity;
    const oldState = oldHoveredEntity ? oldHoveredEntity.getData<SelectState>(SelectState) : undefined;
    // 鼠标在选择框内
    if (this.isSelectorBoundsContains(e)) {
      if (oldState) oldState.hovered = false;
      return;
    }
    const hoveredNode = this.getNodeByMouseEvent(e);
    const stateHovered = hoveredNode ? hoveredNode.getData<SelectState>(SelectState) : undefined;
    if (stateHovered !== oldState) {
      if (oldState) oldState.hovered = false;
      if (stateHovered) {
        stateHovered.hovered = true;
      }
    }
  }
  /**
   * 通过鼠标获取节点
   * @param e
   * @param ignoreSelected
   */
  getNodeByMouseEvent(e: MouseEvent, ignoreSelected: boolean = true): Entity | undefined {
    // 查找hover的节点的zIndex
    let zIndexHovered: number | undefined;
    let transformHovered: TransformData;
    let current: Entity | undefined;
    const mousePos = this.getPosFromMouseEvent(e, true);
    this.selectableNodes.forEach(node => {
      const state = node.getData<SelectState>(SelectState);
      const transform = node.getData<TransformData>(TransformData);
      if (!state || !transform || !state.mouseSelect || (ignoreSelected && this.isSelected(node))) return;
      // 如果父节点被hovered，则优先选择父节点
      if (transformHovered && transformHovered.isParentTransform(transform)) {
        return;
      }
      if (transform.contains(mousePos.x, mousePos.y, state.displayType === 'circle')) {
        if (zIndexHovered === undefined || zIndexHovered < state.zIndex) {
          // // 如果选中的元素在hover的元素内部，则忽略
          // if (selectedBounds && mouseInSelector && transform.bounds.containsRectangle(selectedBounds)) {
          //   return;
          // }
          zIndexHovered = state.zIndex;
          transformHovered = transform;
          current = node;
        }
      }
    });
    return current;
  }
  /**
   * 判断节点是否选中
   * @param node
   */
  isSelected(node: Entity): boolean {
    const state = node.getData<SelectState>(SelectState)!;
    if (state.selected) return true;
    const selectedTransforms = this.selectedEntities.map(s => s.getData<TransformData>(TransformData)!);
    const transform = node.getData<TransformData>(TransformData)!;
    return !!selectedTransforms.find(trans => transform === trans || transform.isParentTransform(trans) || trans.isParentTransform(transform));
  }

  get selectedEntities(): Entity[] {
    return this.selectableNodes.filter(node => node.getData<SelectState>(SelectState)?.selected);
  }
  get hoveredEntity(): Entity | undefined {
    return this.selectableNodes.find(node => node.getData<SelectState>(SelectState)?.hovered);
  }
  get isDisabled(): boolean {
    const currentState = this.editorStateConfig.getCurrentState();
    // 按住 ALT 键时，选择器不 disabled;
    return (currentState !== EditorState.STATE_SELECT && currentState !== EditorState.SHOW_SCENE_RANGE_RULER)
      || this.selectorConfigEntity.disabled
      // pathPoint 有值时，不允许选择
      || !!(this.pathPointSelection.selection?.length);
  }
  get isVisible(): boolean {
    const currentState = this.editorStateConfig.getCurrentState();
    return !currentState?.disableSelector;
  }
  /**
   * 判断选择框是否包含鼠标点
   * @param e
   * @protected
   */
  protected isSelectorBoundsContains(e: MouseEvent): boolean {
    const mousePos = this.getPosFromMouseEvent(e, true);
    if (!this.selectorBounds) return false;
    // 如果是一个需要考虑选择框旋转情况
    if (this.selectedEntities.length === 1) {
      const state = this.selectedEntities[0].getData<SelectState>(SelectState)!;
      const selectedTransform: TransformData = this.selectedEntities[0].getData<TransformData>(TransformData)!;
      // 容器默认采用子元素当选择框
      if (!selectedTransform.isContainer) {
        return selectedTransform.contains(mousePos.x, mousePos.y, state.displayType === 'circle');
      }
    }
    // 选择多个则使用外围框
    return this.selectorBounds.contains(mousePos.x, mousePos.y);
  }
  clearSelectedNodes(): void {
    this.selectedEntities.forEach(node => {
      const data = node.getData<SelectState>(SelectState)!;
      data.selected = false;
    });
  }
  clearHoveredNode(): void {
    const hoveredEntity = this.hoveredEntity;
    if (hoveredEntity) {
      hoveredEntity.getData<SelectState>(SelectState)!.hovered = false;
    }
  }
  /**
   * 渲染实体数据
   */
  drawEntities(selectedEntities: Entity[]): React.ReactNode[] {
    const selectorConfigEntity = this.selectorConfigEntity;
    const EntityRenderer = selectorConfigEntity.getSelectorEntityRenderer();
    const isDisabled = !!this.selectorConfigEntity.config.disabled;
    return this.selectableNodes.map(e => {
      const props = this.getEntityRendererProps(e, selectedEntities, isDisabled);
      if (!props) return <></>;
      return <EntityRenderer {...props} />;
    });
  }

  getEntityRendererProps(selectableEntity: Entity, selectedEntities: Entity[], isDisabled: boolean): (SelectorEntityRendererProps & { key: string }) | undefined {
    const state = selectableEntity.getData<SelectState>(SelectState);
    if (!state) return undefined;
    const selected = !isDisabled && state.selected;
    const hovered = !isDisabled && state.hovered && !this.isInOperation(); // 操作时候禁用hover
    let boundsProps: SelectorBoundsProps = {};
    if (selected) {
      boundsProps = this.selectorConfigEntity.config.selectSelectedBounds || {};
    } else if (hovered) {
      boundsProps = this.selectorConfigEntity.config.selectHoveredBounds || {};
    }
    return {
      key: selectableEntity.id,
      dispatch: this.dispatch,
      playgroundConfig: this.config,
      selected,
      hovered,
      multipleSelected: selectedEntities.length > 1,
      isMoving: this.isMoving && selected,
      isInOperation: this.isInOperation() && selected,
      entity: selectableEntity,
      scale: this.config.finalScale,
      displayType: state.displayType,
      selectableEntities: this.selectableNodes,
      selectControlKeys: state.controlKeys,
      onControlClick: this.onControlClick,
      focused: this.isFocused,
      originBtnDisabled: state.originBtnDisabled,
      boundsProps,
    };
  }

  draw(): React.JSX.Element {
    if (!this.isVisible) {
      return <></>;
    }
    const { selectedEntities } = this;
    const selectConfig = this.selectorConfigEntity.config;
    const selectedTransforms: TransformData[] = selectedEntities.map(node => node.getData<TransformData>(TransformData)!);
    const bounds = this.selectorBounds = Rectangle.enlarge(selectedTransforms.map(t => t.bounds));
    return <>
      {this.drawEntities(selectedEntities)}
      <SelectorMultipleRenderer
        playgroundConfigEntity={this.config}
        isMoving={this.isMoving}
        selectControlKeys={getSelectControlKeys(selectedEntities)}
        scaleVisible={this.rulerConfigEntity.config.scaleVisible}
        bounds={bounds}
        rotate={0}
        visible={!selectConfig.disabled}
        multiple={selectedTransforms.length > 1}
        boundsProps={selectConfig.selectSelectedBounds}
        onControlClick={this.onControlClick}
        displayType={'rectangle'}
        focused={this.isFocused}
      />
    </>;
  }
}
