import "./styles/DraggableTree.css";

import { Tree } from "antd";
import React from "react";

import { noop } from "../services/utils";

interface IProps {
  checkable?: boolean;
  data?: IStructureItem[];
  onUpdate?: any;
  onCheck?: any;
  onSelect?: any;
  onDropCheck?: any;
  checkedKeys?: string[];
  checkStrictly?: boolean;
  draggable?: boolean;
  noRootParent?: boolean;
}

interface IState {
  gData: any;
  expandedKeys: any;
}

class DraggableTree extends React.Component<IProps, IState> {
  constructor(props) {
    super(props);

    this.state = {
      gData: props.data,
      expandedKeys: [],
    };
  }

  onDragEnter = (info) => {};

  componentDidUpdate(nextProps) {
    const { data } = this.props;
    if (nextProps.data !== data) {
      if (data) {
        this.setState({ gData: data });
      }
    }
  }

  onDrop = async (info) => {
    const dropKey = info.node.props.eventKey;
    const dragKey = info.dragNode.props.eventKey;
    const dropPos = info.node.props.pos.split("-");
    const dropPosition =
      info.dropPosition - Number(dropPos[dropPos.length - 1]);

    /**
     * Don't allow root level drop
     */
    if (dropPos.length === 2 && !this.props.noRootParent) {
      return;
    }

    const loop = (data, key, callback) => {
      data.forEach((item, index, arr) => {
        if (item.key === key) {
          return callback(item, index, arr);
        }
        if (item.children) {
          return loop(item.children, key, callback);
        }
      });
    };
    const data = [...this.state.gData];

    let dragObj;
    loop(data, dragKey, (item, index, arr) => {
      arr.splice(index, 1);
      dragObj = item;
    });

    if (!info.dropToGap) {
      loop(data, dropKey, (item) => {
        item.children = item.children || [];
        item.children.push(dragObj);
      });
    } else if (
      (info.node.props.children || []).length > 0 &&
      info.node.props.expanded &&
      dropPosition === 1
    ) {
      loop(data, dropKey, (item) => {
        item.children = item.children || [];
        item.children.unshift(dragObj);
      });
    } else {
      let ar;
      let i;
      loop(data, dropKey, (item, index, arr) => {
        ar = arr;
        i = index;
      });
      if (dropPosition === -1) {
        ar.splice(i, 0, dragObj);
      } else {
        ar.splice(i + 1, 0, dragObj);
      }
    }

    const dropCheck =
      typeof this.props.onDropCheck === "function" &&
      (await this.props.onDropCheck(data, dragKey));

    /**
     * dropCheck true = fail
     */
    if (dropCheck) {
      return;
    } else {
      this.setState(
        {
          gData: data,
        },
        this.props.onUpdate && this.props.onUpdate(data, dragKey)
      );
    }
  };

  render() {
    const {
      onCheck = noop,
      onSelect = noop,
      checkable,
      checkedKeys = [],
      checkStrictly,
    } = this.props;

    return (
      <div className="draggable-tree">
        <Tree
          className="draggable-tree"
          defaultExpandAll
          draggable={this.props.draggable ?? true}
          blockNode
          onDragEnter={this.onDragEnter}
          onDrop={this.onDrop}
          treeData={this.state.gData}
          onSelect={onSelect}
          onCheck={(chk: any) => {
            const checked = checkStrictly ? chk.checked : chk;
            onCheck(checked);
          }}
          showLine
          checkable={checkable}
          checkedKeys={checkedKeys}
          selectedKeys={[]}
          checkStrictly={checkStrictly}
        />
      </div>
    );
  }
}

export default DraggableTree;