import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { utils } from 'amos-tool';
import { Modal, Input, message, PureRenderMixin, AmosAlert } from 'amos-framework';
import { tools, stopEvent, graph, handlers, domTools } from 'amos-jsplumb';
import { PubSub } from 'ray-eventpool';
import { browserHistory } from 'amos-react-router';
import { initStageView, saveStageView, addViewTrigger, delViewNode, addViewNode, addViewLink } from './../../../services/ruleDesignService';
import StageHeader from './header/StageHeader';
import RuleScene from './RuleScenePlumb';
import EleCatalogPanel from './extra/EleCatalogPanel';
import {
  ELE_ADD_TOPIC,
  ELE_DEL_TOPIC,
  ELE_CATALOG_TOPIC,
  ELE_ENTRY_TOPIC,
  locale,
  isStatusDragLink,
  transferDropData,
  isDragEle,
  typeValue
} from './modelConf/stageConf';
import { commonAddNode, addTrigerAndAction, commonAddNodeAndConn, getEleNode } from './addNode';

const extraSize = {
  top: 90,
  left: 230
};

/**
 * 舞台
 *
 * @class RuleStage
 * @extends {Component}
 */
class RuleStage extends Component {
  static propTypes = {
    objId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    needReloadStage: PropTypes.bool,
    toggleReloadStage: PropTypes.func,
    onStatusConnLink: PropTypes.func
  };

  constructor(props) {
    super(props);
    this.state = {
      connections: [],
      nodes: [],
      zoom: 1,
      visible: false,
      catalogVisible: false,
      eleType: '',
      statusId: '',
      position: {
        left: 0,
        top: 0
      },
      ruleSubObj: {},
      needDestroy: false,
      catalogOptions: {},
      subGraphVisible: false,
      subGraphOptions: {}
    };
  }

  componentWillMount() {}
  onAddConnection = (connId, sourceId, targetId) => {
    const newConn = {
      id: connId,
      sourceId,
      targetId
    };
    this.setState({
      connections: [...this.state.connections, newConn]
    });
  };
  onRemoveConnection = (connectionId, sourceId) => {
    this.setState({
      connections: this.state.connections.filter(connection => connection.id !== connectionId)
    });
  };
  onZoom = zoom => {
    this.setState({
      zoom
    });
  };
  changeZoom = zoomIn => {
    let zoom = this.props.zoom;
    if (zoomIn) {
      zoom += 0.2;
    } else {
      zoom -= 0.2;
    }
    this.props.onZoom(zoom);
  };
  onRemoveNode = nodeId => {
    const { nodes } = this.state;
    const newNodes = nodes.filter(n => n.id !== nodeId);
    this.setState({
      nodes: newNodes
    });
  };

  componentDidMount() {
    this.initData();

    // 添加元素
    this.pubsub_eleadd = PubSub.subscribe(ELE_ADD_TOPIC.eleAdd, (topic, { eleType, statusId, position }) => {
      if (!utils.isEmpty(eleType)) {
        this.setState({
          visible: true,
          eleType,
          statusId,
          position
        });
      }
    });

    // 删除元素
    this.pubsub_eledel = PubSub.subscribe(ELE_DEL_TOPIC.eleDel, (topic, { eleId }) => {
      if (!utils.isEmpty(eleId)) {
        // 删除
        delViewNode(eleId).then(
          data => {
            message.success('删除成功！');
            // 刷新
            this.setState(
              {
                needDestroy: true
              },
              () => this.reloadPlumb()
            );
          },
          err => {
            message.danger(err || '删除失败');
          }
        );
      }
    });

    // 子操作
    this.pubsub_catalog = PubSub.subscribe(ELE_CATALOG_TOPIC.NORMAL, (topic, { catalogOptions, catalogVisible }) => {
      if (!utils.isEmpty(catalogOptions)) {
        this.setState({
          catalogVisible,
          catalogOptions
        });
      }
    });

    // 子视图
    this.pubsub_subgraph = PubSub.subscribe(ELE_ENTRY_TOPIC.eleEntry, (topic, { subGraphOptions, subGraphVisible }) => {
      if (!utils.isEmpty(subGraphOptions)) {
        this.setState({
          subGraphOptions,
          subGraphVisible
        });
      }
    });
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.needReloadStage) {
      // 设置已reload
      this.props.toggleReloadStage(false);
      this.setState(
        {
          needDestroy: true
        },
        () => this.reloadPlumb()
      );
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    return PureRenderMixin.shouldComponentUpdate.apply(this, nextProps, nextState);
  }

