import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import MouseTrap from 'mousetrap';
import classNames from 'classnames';
import { withRouter } from 'react-router-dom';
import Simulator from './Simulator';
import SketchRuler from '../../../../components/SketchRuler';
import ToolBar from 'components/ToolBar';
import ComponentDeleteModal from '../ComponentDeleteModal';
import ContextMenu from '../ContextMenu';
import DropBox from './DropBox';
import { updateScreen } from 'models/screens';
import { addComponentByDrop } from 'models/component';
import { changeScale, toggleLeftCollapsed, toggleRightCollapsed } from 'models/create-app';
import { Utils } from '@easyv/react-gui';
import EasyvSwitch from 'components/EasyvSwitch';
import HeaderCard from '../ScreenHeader/HeaderCard';
import styles from './index.less';
import { updateScreentEditModel } from 'models/screen-edit';
import { PartialStateShare, isEditPage } from 'utils/utils';
import { editPageModelLInk } from 'utils/nodes';
import AuthGeneric from 'components/AuthGeneric';
import { modules } from '@/constants/authority';

const { reduceConfig } = Utils;

const thick = 20;
const zoomMin = 0.2;
const zoomMax = 4;

class ScreenPreview extends PureComponent<
  {
    id: number;
    screen: any;
    panel: any;
    scale: number;
    leftCollapsed: boolean;
    rightCollapsed: boolean;
    modalEdit: number | null;
    focusMode: boolean;
    editModel: number | string;
    deadline: Date | string;
    componentAddable: boolean;
    history: History;
    changeScale: (scale) => any;
    addComponentByDrop: (param, signal) => any;
    toggleLeftCollapsed: () => any;
    toggleRightCollapsed: () => any;
    updateScreen: (param) => any;
    updateScreentEditModel: (x) => void;
  },
  {
    startX: number;
    startY: number;
    width: number;
    height: number;
    lines: {
      h: any;
      v: any;
    };
    showLines: boolean;
    space: boolean;
    leftClick: boolean;
    moveStart: number[];
    dragState: string | null;
  }
