import * as React from 'react';
import { connect } from 'unistore/react';
import {
  IStoreState,
  ID,
  IRowDimensions,
  IColDimensions,
  ISelectionType,
  IRow,
  IColumn,
  ICell,
} from '../index.data';
import Row from '../Row';
import { getCell, getCellType, getPresentCell } from '../store/selectors';
import ColGroup from '../ColGroup';
import {
  queryColDimensions,
  queryRowDimensions,
  getCellAttrsFromEvent,
  queryContainerBox,
} from './helper';
import { setDimensions, setContainerBox, tryToggleCollapse } from '../store/actions';
import {
  ID_CELL,
  ID_ROW_INDICATOR,
  FSM_IGNORE,
  ID_COLUMN_INDICATOR,
  ID_SHEET_INDICATOR,
} from '../constants';
import * as Rx from 'rambdax';
import * as R from 'rambda';
import { Store } from 'unistore';
import { EditEvent } from '../StateMachine/index.data';
import RowGroup from '../RowGroup';
import RuntimeContext from '../RuntimeContext';
import { getTypedId, pickPatch } from '../utils';
import { shallowEqualsArray } from 'valor-app-utils';
/*
interface Props {}

interface MappedProps {
  selectionType: ISelectionType | null;
  selectionCellRange: [ID, ID];
  mode: any;
}
*/
interface Props {
  selectionType: ISelectionType | null;
  selectionCellRange: [ID, ID];
  mode: any;
  // for RowGroup
  rows: IRow[];
  columns: IColumn[];
  loadableRowFlags: boolean[];
  loadableColumnFlags: boolean[];
  cells: Record<ID, ICell>;
}

interface MappedActions {
  setDimensions: (
    rowDimensions: IRowDimensions,
    colDimensions: IColDimensions,
  ) => { rowDimensions: IRowDimensions; colDimensions: IColDimensions };
  setContainerDimensions: (
    containerDimensions: IStoreState['containerBox'],
  ) => { containerDimensions: IStoreState['containerBox'] };
  tryToggleCollapse: (
    rowId: ID,
  ) => {
    collapsedRowIds: [];
  } | null;
  // send: (event: EditEvent) => void;
  // attachToStore: (propName: string, body: () => void) => void;
}
class DataSheet extends React.Component<Props & MappedActions, {}> {
  ref = React.createRef<HTMLTableElement>();

  dragging: boolean = false;

  static contextType = RuntimeContext;
  context!: React.ContextType<typeof RuntimeContext>;

  lastSelection: { selectionType: ISelectionType | null; selectionCellRange: [ID, ID] | null } = {
    selectionType: null,
    selectionCellRange: null,
  };

  observer: ResizeObserver;

  constructor(props: Props & MappedActions) {
    super(props);
    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.handleMouseMove = Rx.throttle(this.handleMouseMove, 16);
    this.handleMouseUp = this.handleMouseUp.bind(this);

    this.handleDoubleClick = this.handleDoubleClick.bind(this);

    this.setSheetDimensions = this.setSheetDimensions.bind(this);
    this.setContainerDimensions = this.setContainerDimensions.bind(this);

    this.observer = new ResizeObserver(() => {
      this.setContainerDimensions();
    });
  }

  handleMouseDown(e: MouseEvent) {
    const { selectionType, selectionCellRange } = this.props;
    this.lastSelection = {
      selectionType,
      selectionCellRange,
    };
    // if (this.ref!.contains(e.target! as any)) {
    // 计算每行和每列dim ( 注意是每行 和 每列, 一次性计算完成 )
    //   this.setSheetDimensions();
    // }

    if (this.sendEvent(e, 'MOUSE.DOWN')) {
      // 绑定事件
      document.body.addEventListener('mousemove', this.handleMouseMove);
      document.body.addEventListener('mouseup', this.handleMouseUp);
    }
  }

  setContainerDimensions() {
    const containerDimensions = queryContainerBox(this.ref.current!);
    this.props.setContainerDimensions(containerDimensions);

    // 容器大小变更后, 可能重算
    setTimeout(() => {
      this.setSheetDimensions();
    });
  }

  setSheetDimensions() {
    // 计算每行和每列dim ( 注意是每行 和 每列, 一次性计算完成 )
    const colDimensions = queryColDimensions(this.ref.current!);
    const rowDimensions = queryRowDimensions(this.ref.current!);
    this.props.setDimensions(rowDimensions, colDimensions);
  }

  handleMouseMove(e: MouseEvent) {
    this.dragging = true;
    this.sendEvent(e, 'MOUSE.MOVE');
  }

  handleMouseUp(e: MouseEvent) {
    this.sendEvent(e, 'MOUSE.UP');

    document.body.removeEventListener('mousemove', this.handleMouseMove);
    document.body.removeEventListener('mouseup', this.handleMouseUp);

    this.dragging = false;
  }