  componentWillUnmount() {
    PubSub.unsubscribe(this.pubsub_eleadd);
    PubSub.unsubscribe(this.pubsub_eledel);
    PubSub.unsubscribe(this.pubsub_catalog);
    PubSub.unsubscribe(this.pubsub_subgraph);
  }

  onChange = ({ nodes, connections }) => {
    this.setState({ nodes, connections });
  };

  onSceneCreated = plumbInstance => {
    this.plumbInstance = plumbInstance;
  };

  onSceneClick = e => {
    const catalogOptions = {
      eleType: '',
      sourceId: '',
      position: { left: 0, top: 0 },
      catalogs: []
    };
    this.setState({
      catalogVisible: false,
      catalogOptions
    });
  };

  onSubGraphClosed = () => {
    this.setState({
      subGraphVisible: false,
      subGraphOptions: {}
    });
  };

  /**
   * 双击连线
   */
  onConnectionDbClick = (conn, e) => {
    // 判断仅是 状态之间的连线
    console.log(conn, e, conn.getData());
    let { source, target } = conn;
    if (isStatusDragLink(target)) {
      this.props.onStatusConnLink(source, target, conn.getData());
    }
  };

  initData = () => {
    const { objId } = this.props;
    if (objId) {
      initStageView(objId).then(data => {
        this.setState({
          connections: data.linkVoList,
          nodes: data.nodeVoList,
          ruleSubObj: data.rule
        });
      });
    }
  };

  reloadPlumb = () => {
    const { objId } = this.props;
    if (objId) {
      initStageView(objId).then(data => {
        this.setState({
          needDestroy: false,
          connections: data.linkVoList,
          nodes: data.nodeVoList
        });
      });
    }
  };

  filterNodes = nodes => {
    const oldNodes = this.state.nodes;
    const newNodes = nodes.filter(n => !oldNodes.find(on => on.id === n.id));
    return [...oldNodes, ...newNodes];
  };

  filterConns = conns => {
    const oldConns = this.state.connections;
    const newConns = conns.filter(c => !oldConns.find(oc => oc.sourceId === c.sourceId && oc.targetId === c.targetId));

    return [...oldConns, ...newConns];
  };

  saveStage = objId => {
    const { nodes, connections } = this.state;
    // 获取布局
    graph.completePosition(this.plumbInstance, nodes);
    saveStageView({
      id: objId,
      nodeVoList: nodes,
      linkVoList: connections
    }).then(
      data => {
        message.success('保存成功！');
      },
      err => {
        message.danger(err || '保存失败!');
      }
    );
  };

  exitStage = () => {
    browserHistory.goBack();
  };

  addNode = ({ labelValue }) => {
    const { nodes, connections, position, statusId } = this.state;

    const result = addTrigerAndAction({ labelValue, position, statusId });
    // 持久化
    addViewTrigger(result);

    this.setState({
      nodes: [...nodes, ...result.node],
      connections: [...connections, ...result.conn],
      visible: false,
      eleType: '',
      statusId: ''
    });
  };

  cancel = modal => {
    this.setState({
      visible: false
    });
  };

  /**
   * 拖拽结束事件
   * @memberOf RuleStage
   */
  handleDrop = event => {
    stopEvent(event);
    const { left: x, top: y } = domTools.getNodeOffset(event);
    const data = event.dataTransfer.getData('FlowChartNode');
    const ruleNodeId = data.split('-')[1];
    const label = data.split('-')[2];
    const { nodeType, sourceId } = transferDropData(data.split('-')[0]);
    if (isDragEle(nodeType)) {
      if (sourceId) {
        this.doDropAddNodeAndConn({ nodeType, x, y, sourceId });
      } else {
        this.doDropAddNode({ nodeType, x, y, ruleNodeId, label });
      }
    } else {
      const target = event.target;
      if (target.className !== 'plumb-charts') {
        const ele = getEleNode(target);
        if (ele) {
          this.doDropAddConn(sourceId, ele.id);
        }
      }
    }
  };

