import * as d3 from 'd3';
import { globalLookup, globalSetting } from '../global';
import { NetlistRender } from '.';
import { LAYOUT_CONSTANT, makeEdgeId } from './layout';

export class CellRender {
    /**
     * 
     * @param {d3.Selection} selection 
     * @param {NetlistRender} rootRender 
     */
    constructor(selection, rootRender) {
        this.parentSelection = selection;
        this.rootRender = rootRender;

        /**
         * @type {BasicD3DataItem[]}
         */
        this.data = [];

        /**
         * @description id 到管理数据项的映射
         * @type {Map<string, BasicD3ManagmentItem[]>}
         */
        this.id2manager = rootRender.id2manager;
    }

    /**
     * @description 将 elknode 关于 器件 的数据添加为 d3 数据项目
     * @param {ElkNode} node 
     * @param {HTMLElement} element
     */
    addAsD3DataItem(node, element) {
        this.data.push({
            id: node.id,
            element,
            x: node.x,
            y: node.y,
            width: node.width,
            height: node.height,
            fill: 'var(--main-dark-color)'
        });
    }

    render() {
        const data = this.data;
        const rootRender = this.rootRender;
        const id2manager = this.id2manager;
        const _this = this;

        let cellSelections = this.parentSelection.selectAll('svg')
            .data(data, d => d.id)
            .enter()
            .append(data => {
                const element = data.element;                     
                element.setAttribute('x', data.x + LAYOUT_CONSTANT.CELL_LEFT_MARGIN);
                element.setAttribute('y', data.y);
                if (globalSetting.renderAnimation) {
                    element.setAttribute('opacity', 0);
                }
                return element;
            })
            .each(function (data) {
                const cellSelection = d3.select(this);
                // const manager = _this.createDataManager(cellSelection, data);

                // TODO: 实现拖拽
                // registerDragEvent(manager, rootRender);
            });        
        
        if (globalSetting.renderAnimation) {
            cellSelections = cellSelections
                .transition()
                .duration(1000)
                .attr('opacity', 1)
                .attr('class', 'grab');
        } else {
            cellSelections = cellSelections
                .attr('class', 'grab');
        }

        this.selections = cellSelections;
        return cellSelections;
    }

    /**
     * 
     * @param {d3.Selection} selection 
     * @param {BasicD3DataItem} data 
     * @returns {BasicD3ManagmentItem}
     */
    createDataManager(selection, data) {
        const id2manager = this.id2manager;
        // 创建拖拽上下文
        const dragContext = {
            neighbors: [],
            elkGraph: {
                // elk 是无状态的，id 取什么名字都行
                id: 'root',
                children: [],
                edges: [],
                layoutOptions: {}
            }
        }
        const managerItem = {
            data,
            selection,
            type: 'cell',
            dragContext
        };

        if (!id2manager.has(data.id)) {
            id2manager.set(data.id, []);
        }

        id2manager.get(data.id).push(managerItem);
        return managerItem;
    }
}

/**
 * @description 注册关于 器件 的拖动事件
 * 
 * 需要提取最小拓扑子图，然后重新调整各个区域的尺寸
 * @param {BasicD3ManagmentItem} manager
 * @param {NetlistRender} rootRender
 */
export function registerDragEvent(manager, rootRender) {
    // 创建拖拽行为
    const drag = d3.drag();

    drag.on('start', async event => await dragStart(event, manager, rootRender));
    drag.on('drag', async event => await dragged(event, manager, rootRender));
    drag.on('end', async event => await dragEnd(event, manager, rootRender))
    
    manager.selection.call(drag);
}

/**
 * 
 * @param {d3.D3DragEvent} event 
 * @param {BasicD3ManagmentItem} manager
 * @param {NetlistRender} rootRender 
 */