  private sendEvent(e: MouseEvent, eventType: 'MOUSE.DOWN' | 'MOUSE.MOVE' | 'MOUSE.UP'): boolean {
    // 注意: 这里需要防止以下元素发出事件: 比如 input , 比如 dialog
    // 这些事件会被解释为: clickoutside 事件
    // 所以, 如果不想发出事件, 那么应标记组件为 className='fsm-ignore'
    if ((e.target as HTMLElement).closest(`.${FSM_IGNORE}`)) return false;

    const [cellType, id] = getCellAttrsFromEvent(e);

    // 暂不支持选择整个sheet
    if (ID_SHEET_INDICATOR === cellType) return false;

    const selectedKey =
      cellType === ID_ROW_INDICATOR
        ? 'selectedRow'
        : cellType === ID_COLUMN_INDICATOR
        ? 'selectedColumn'
        : 'selectedCell';

    const context = this.context!;
    const state = context.store.getState();

    if (
      // 排除picking状态, 此时不应重选单元格
      !(state.mode.editing && state.mode.editing.picking === 'on') &&
      eventType === 'MOUSE.DOWN' &&
      state.isActiving
    ) {
      // 意外情况: 当前正在编辑(Active), 用户点了其它单元格 , 或表格之外
      if (cellType === ID_CELL && id !== null && id + '' !== getPresentCell(state) + '') {
        // 1. 正在编辑, 点了其它单元格
        context.sendFsmEvent({
          type: 'SELECT.CELL',
          selectedCell: id,
          state,
        });
      } else if (cellType === ID_ROW_INDICATOR && id !== null) {
        // 2. 正在编辑, 点了行标头
        context.sendFsmEvent({
          type: 'SELECT.ROW',
          selectedRow: id,
          state,
        });
      } else if (cellType === ID_COLUMN_INDICATOR && id !== null) {
        // 2. 正在编辑, 点了行标头
        context.sendFsmEvent({
          type: 'SELECT.COLUMN',
          selectedColumn: id,
          state,
        });
      }
    } else {
      // 正常情况, 发送鼠标事件到状态机 ( 主要确保可以多选 )
      context.sendFsmEvent({
        type: eventType as any,
        [selectedKey]: id,
        state,
      });
    }

    return true;
  }

  handleDoubleClick(e: React.MouseEvent) {
    // 对于可编辑单元格, 不会触发! (因为editor实际上并不在dataSheet内部)
    // 所以以下代码安全
    const [cellType, id_] = getCellAttrsFromEvent(e.nativeEvent);
    const state = this.context!.store.getState();
    const id = id_ && getTypedId(id_);

    if (cellType === ID_CELL && id) {
      if (getCellType(state, id) === 'row-no') {
        this.props.tryToggleCollapse(id!);
        setTimeout(() => {
          this.context!.setSheetDimensions();
        });
      } else {
        this.context!.sendFsmEvent({ type: 'EDITING' });
      }
    }
  }

  shouldComponentUpdate(nextProps: Props) {
    const props = this.props;
    return (
      nextProps.rows !== props.rows ||
      nextProps.cells !== props.cells ||
      !shallowEqualsArray(nextProps.loadableRowFlags, props.loadableRowFlags) ||
      !shallowEqualsArray(nextProps.loadableColumnFlags, props.loadableColumnFlags)
    );
  }

  componentDidMount() {
    const self = this;
    // self.props.attachToStore('setSheetDimensions', self.setSheetDimensions);
    self.context!.setSheetDimensions = self.setSheetDimensions;
    document.body.addEventListener('mousedown', self.handleMouseDown);
    // 让安装好的列列立即可查询dim
    setTimeout(() => {
      // 立即计算容器宽高一次
      self.setContainerDimensions();
    }, 0);

    if (!this.ref.current) {
      throw new Error('Dastasheet 未找到ref节点');
    }
    const container = this.ref.current.closest('.spread-sheet');
    if (!container) {
      throw new Error('没有找到class-name = spread-sheet 的容器节点');
    }

    this.observer.observe(container);
  }

  componentWillUnmount() {
    document.body.removeEventListener('mousedown', this.handleMouseDown);
    this.observer.disconnect();
    const container = this.ref.current!.closest('.spread-sheet')!;
    this.observer.observe(container);
  }

  render() {
    //console.log('dataSheet render ');
    const { rows, cells, columns, loadableRowFlags, loadableColumnFlags } = this.props;
    return (
      <table className="spread-sheet-table" ref={this.ref} onDoubleClick={this.handleDoubleClick}>
        <ColGroup loadableColumnFlags={loadableColumnFlags} />
        <tbody>
          <RowGroup
            rows={rows}
            cells={cells}
            loadableRowFlags={loadableRowFlags}
            loadableColumnFlags={loadableColumnFlags}
          />
        </tbody>
      </table>
    );
  }
}

/*
const mapState = (state: IStoreState, props: Props): MappedProps => ({
  selectionType: state.selectionType,
  selectionCellRange: state.selectedCellRange,
  mode: state.mode,
});
*/
const mapState = (state: IStoreState) => ({});

const actions = (store: Store<IStoreState>) => ({
  setDimensions: (
    state: IStoreState,
    rowDimensions: IRowDimensions,
    colDimensions: IColDimensions,
  ) => {
    return setDimensions(store.getState(), rowDimensions, colDimensions);
  },
  setContainerDimensions: (
    state: IStoreState,
    containerDimensions: IStoreState['containerBox'],
  ) => {
    return setContainerBox(store.getState(), containerDimensions);
  },
  tryToggleCollapse: (state: IStoreState, cellId: ID) => {
    return tryToggleCollapse(state, cellId);
  },
});

export default connect<Props, any, IStoreState, any>(
  mapState,
  actions,
)(DataSheet);
