import React from "react";
import { Addon } from '@antv/x6';
import CraftOtype from '../script/craftOtype';
import GxCraft from "../model/craft/GxCraft";
import GxProcedure from "../model/craft/GxProcedure";
import GraphNode from "../CraftView/GraphNode";
import eventBus from '../script/event';
import EditProcessor from './EditProcessor'
import { Icon, Tooltip, Button } from 'antd';
import aliIcon from "../script/iconfont.js";
const { Dnd } = Addon;
const IconFont = Icon.createFromIconfontCN({
    scriptUrl: aliIcon,
  });
class CraftEdit extends React.Component {
    dxy = {dx:0,dy:0};
    constructor(){
        super();
        this.state = {
            createFlag: false, // 创建/取消
            showPointFlag: false,  // 展示连线圆圈
            redoLoading: false,
            undoLoading: false,
            isDrop:false,
            creactOtype:{},
            oldLoc:{x:0,y:0},
            canRedo: false,
            canUndo: false,
            undoStack:[], // 撤销操作
            redoStack:[],
        }
    }
    componentWillUnmount(){
        if( this.graph ){
            this.graph.cleanHistory();
            this.graph.clearCells();
            this.setState({
                redoStack:[],
                undoStack:[]
            })
        };
        eventBus.off('nodeMenuHandle');
        eventBus.off('linkMenuHandle')
    }
    componentDidMount() {
        this.graph = this.props.craftview.graph;
        if( !this.graph ) return ;
        this.initDnd(this.graph);
        this.initEvent(); // 初始化操作
        this.initEventBus(); // 初始化自定义监听事件
        this.getEditProcessor(); // 注册 撤销/重做
    }
    initEvent = () => {
        this.history = this.graph.history;
        this.history.on('change', () => {
            this.setState({
                canRedo: this.history.canRedo(),
                canUndo: this.history.canUndo(),
            })
        })
        // 移入/移除展示连线桩 
        // node
        this.graph.on('node:mouseenter',({cell})=>{
            this.showPorts(true);
        })
        this.graph.on('node:mouseleave',({cell})=>{
            this.showPorts(false);
        })
        // 快捷键删除
        // 删除工序/连线
        this.graph.bindKey('backspace', (ev) => {
            this.removeCells();
        })
        this.graph.bindKey('delete', (ev) => {
            this.removeCells();
        })
        // 记录工序之前位置
        this.graph.on('node:mousedown',(ev) => {
            const { node } = ev;
            let position = node.position();
            let oldLoc = {x:position.x,y:position.y}
            this.setState({oldLoc}); 
        })
        this.graph.on('node:mouseup',(ev) => {
            let oldLoc = {x:0,y:0};
            setTimeout(()=>{
                this.setState({oldLoc});
            },100)
        })
        // 更新工序位置
        this.graph.on('node:moved',(ev) => {
            const { node, x, y } = ev;
            let currentNode = this.getItem(node.id);
            currentNode.x = x;
            currentNode.y = y;
            const { oldLoc } = this.state;
            if(this.judgmentGland(currentNode)){
                this.graph.undoAndCancel();  //撤销，并且不添加到重做队列中，所以这个被撤销的命令不能被重做。
                if( oldLoc.x ){
                    node.position(oldLoc.x,oldLoc.y)
                }
            }else{
                // let oldProcedure = Object.assign({},currentNode,{form:oldLoc})
                // if( JSON.stringify(currentNode.form) !== JSON.stringify(oldProcedure.form) ){
                //     this.graph.editProcessor.addEditOp({type:'node:move',item:oldProcedure,children:[]})
                // }
                // this.graph.editProcessor.addEditOp({type:'node:move',item:currentNode,children:[]})
                // this.getHandleDisAble()
                this.updateProcedure(currentNode);
            }
        })
        // 连线
        this.graph.on('edge:change:target',(ev) => {
            const { showPointFlag } = this.state;
            if( showPointFlag ) return;
            this.showPorts(true);
        })
        this.graph.on('edge:change:source',(ev) => {
            const { showPointFlag } = this.state;
            if( showPointFlag ) return;
            this.showPorts(true);
        })
        this.graph.on('edge:added',(ev) => {
            const { edge } = ev;
            setTimeout(() => {
                this.creatLabelObject(edge);
            },100)
        })
        this.graph.on('edge:connected',(ev) => {
            const { edge } = ev;
            let sourcePos = this.getPointType(edge.getSourceNode(),edge.getSourcePortId());
            let targetPos = this.getPointType(edge.getTargetNode(),edge.getTargetPortId());
            // let currentEdge = this.getItem(currentCell.id,false)
            let source = edge.getSourceCellId();
            let target = edge.getTargetCellId();
            let lid = edge.id;
            let option = { source, sourcePos, target, targetPos, lid}
            let links = this.graph.getEdges();
            let currentLink = links[links.length - 1];
            this.creatLabelObject(currentLink);
            if( ev.isNew ){
                currentLink.appendLabel({
                    attrs: {
                        body:{
                            fill:'none',
                            ref: 'text',
                        },
                      },
                      position: {
                        distance: 0.5,
                        offset: {
                          x: 0,
                          y:0,
                        },
                    }
                })
                this.addLink(option,currentLink.id);
            }else{
                this.updateLink(option,currentLink.id);
            }
            this.showPorts(false);
            edge.attr('lines/stroke', 'transparent');
            edge.removeTools();
        })
        // 智能路由 (性能太差不用)
        // this.graph.on('node:change:position',({cell})=>{
        //     this.update()
        // })
    }
    