  doDropAddNode = ({ nodeType: eleType, x, y, ruleNodeId, label }) => {
    const { nodes } = this.state;
    const { objId } = this.props;
    const labelValue = label || '节点';
    const position = {
      left: x,
      top: y
    };
    const result = commonAddNode({ eleType, labelValue, position, ruleNodeId });
    // 持久化
    Object.assign(result, { ruleId: objId });
    addViewNode(result);
    this.setState({
      catalogVisible: false,
      nodes: [...nodes, result]
    });
  };

  doDropAddNodeAndConn = ({ nodeType: eleType, x, y, sourceId }) => {
    const { nodes, connections } = this.state;
    const { objId } = this.props;
    const labelValue = locale[eleType] || '节点';
    const position = {
      left: x,
      top: y
    };
    const result = commonAddNodeAndConn({ eleType, labelValue, position, sourceId });
    // 持久化
    addViewNode({
      ...result.node,
      subObjId: objId,
      sourceId
    });

    this.setState({
      catalogVisible: false,
      nodes: [...nodes, result.node],
      connections: [...connections, result.conn]
    });
  };

  doDropAddConn = (sourceId, targetId) => {
    const { objId } = this.props;

    // 持久化
    addViewLink({
      oid: objId,
      sourceId,
      targetId
    }).then(
      data => {
        // 刷新
        this.setState(
          {
            needDestroy: true,
            catalogVisible: false
          },
          () => this.reloadPlumb()
        );
      },
      err => {
        message.danger(err || '添加链路失败！');
      }
    );
  };

  /**
   * 准备添加元素,用于填写必要字段
   */
  doPreAddEle = ({ type, x, y }) => {
    if (!utils.isEmpty(type)) {
      this.setState({
        visible: true,
        eleType: type,
        position: {
          left: x,
          top: y
        }
      });
    }
  };
  //检查是否已经建立过连接
  beforeDrop = info => {
    info.targetId = info.dropEndpoint.elementId;
    if (info.targetId === info.sourceId) {
      return;
    } else {
      let connections = this.jspInstance.getConnections({ source: info.sourceId, target: info.targetId });
      if (!connections.length === 0) {
        return;
      }
    }
  };
  updateGraph = (args = {}) => {
    this.setState({
      ...args
    });
  };

  updateGraphWithDestroy = (args = {}) => {
    this.setState(
      {
        destroy: true
      },
      () => {
        this.setState({
          ...args
        });
      }
    );
  };
  onDropHandler = (id, left, top) => {
    const { nodes } = this.state;
    nodes.map(node => {
      if (node.id === id) {
        let location = { left: left, top: top };
        node.config.style = location;
      }
    });
    this.setState({
      nodes: nodes
    });
  };
  render() {
    const { zoom, nodes, connections, catalogOptions, catalogVisible, ruleSubObj, needDestroy } = this.state;

    const dataSource = {
      nodes,
      connections
    };
    const nodeProps = {
      onDrop: this.onDropHandler
    };
    const { objId, ...restProps } = this.props;
    const newRestProps = tools.omit(restProps, ['updateGraph', 'updateGraphWithDestroy']);
    const eventListeners = {
      dblclick: this.onConnectionDbClick,
      connection: handlers.onConnectionEventHandler,
      beforeDrop: this.beforeDrop
    };

    return (
      <div className="rule-stage">
        <StageHeader saveStage={this.saveStage} exitStage={this.exitStage} ruleSubObj={ruleSubObj} objId={objId} />
        <RuleScene
          handleDrop={this.handleDrop}
          onAddConnection={this.onAddConnection}
          onRemoveConnection={this.onRemoveConnection}
          onRemoveNode={this.onRemoveNode}
          dataSource={dataSource}
          newRestProps={newRestProps}
          onPlumbChange={this.onChange}
          nodeProps={nodeProps}
          onSceneCreated={this.onSceneCreated}
          onPlumbClick={this.onSceneClick}
          eventListeners={eventListeners}
          zoom={zoom}
          onZoom={this.onZoom}
          destroy={needDestroy}
        />
        <EleCatalogPanel visible={catalogVisible} {...catalogOptions} />
      </div>
    );
  }
}

export default RuleStage;
