import {TextSelection} from 'prosemirror-state';
import {selectedRect} from '../commands';
import {findParentNodeOfType} from 'prosemirror-utils';
import {switchRows} from './commands';
import {createElementWithClass} from '../util';

/**
 * RowDragHandler is controlling columns dragging behavior in tables.
 *
 * - onmousedown - create row ghost and add event listeners for mousemove and mouseup.
 * - mousemove - append ghost to table on first move, and update ghost position on every move.
 * - mouseup - execute drop behavior - delete origin and drop row in new position.
 */

export class RowDragHandler {
  constructor(view, handle, body, getPos, cellDom) {
    this.view = view;
    this.body = body;
    this.getPos = getPos;
    this.cellDom = cellDom;
    this.handle = handle;

    this.tableWrapper = null;
    this.trGhost = null;

    this.firstMove = true;
    this.originCellPos = null;

    this.handle.addEventListener('mousedown', (e) => this.onmousedown(e));
  }

  onmousedown(e) {
    if (!this.view.editable) return;
    const trRect = this.cellDom.parentElement.getBoundingClientRect();

    const trGhost = createElementWithClass('div', 'tableRowGhost');
    trGhost.style.position = 'absolute';
    trGhost.style.width = `${trRect.width}px`;
    trGhost.style.height = `${trRect.height}px`;
    trGhost.style.background = '#0000ff2e';
    trGhost.style.opacity = 0.5;
    trGhost.style.pointerEvents = 'none';

    this.trGhost = trGhost;

    const originMousePos = this.view.posAtCoords({
      left: e.clientX + 20,
      top: e.clientY
    });

    this.originCellPos = originMousePos.inside;

    this.firstMove = true;

    this.body.onmousemove = (e) => this.onmousemove(e, trRect);
    this.body.onmouseup = (e) => this.onmouseup(e);

    // Stop the editor from making selection
    e.preventDefault();
  }

  onmousemove(e, trRect) {
    this.tableWrapper = document.querySelector('.tableFocus').firstChild;
    const tableRect = this.tableWrapper
      .querySelector('table')
      .getBoundingClientRect();

    // append ghost to dom and add class only in the fist move in each drag
    if (this.firstMove) {
      this.tableWrapper.appendChild(this.trGhost);
      this.tableWrapper.parentElement.classList.add('rowRearrangement');
      this.firstMove = false;
    }
    const middleTr = trRect.height / 2;
    let trGhostTop = e.clientY - tableRect.top - middleTr;
    if (e.clientY - middleTr < tableRect.top) {
      trGhostTop = 0;
    }
    if (e.clientY + middleTr > tableRect.bottom) {
      trGhostTop = tableRect.height - trRect.height;
    }
    this.trGhost.style.top = `${trGhostTop}px`;
  }

  onmouseup(e) {
    this.trGhost.remove();
    if (this.tableWrapper)
      this.tableWrapper.parentElement.classList.remove('rowRearrangement');
    this.body.onmousemove = this.body.onmouseup = null;

    const state = this.view.state;

    const mousePos = this.view.posAtCoords({
      left: e.clientX + 20,
      top: e.clientY
    });
    if (!mousePos) return;

    const rect = selectedRect(state);

    let originCellIndex = rect.map.map.indexOf(
      this.originCellPos - rect.tableStart
    );

    if (originCellIndex === -1) {
      originCellIndex = rect.map.map.indexOf(
        this.originCellPos - 1 - rect.tableStart
      );
    }

    const {pos: insertRowPos} =
      findParentNodeOfType(state.schema.nodes.table_cell)(
        TextSelection.create(state.doc, mousePos.pos)
      ) || mousePos.inside;

    const insertCellIndex = rect.map.map.indexOf(
      insertRowPos - rect.tableStart
    );

    if (
      originCellIndex === -1 ||
      insertCellIndex === -1 ||
      originCellIndex === insertCellIndex
    )
      return;

    const originRowNumber = originCellIndex / rect.map.width;
    let insertRowNumber = insertCellIndex / rect.map.width;
    // Avoid drag above the header
    insertRowNumber = insertRowNumber > 0 ? insertRowNumber : 1;

    if (insertRowNumber === originRowNumber) {
      // Case when after avoid switch header try to drag second row above first
      return;
    }

    switchRows(
      this.view,
      rect,
      originRowNumber,
      insertRowNumber,
      this.getPos()
    );
  }
}