    creatLabelObject = (link) => {  // 添加配比值  edge
        let currentEdgeDom = this.graph.findView(link)?.labelCache[0];
        if( !currentEdgeDom ) return ;
        let _currentLink = link.getProp();
        let _multiplicity = _currentLink.multiplicity !== undefined?_currentLink.multiplicity:1;
        let tep = `<foreignObject width="20" height="16" x="-10" y="-10">
            <body xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%; background: transparent;">
                <div style="width: 100%; height: 100%;">
                    <div class="multiplicity-box mark-item-${_currentLink.id}">
                        <span id="multiplicity-item-${_currentLink.id}">${_multiplicity}</span>
                    </div>
                </div>
            </body>
            </foreignObject>`
        currentEdgeDom.innerHTML = tep;
        return currentEdgeDom;
    }
    judgmentGland = (procedure) => {  //是否压盖
        let flag = false;
        let nodes = this.graph.getNodes();
        let currentNode = nodes.find(el => el.id === procedure.id);
        if( procedure ){
            let t1,l1,r1,b1;
            if( currentNode ){
                let currentBox = currentNode.getBBox({ deep: true });
                t1 = currentBox.top;
                l1 = currentBox.left;
                r1 = currentBox.right;
                b1 = currentBox.bottom;
            }else{
                /** 新创建的 */
                let width = new GraphNode(procedure).width;
                let height = new GraphNode(procedure).height;
                t1 = procedure.form.y;
                l1 = procedure.form.x;
                r1 = procedure.form.x + width;
                b1 = procedure.form.y + height;
                if( CraftOtype.isCraft(procedure?.procedure) ){
                    l1 -= width-120;
                    r1 += width+50;
                }
            }
            let filterNodes = nodes.filter(el => el.id !== procedure.id);
            for (let i = 0; i < filterNodes.length; i++) {
                const element = filterNodes[i].getBBox({ deep: true });
                let t2 = element.top;
                let l2 = element.left;
                let r2 = element.right;
                let b2 = element.bottom;
                if( b1<t2 || l1>r2 || t1>b2 || r1<l2 ){
                    flag = false;
                }else{
                    flag = true;
                    return flag;
                }
            }
        }
        return flag;
    }
    creatLabel = (item) => {
        const btn = document.createElement('div');
        let _multiplicity = item.multiplicity !== undefined?item.multiplicity:1;
        btn.className = `multiplicity-box mark-item-${item.id}`;
        btn.innerHTML = `<span id="multiplicity-item-${item.id}">${_multiplicity}</span>`;
        btn.addEventListener('dblclick', () => {
            const { clickMultiplicity } = this.props;
            if( typeof clickMultiplicity == 'function' ){
                clickMultiplicity(item)
            }
        })
        return btn;
    }
    initEventBus = () => {
        // 自定义点击事件
        // node右键操作
        eventBus.on('nodeMenuHandle',obj => {
            if( obj.key === "2" ){
                let cells = this.graph.getNodes();
                let currentCell = cells.find(el => el.id == obj?.item?.id);
                if( !currentCell ) return;
                // console.log(this.graph);
                this.removeProcedure([currentCell]);
            }else{
                // 编辑
                const { editorProcedure } = this.props;
                // console.log('编辑工序');
                if( typeof editorProcedure == 'function' ){
                    editorProcedure(obj);
                }
            }
        })
        // link右键操作
        eventBus.on('linkMenuHandle',obj => {
            if( obj.key === "1" ){
                let cells = this.graph.getEdges();
                let currentCell = cells.find(el => el.id == (obj?.item?.id || obj?.item?.lid));
                if( !currentCell ) return;    
                this.removeLink([currentCell]);
            }else{
                // 配置值
                const { editorMultiplicity } = this.props;
                if( typeof editorMultiplicity == 'function' ){
                    editorMultiplicity(obj);
                }
            }
        })
    }
    update = () => { // 智能更新线
        let edges = this.graph.getEdges();
        edges.forEach(el => {
            const edgeView = this.graph.findViewByCell(el);
            edgeView.update();
        })
    }
    showPorts = (show) =>{
        this.setState({showPointFlag:show});
        const ports = this.graph.container.querySelectorAll('.x6-port-body');
        for (let i = 0, len = ports.length; i < len; i = i + 1) {
            ports[i].style.visibility = show ? 'visible' : 'hidden'
        }
    }
    getPointType = (node,point) => {  // 获取连接点
        const { ports } = node.port;
        let pointType = ports.find(el => el.id == point);
        return pointType?pointType.mark:1;
    }
    removeCells = () => {  // 快捷键删除操作
        let cells = this.graph.getSelectedCells();
        if( cells.length ){
            if( cells[0].isNode() ){
                this.removeProcedure(cells);
            }
            if( cells[0].isEdge() ){
                this.removeLink(cells);
            }
        }
    }
    addProcedure = (procedure) => {  // 添加工序
        this.props.craftview.addNode(procedure);
        this.getHandleDisAble();
    }
    removeProcedure = (procedure) => { // 删除工序
        if (procedure.length) {
            const { removeProcedure } = this.props;
            let currentNode = this.getItem(procedure[0].id);
            this.getHandleDisAble()
            this.graph.removeCells(procedure);
            if( typeof removeProcedure == 'function' ){
                removeProcedure(currentNode)
            }
        }
    }
    updateProcedure = (procedure) => {  // 更新工序位置 
        const { updateProcedure } = this.props;
        if( typeof updateProcedure == 'function' ){
            updateProcedure(procedure);
        }
    }
    getNodeLinks = (node) => {
        let links = this.graph.getEdges();
        let cells = [];
        let beforLink = this.graph.getIncomingEdges(node)||[];
        let afterLink = this.graph.getOutgoingEdges(node)||[];
        if(beforLink.length || afterLink.length){
            let _cells = [].concat(beforLink,afterLink);
            for (let i = 0; i < _cells.length; i++) {
                const link = _cells[i];
                const currentLink = links.find(el => el.id === link.id)
                if( currentLink ){
                    cells.push(currentLink)
                }
            } 
        }
        return cells;

    }
    addLink = (link) => {  // 添加连线
        const { addLink } = this.props;
        this.getHandleDisAble()
        if( typeof addLink == 'function' ){
            addLink(link);
        }
    }
    updateLink = ( link ) => {  // 更新连线
        const { updateLink } = this.props;
        if( typeof updateLink == 'function' ){
            updateLink(link);
        }
    }   
    removeLink = (link) => {  // 删除连线
        if (link.length) {
            const { removeLink } = this.props;
            let currentLink = this.getItem(link[0].id,false);
            this.getHandleDisAble()
            this.graph.removeCells(link);
            if( typeof removeLink == 'function' ){
                removeLink(link);
            }
        }
    }
    getNodes = () => {  // 获取所有节点
        let nodes = [];
        let graphNodes = this.graph.getNodes();
        if( graphNodes.length ){
            for (let i = 0; i < graphNodes.length; i++) {
                const data = graphNodes[i].getProp();
                let current = Object.assign({},data.procedure,{form:data.position})
                nodes.push(current);
            }
        }
        return nodes;
    }
    getLinks = () => {  // 获取所有线
        let links = [];
        let edges = this.graph.getEdges();
        if( edges.length ){
            for (let i = 0; i < edges.length; i++) {
                const data = edges[i].getProp();
                let current = Object.assign({},data.link)
                links.push(current);
            }
        }
        return links;
    }
    getItem = (id,type=true) => {
        let nodes = this.getNodes();
        let edges = this.getLinks();
        if( type ){
            let crrentNode = nodes.find(el => el.id == id);
            if(crrentNode) return crrentNode;
        }else{
            let crrentEdges = edges.find(el => el.id == id);
            if(crrentEdges) return crrentEdges;
        }
    }
    loadData = (procedure) => {
        const { dataInfo } = this.props;
        let craft = new GxCraft(dataInfo);
        if( procedure ){
            craft.dag.addProcedure(procedure);
        }
        this.props.craftview.loadCraft(craft);
    }
    loadCraft=()=>{
        const { dataInfo } = this.props;
        const craft = new GxCraft(dataInfo);
        this.props.craftview.loadCraft(craft);
    }
    getEditProcessor() {  // 撤销/重做
        this.history = this.graph.history;
        this.history.on('change', () => {
            this.setState({
                canRedo: this.graph.history.canRedo(),
                canUndo: this.graph.history.canUndo(),
            })
        })
    }
    commonFn = (data)=> {
        let nodes = this.getNodes();
        let links = this.getLinks();
        let craft = {dag:{nodes,links}}
        return craft;
    }
    undo=()=>{
        this.history.undo();
        this.setState({undoLoading:true})
        const { undoFn } = this.props;
        if( typeof undoFn == 'function' ){
            undoFn(this.history);
        }
        setTimeout(() => {
            this.setState({undoLoading:false})
        },500)
    }
    redo=()=>{
        this.history.redo();
        this.setState({redoLoading:true})
        const { redoFn } = this.props;
        if( typeof redoFn == 'function' ){
            redoFn(this.history);
        }
        setTimeout(() => {
            this.setState({redoLoading:false})
        },500)
    }
    addX6Node = (node) => {
        let procedure = (node && typeof node.getProp =='function')?node.getProp():node;
        this.props.craftview.addNode(procedure?.procedure||procedure);
        const { handleAddProcedure } = this.props
        if( typeof handleAddProcedure == 'function' ){
            handleAddProcedure(node)
        }
    }
    removeX6Node = (node) => {
        this.graph.removeCell(node);
        const { handleRemoveProcedure } = this.props
        if( typeof handleRemoveProcedure == 'function' ){
            handleRemoveProcedure(node)
        }
    }
    moveX6Node = (node) => {
        let nodes = this.graph.getNodes();
        let currentNode = nodes.find(el => el.id == node.id);
        if( currentNode ){
            currentNode.position(node.form.x,node.form.y)
            const { handleMoveProcedure } = this.props
            if( typeof handleMoveProcedure == 'function' ){
                handleMoveProcedure(node)
            }
        }else{
            console.log('找不到相应节点');
        }
    }
    addX6Link = (edge) => {
        let link = (edge && typeof edge.getProp =='function')?edge.getProp().link:edge;
        if( link ){
            this.props.craftview.addlink(link);
        }else{
            this.graph.addCell(edge)
        }
        const { handleAddLink } = this.props
        if( typeof handleAddLink == 'function' ){
            handleAddLink(edge)
        }
    }
    removeX6Link = (edge) => {
        let links = this.graph.getEdges();
        let currentEdge = edge.lid?links.find(el => el.id === edge.lid):edge;
        this.graph.removeCell(currentEdge);
        const { handleRemoveLink } = this.props
        if( typeof handleRemoveLink == 'function' ){
            handleRemoveLink(currentEdge);
        }
    }
    changeView = (op,flag) => {
        if( op ){
            if( op.type === "node:add" ){
                this.handleFn(op.item,1,flag?2:3);
            }else if( op.type === "node:remove" ){
                this.handleFn(op.item,1,flag?3:2);
            }else if( op.type === "node:move" ){
                this.handleFn(op.item,1,1);
            }else if(op.type === "edge:add"){
                this.handleFn(op.item,0,flag?2:3);
            }else if(op.type === "edge:remove"){
                this.handleFn(op.item,0,flag?3:2);
            }
            if( op.children && op.children.length ){ // 处理线
                for (let i = 0; i < op.children.length; i++) {
                    const element = op.children[i];
                    if( op.type === "node:remove" ){
                        this.handleFn(element,0,flag?3:2);
                    }else if( op.type === "node:add" ){
                        this.handleFn(element,0,flag?2:3);
                    }
                }
            }
        }
    }
    handleFn = (item,type,opType)=>{  // type(0:link,1:node) 类型 opType (1为更新，2为删除，3为增加) 操作
        switch (opType) {
            case 1:
                this.moveX6Node(item);
                break;
            case 2:
                type?this.removeX6Node(item):this.removeX6Link(item);
                break;
            case 3:
                type?this.addX6Node(item):this.addX6Link(item);
                break;
            default:
                console.log('未知操作');
                break;
        }
        this.props.craftview.unSelectHighlinghtNode();  // 取消选中
        this.getHandleDisAble();
    }
    getHandleDisAble = () => {
        // this.setState({
        //     canRedo: this.graph.editProcessor.canRedo(),
        //     canUndo: this.graph.editProcessor.canUndo(),
        // })
    }
    changePointer = (className) => {
        let el = document.getElementsByClassName(`${className}`)[0];
        if( el ){
            el.addEventListener('mousemove',(e) => {
                const { creactOtype } = this.state;
                let procedure = Object.assign({},new GraphNode(creactOtype));
                let form = {
                    x: this.props.craftview.graph.clientToLocal(e.clientX,e.clientY).x - procedure.width/2,
                    y: this.props.craftview.graph.clientToLocal(e.clientX,e.clientY).y - procedure.height/2
                }
                procedure.form = form;
                if( this.judgmentGland(procedure) ){
                    el.style.cursor = 'no-drop';
                }else{
                    el.style.cursor = 'move'; 
                }
            })
        }
    }
    mouseUpNode = (e,item) => {
        this.setState({isDrop:true,creactOtype:item})
    }
    initDnd = (graph) => {
        this.dnd = new Dnd({
            target: graph,
            scaled: false,
            animation: false,
            getDragNode: (node) => node.clone({ keepId: true }),
            getDropNode: (node) => node.clone({ keepId: true }),
            validateNode: (droppingNode,options) => {
                const { createFlag } = this.state;
                if( createFlag ){
                    const item = droppingNode.getProp();
                    let procedure = Object.assign({},item.procedure);
                    procedure.form = item.position;
                    // this.loadData(procedure);
                    if( this.judgmentGland(procedure) ){
                        this.setState({isDrop:false,creactOtype:null})
                        console.log('创建失败:原因重叠');
                    }else{
                        // this.props.craftview.addNode(procedure);
                        // this.props.craftview.selectHighlinghtNode(procedure.id)
                        this.setState({isDrop:false,creactOtype:null})
                        // 添加节点 
                        // this.addProcedure(procedure)
                        const { createProcedure } = this.props;
                        if( typeof createProcedure == 'function' ){
                            createProcedure(procedure);
                        }
                    }
                }else{
                    this.setState({isDrop:false,createFlag:false,creactOtype:null})
                    const { cancelCreateProcedure } = this.props;
                    if( typeof cancelCreateProcedure == 'function' ){
                        cancelCreateProcedure(procedure);
                    }
                    return false;
                }
                return false;
            }
        })
    }
    startDrag = (e,item) => {  // 拖拽创建
        const { creactOtype } = this.state;
        const id = new Date().getTime();
        let _otid = (creactOtype&&creactOtype?.type)?creactOtype?.type?.otid:(creactOtype?.id);
        const procedure = new GxProcedure({id:id,name:creactOtype?.name,type:{otid:_otid}});
        const node = this.props.craftview.graph.createNode(new GraphNode(procedure,true));
        const { isDrop } = this.state;
        window.removeEventListener('mousedown',this.addEventListenerMousedown);
        if( isDrop ){
            this.dnd.start(node, e.nativeEvent)
            this.setState({creactOtype:procedure},()=>{
                this.changePointer('x6-widget-dnd');
                window.addEventListener('mousedown',this.addEventListenerMousedown);
            })
        }
    }
    addEventListenerMousedown = (e) =>{
        let value = e?.button || 0;
        let createFlag = (value == 0)? true: false;
        this.setState({createFlag})
    }
    render() {
        let dataOtypes = CraftOtype.getDataList();
        const { canRedo, canUndo, redoLoading, undoLoading } = this.state;
        return (
            <div className="gx_drawer_container">
                <div className="drawer_shape">
                    <ul>
                        {
                            dataOtypes.map((item,index) => (
                            <li key={index}
                            item={item}
                            className="dnd-rect"
                            onMouseUp={e => this.mouseUpNode(e,item)}
                            onMouseLeave={e=>this.startDrag(e,item)}
                            >
                                <Tooltip title={item.name} placement="bottom">
                                    <IconFont type={item.icon}></IconFont>
                                </Tooltip>
                                {/* <p>{item.name}</p> */}
                            </li>))
                        }
                    </ul>
                </div>
                <div className="drawer_handle">
                    <ul>
                        <li>
                            <Tooltip title="撤销" placement="bottom">
                                <Button disabled={!canUndo} type="link" onClick={this.undo} loading={undoLoading} id="wait-time-undo">
                                    {undoLoading?'':<IconFont type="iconchexiao"></IconFont>}
                                </Button>
                            </Tooltip>
                        </li>
                        <li>
                            <Tooltip title="重做" placement="bottom">
                                <Button disabled={!canRedo} type="link" onClick={this.redo} loading={redoLoading} className="canredo-btn" id="wait-time-redo">
                                    {redoLoading?'':<IconFont type="iconchexiao1"></IconFont>}
                                </Button>
                            </Tooltip>
                        </li>
                    </ul>
                </div>
            </div>
        );
    }
}

export default CraftEdit;