async function dragStart(event, manager, rootRender) {
    const selection = manager.selection;
    selection.attr('class', 'grabbing');
    
    // 更新当前的 elkGraph
    const context = manager.dragContext;
    const nodes = [];
    const edges = [];

    const Avoid = globalLookup.Avoid;

    const layoutOptions = {
        // node 固定不动
        'elk.stress.fixed': true,
        // node 的 port 固定不动
        'elk.portConstraints': 'FIXED_POS',
        // 从左到右
        'elk.direction': 'RIGHT'
    };

    // 添加自己的节点
    const cell = manager.selection.datum();
    // 获取 connection

    const id2manager = rootRender.id2manager;

    const cellNode = {
        id: cell.id,
        x: cell.x,
        y: cell.y,
        width: cell.width,
        height: cell.height,
        // 后续遍历时获取该 port
        ports: [],
        layoutOptions
    };

    // 添加邻居的节点和边
    for (const neighbor of context.neighbors) {
        const ndata = neighbor.selection.datum();
        const node = {
            id: ndata.id,
            width: ndata.width,
            height: ndata.height,
            x: ndata.x,
            y: ndata.y,
            ports: [],
            layoutOptions
        };

        // 加入 port（如果有的话）
        for (const connection of neighbor.connections) {
            const conndata = connection.selection.datum();
            node.ports.push({
                id: conndata.id,
                width: conndata.width,
                height: conndata.height,
                x: conndata.x,
                y: conndata.y
            });
        }

        nodes.push(node);

        edges.push({
            id: neighbor.edge.id,
            source: neighbor.edge.source,
            sourcePort: neighbor.edge.sourcePort,
            target: neighbor.edge.target,
            targetPort: neighbor.edge.targetPort             
        });

        if (neighbor.location === 'source') {
            // 此时 cell 的 connection 就是 targetPort
            const cellConnectionId = neighbor.edge.targetPort;
            const connectionManager = id2manager.get(cellConnectionId)[0];
            const conndata = connectionManager.data;
            cellNode.ports.push({
                id: conndata.id,
                width: conndata.width,
                height: conndata.height,
                x: conndata.x,
                y: conndata.y
            });
        } else {
            // 此时 cell 的 connection 就是 sourcePort
            const cellConnectionId = neighbor.edge.sourcePort;
            const connectionManager = id2manager.get(cellConnectionId)[0];
            const conndata = connectionManager.data;
            cellNode.ports.push({
                id: conndata.id,
                width: conndata.width,
                height: conndata.height,
                x: conndata.x,
                y: conndata.y
            });
        }
    }

    nodes.push(cellNode);

    context.cellNode = cellNode;
    context.elkGraph.children = nodes;
    context.elkGraph.edges = edges;
    context.elkGraph.layoutOptions = {
        'elk.algorithm': 'layered'
    };
}

/**
 * 
 * @param {d3.D3DragEvent} event 
 * @param {BasicD3ManagmentItem} manager
 * @param {NetlistRender} rootRender 
 */
async function dragged(event, manager, rootRender) {
    // 当拖动结束时，D3 会根据绑定的数据（data.x 和 data.y）重新渲染元素，导致元素回到初始位置。
    // 所以需要 手动更新 data.x 和 data.y
    const selection = manager.selection;
    const data = manager.data;

    data.x = event.x;
    data.y = event.y;
    
    selection
        .attr('x', event.x)
        .attr('y', event.y);
    
    // 根据最小拓扑图，提取出关键点，重新计算布局
    const context = manager.dragContext;
    const elkGraph = context.elkGraph;
    const currentNode = elkGraph.children.at(-1);
    currentNode.x = event.x;
    currentNode.y = event.y;

    // const computedLayout = await rootRender.elk.layout(elkGraph);
}

/**
 * 
 * @param {d3.D3DragEvent} event 
 * @param {BasicD3ManagmentItem} manager
 * @param {NetlistRender} rootRender
 */
async function dragEnd(event, manager, rootRender) {
    const selection = manager.selection;
    selection.attr('class', 'grab');
}