> {
  contentDOM: HTMLElement | null;
  controller: any;
  simulator: HTMLElement | null;
  ruler: SketchRuler | null;
  container: HTMLDivElement | null;

  constructor(props) {
    super(props);
    this.state = {
      startX: -50,
      startY: -50,
      width: 1920,
      height: 1080,
      lines: {
        h: [],
        v: [],
      },
      showLines: true,
      space: false,
      leftClick: false,
      moveStart: [0, 0, 0, 0],
      dragState: null,
    };
  }

  componentDidMount() {
    this.contentDOM = document.getElementById('j-previewarea');
    this.simulator = document.getElementById('m-simulator-container');
    this.handleResize(1); // 最大放大比例

    // 专注模式才需要监听resize
    window.addEventListener('resize', this.resetRuler, false);
    this.addSpaceListener();
    this.addLeftClickListener();
    this.addResizeListener();
    MouseTrap.bind(['command+=', 'ctrl+='], this.onZoomIn);
    MouseTrap.bind(['command+-', 'ctrl+-'], this.onZoomOut);
    MouseTrap.bind(['command+2', 'ctrl+2'], this.handleResize);
    MouseTrap.bind(['command+1', 'ctrl+1'], this.resetRuler);
    MouseTrap.bind(['alt+command+1', 'alt+ctrl+1'], this.toggleLeft);
    MouseTrap.bind(['alt+command+2', 'alt+ctrl+2'], this.toggleRight);
  }

  handleFileDrag = (e) => {
    e.preventDefault();
    if (e.dataTransfer.effectAllowed === 'all') {
      this.setState({
        dragState: e.type,
      });
    }
  };

  handleFileDrop = (e) => {
    if (this.props.componentAddable) {
      e.preventDefault(); // 阻止默认事件
      this.setState({
        dragState: e.type,
      });
      const files = e.dataTransfer.files;
      if (files.length) {
        const signal = this.controller && this.controller.signal;
        this.props.addComponentByDrop(files, signal);
      }
    }
  };

  UNSAFE_componentWillReceiveProps(nextProps) {
    if (this.props.screen != nextProps.screen || this.props.panel !== nextProps.panel) {
      if (nextProps.screen) {
        const { panelId, lines = { h: [], v: [] }, config } = nextProps.screen;
        let width;
        let height;
        if (panelId) {
          if (nextProps.panel) {
            const { config } = nextProps.panel;
            width = config.width;
            height = config.height;
          }
        } else {
          const { dimension } = reduceConfig(config);
          width = dimension.width;
          height = dimension.height;
        }

        if (lines !== this.state.lines) {
          this.setState({
            lines: lines,
          });
        }

        if (width && width !== this.state.width) {
          this.setState(
            {
              width: width,
            },
            () => {
              this.handleResize(1);
            },
          );
        }

        if (height && height !== this.state.height) {
          this.setState(
            {
              height: height,
            },
            () => {
              this.handleResize(1);
            },
          );
        }
      }
    }
  }

  toggleLeft = () => {
    this.props.toggleLeftCollapsed();
  };

  toggleRight = () => {
    this.props.toggleRightCollapsed();
  };

  resizeListener = (e) => {
    if (this.props.modalEdit) {
      return;
    }

    if (e.ctrlKey || e.metaKey) {
      let { scale } = this.props;
      e.preventDefault(); // down > 0 | up < 0
      const wrap = this.contentDOM;
      if (!wrap) {
        return;
      }

      const simulatorRect = this.simulator?.getBoundingClientRect();
      if (!simulatorRect) {
        return;
      }

      const mouseLeftDis = e.clientX - simulatorRect.left;
      const mouseTopDis = e.clientY - simulatorRect.top;
      const mouseLeftRealDis = mouseLeftDis / scale;
      const mouseTopRealDis = mouseTopDis / scale;

      if (e.deltaY > 0) {
        if (scale - 0.01 < zoomMin) return;
        scale = scale - 0.03;
        this.props.changeScale(scale);
      } else if (e.deltaY < 0) {
        if (scale + 0.01 > zoomMax) return;
        scale = scale + 0.03;
        this.props.changeScale(scale);
      }

      const nextMouseLeftDis = mouseLeftRealDis * scale;
      const nextMouseTopDis = mouseTopRealDis * scale;
      const deltaLeftDis = nextMouseLeftDis - mouseLeftDis;
      const deltaTopDis = nextMouseTopDis - mouseTopDis;
      wrap.scrollLeft += deltaLeftDis;
      wrap.scrollTop += deltaTopDis;
    }
  };

  addResizeListener = () => {
    document.addEventListener('wheel', this.resizeListener, { passive: false });
  };

  removeResizeListener = () => {
    document.removeEventListener('wheel', this.resizeListener);
  };

  handleMouseScroll = (e) => {
    if (this.props.modalEdit) {
      return;
    }

    if (this.state.space && this.state.leftClick) {
      e.stopPropagation();
      const { moveStart } = this.state;
      const x = e.clientX;
      const y = e.clientY;
      const wrap = this.contentDOM;
      if (wrap) {
        wrap.scrollLeft = moveStart[0] - (x - moveStart[2]);
        wrap.scrollTop = moveStart[1] - (y - moveStart[3]);
      }
    }
  };

  leftClickDownListener = (e) => {
    if (e.button === 0 && this.state.space) {
      if (!this.state.leftClick) {
        const wrap = this.contentDOM;
        if (wrap) {
          this.setState(() => ({
            leftClick: true,
            moveStart: [wrap.scrollLeft, wrap.scrollTop, e.clientX, e.clientY],
          }));
        }
      }
    }
  };

  leftClickUpListener = (e) => {
    if (e.button === 0) {
      if (this.state.leftClick) {
        this.setState(() => ({
          leftClick: false,
        }));
      }
    }
  };

  addLeftClickListener = () => {
    document.addEventListener('mousedown', this.leftClickDownListener);
    document.addEventListener('mouseup', this.leftClickUpListener);
  };

  removeLeftClickListener = () => {
    document.removeEventListener('mousedown', this.leftClickDownListener);
    document.removeEventListener('mouseup', this.leftClickUpListener);
  };

  // todo: 把监听放到simulator里
  spaceDownListener = (e) => {
    e.preventDefault();
    if (!this.state.space) {
      PartialStateShare.emit('space_press', true);
      this.setState(() => ({
        space: true,
      }));
    }
  };

  spaceUpListener = (e) => {
    e.preventDefault();
    if (this.state.space) {
      PartialStateShare.emit('space_press', false);
      this.setState(() => ({
        space: false,
      }));
    }
  };

  addSpaceListener = () => {
    MouseTrap.bind('space', this.spaceDownListener, 'keydown');
    MouseTrap.bind('space', this.spaceUpListener, 'keyup');
  };

  removeSpaceListener = () => {
    MouseTrap.unbind('space', 'keydown');
    MouseTrap.unbind('space', 'keyup');
  };

  componentWillUnmount() {
    window.removeEventListener('resize', this.resetRuler, false);

    this.removeSpaceListener();
    this.removeLeftClickListener();
    this.removeResizeListener();
    MouseTrap.unbind(['command+=', 'ctrl+=']);
    MouseTrap.unbind(['command+-', 'ctrl+-']);
    MouseTrap.unbind(['command+2', 'ctrl+2']);
    MouseTrap.unbind(['command+1', 'ctrl+1']);
    MouseTrap.unbind(['alt+command+1', 'alt+ctrl+1']);
    MouseTrap.unbind(['alt+command+2', 'alt+ctrl+2']);
  }

  componentDidUpdate(prevProps) {
    if (
      prevProps.leftCollapsed !== this.props.leftCollapsed ||
      prevProps.rightCollapsed !== this.props.rightCollapsed
    ) {
      this.resetRuler();
    }
    if (this.props.modalEdit && prevProps.modalEdit !== this.props.modalEdit) {
      // 非专注模式，进入场景模式，重置当前视图
      this.handleResize(1);
    }
  }

  handleResize = (maxScale) => {
    if (this.props.modalEdit) {
      // 场景模式并且非专注模式
      maxScale = 1;
    }
    const { width = 1920, height = 1080 } = this.state;
    const contentWidth = this.contentDOM?.clientWidth;
    const contentHeight = this.contentDOM?.clientHeight;
    if (contentWidth == null || contentHeight == null) {
      return;
    }

    const scaleX = (contentWidth - thick - thick * 2) / width;
    const scaleY = (contentHeight - thick - thick * 2) / height;
    let scale = Math.min(scaleX, scaleY);
    scale = maxScale ? (scale > maxScale ? maxScale : scale) : scale;
    this.props.changeScale(scale);

    const screensRect = this.container?.getBoundingClientRect();
    if (!screensRect) {
      return;
    }

    const canvasWidth = width * scale;
    const canvasHeight = height * scale;
    const centerX = (screensRect.width - thick - canvasWidth) / 2;
    const centerY = (screensRect.height - thick - canvasHeight) / 2;
    const preview = this.contentDOM;
    if (preview) {
      preview.scrollLeft = 1994 - thick - centerX;
      preview.scrollTop = 969 - thick - centerY;
    }

    this.ruler?.fixWidthAndHeight();
    this.handleScroll(scale);
  };

  handleLine = (lines, shouldPost) => {
    this.setState({ lines }, () => {
      PartialStateShare.emit('ruler_horizontal_lines', lines.v);
      PartialStateShare.emit('ruler_vertical_lines', lines.h);
    });
    if (shouldPost) {
      this.props.updateScreen({
        id: this.props.id,
        lines,
      });
    }
  };

  handleScroll = (scale) => {
    // screens --> refs.container
    // canvas --> simulator
    const screensRect = this.container?.getBoundingClientRect();
    const canvasRect = this.simulator?.getBoundingClientRect();

    if (!screensRect || !canvasRect) {
      return;
    }

    // 标尺开始的刻度
    scale = Number(scale) || this.props.scale;
    const startX = (screensRect.left + thick - canvasRect.left) / scale;
    const startY = (screensRect.top + thick - canvasRect.top) / scale;
    this.setState({ startX, startY });
  };

  handleCornerClick = () => {
    this.setState(
      (preState) => ({
        showLines: !preState.showLines,
      }),
      () => {
        PartialStateShare.emit('ruler_show', this.state.showLines);
      },
    );
  };

  handleShowLines = (cb) => {
    this.setState(
      {
        showLines: true,
      },
      () => {
        PartialStateShare.emit('ruler_show', true);
        cb();
      },
    );
  };

  onZoomIn = () => {
    const { scale } = this.props;
    const wrap = this.contentDOM;
    if (scale < zoomMax) {
      this.props.changeScale(scale + 0.1);
      if (wrap) {
        wrap.scrollLeft += 10;
        wrap.scrollTop += 10;
      }
    }
  };

  onZoomOut = () => {
    const { scale } = this.props;
    const wrap = this.contentDOM;
    if (scale > zoomMin) {
      this.props.changeScale(scale - 0.1);
      if (wrap) {
        wrap.scrollLeft -= 10;
        wrap.scrollTop -= 10;
      }
    }
  };

  resetRuler = () => {
    this.ruler?.fixWidthAndHeight();
    MouseTrap.trigger('command+2');
  };
  onChange = (value) => {
    updateScreentEditModel(value);
    editPageModelLInk(value, this.props.history);
  };

  render() {
    const {
      scale = 0.1,
      leftCollapsed,
      rightCollapsed,
      modalEdit,
      focusMode,
      deadline,
      editModel,
    } = this.props;
    const { startX, startY, lines, width, height, space, showLines, dragState } = this.state;
    const { h = [], v = [] } = lines;
    return (
      <div className={classNames(styles.container, { [styles.modalEdit]: focusMode })}>
        <DropBox className={styles.dropbox}>
          <div
            ref={(dom) => (this.container = dom)}
            onScroll={this.handleScroll}
            className={classNames(styles.wrap, { [styles.dragover]: dragState === 'dragover' })}
            onDragLeave={this.handleFileDrag}
            onDrop={this.handleFileDrop}
            onDragOver={this.handleFileDrag}>
            <HeaderCard />
            {!modalEdit && (
              <SketchRuler
                ref={(dom) => (this.ruler = dom)}
                thick={thick}
                scale={scale}
                startX={startX}
                startY={startY}
                horLineArr={showLines ? h : []}
                verLineArr={showLines ? v : []}
                handleLine={this.handleLine}
                showLines={showLines}
                cornerActive={showLines ? true : false}
                onCornerClick={this.handleCornerClick}
                handleShowLines={this.handleShowLines}
              />
            )}
            {window.appConfig.LOCAL && deadline && (
              <div className={styles.migrateTip}>
                <i
                  className="icon iconfont icon-msg-warning"
                  style={{ position: 'absolute', top: 1, left: 16, color: '#EB515A' }}
                />
                {`平台迁移中，请在 ${deadline} 前完成数据备份，到期后平台及数据将自动销毁`}
              </div>
            )}

            {isEditPage() && 
              <AuthGeneric authCode={modules.eventNodes}>
                <div style={{ position: 'relative' }}>
                  <div className={styles.switch}>
                    <EasyvSwitch value={editModel} onChange={this.onChange} />
                  </div>
                </div>
              </AuthGeneric>
            }

            <Simulator
              space={space}
              handleMouseScroll={this.handleMouseScroll}
              width={width}
              height={height}
              rulerLines={lines}
            />
          </div>
        </DropBox>

        {!modalEdit && <ToolBar leftShow={!leftCollapsed} rightShow={!rightCollapsed} />}

        {!modalEdit && <ComponentDeleteModal />}
        {!modalEdit && <ContextMenu />}
        {/* {modalEdit && <ComponentEditModal />} */}
      </div>
    );
  }
}

const mapStateToProps = ({ screensById, panelsById, createApp, user, transfer, screenEdit }) => {
  const { id, scale, leftCollapsed, rightCollapsed, modalEdit, focusMode } = createApp;
  const { deadline } = transfer;
  const screen = screensById[id];
  let panel;
  if (screen) {
    const { panelId } = screen;
    if (panelId) {
      panel = panelsById[panelId];
    }
  }
  const { editModel } = screenEdit;
  return {
    id,
    screen,
    panel,
    editModel,
    scale,
    leftCollapsed,
    focusMode,
    rightCollapsed,
    modalEdit,
    deadline,
    componentAddable: !(window.appConfig.LOCAL && user.userInfo.type === 'education'),
  };
};

const mapDispatchToProps = {
  changeScale,
  addComponentByDrop,
  toggleLeftCollapsed,
  toggleRightCollapsed,
  updateScreen,
  updateScreentEditModel,
};

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ScreenPreview));
