import * as d3 from 'd3';
import { NetlistRender } from '.';
import { globalSetting } from '../global';
import { LAYOUT_CONSTANT } from './layout';
import { svgResource } from '../skin/draw';

export class InstantiationRender {
    /**
     * 
     * @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 {import('../jsdoc').ElkNode} node 
     */
    addAsD3DataItem(node) {
        console.log(node);
        
        const nodeModule = this.rootRender.nameToModule.get(node.renderName);        
        const view = nodeModule.view;
        const textPadding = 5;
        const portnames = [];

        // 当前的例化是否为一个展开的例化
        this.isExpanded = (node.children || []).length > 0;
        const topPadding = this.isExpanded ? 25 : 0;

        for (const port of node.ports || []) {
            const isInput = port.x < LAYOUT_CONSTANT.INSTANTIATION_WIDTH;
            const align = isInput ? 'left': 'end';
            const portX = isInput ? port.x + textPadding : port.x - textPadding - LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN - LAYOUT_CONSTANT.INSTANCE_RIGHT_MARGIN;
            const portY = this.isExpanded ? port.y - 10: port.y;
            
            portnames.push({
                name: port.renderName,
                x: portX,
                y: portY,
                align
            });
        }
        

        this.data.push({
            id: node.id,
            type: node.renderName,  // 例化模块的模块名字
            x: node.x,
            y: node.y,
            name: node.name,
            width: node.width,
            height: node.height + topPadding,
            topPadding,
            text: node.renderName,
            portnames,
            rx: 3,
            ry: 3,
            // 数据本身
            _self: node
        });
    }

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

        // 外部控制主体的 g
        let instantiationSelections = this.parentSelection.selectAll('g.instance')
            .data(data)
            .enter()
            .append('g')
            .attr('class', 'instance')
            .attr("transform", d => `translate(${d.x}, ${d.y})`);
        
        // 例化模块的方块
        let instances = instantiationSelections.append('rect')
            .attr('x', LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
            .attr('y', d => LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT - d.topPadding)
            .attr('width', data => data.width - LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN - LAYOUT_CONSTANT.INSTANCE_RIGHT_MARGIN)
            .attr('height', data => data.height - LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT);
        

        // 说明文字
        let texts = instantiationSelections.append('text')
            .attr('x', LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
            .attr('y', d => LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT - 8 - d.topPadding)
            .attr('dominant-baseline', 'middle')
            .attr('text-anchor', 'left')
            .attr('fill', 'var(--foreground)')
            .attr('font-size', '14px')
            .attr('class', 'port-caption')
            .text(data => data.name)
            .each(function(data) {
                const text = d3.select(this);
                const bbox = text.node().getBBox();                
                data.textX = (data.width - bbox.width) / 2;
            })
            .attr('x', d => d.textX)


        // 渲染 portnames
        instantiationSelections.each(function(node) {
            d3.select(this)
                .selectAll('.port-name')
                .data(node.portnames)
                .enter()
                .append('text')
                .attr('class', 'port-name')
                .attr('x', d => d.x + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
                .attr('y', d => d.y)
                .attr('dominant-baseline', 'middle')
                .attr('text-anchor', d => d.align)
                .attr('fill', 'var(--foreground)')
                .attr('font-size', '11px')
                .text(d => d.name);
        });


        // 增加一个背景方块，防止 svg 点不到
        let bgFullScreenSelections = instantiationSelections.append('rect')
            .attr('x', 5 + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
            .attr('y', d => LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5 - d.topPadding)
            .attr('width', 20)
            .attr('height', 20)
            .attr('opacity', 0)
            .attr('class', 'pointer')
            .on('click', function(data) {
                const selection = d3.select(this);
                const d = selection.datum();
                const children = d._self.children || [];
                if (children.length > 0) {
                    rootRender.collapseInstance(selection);
                } else {
                    rootRender.expandInstance(selection);
                }
            });
        
        
        const id2element = new Map();

        // 渲染左上角的放大缩小图标，并注册对应的展开/收起事件
        let fullSreenSelections = instantiationSelections.append(data => {
            // 获取全屏图标
            const element = svgResource.get('full-screen');
            element.setAttribute('x', 5 + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN);
            element.setAttribute('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5 - data.topPadding);
            element.setAttribute('width', 20);
            element.setAttribute('height', 20);
            element.setAttribute('class', 'pointer');

            return element;
        })
        .each(function(data) {
            const svgElement = d3.select(this);
            id2element.set(data.id, svgElement);
        })
        .on('mouseenter', function() {
            const svgElement = d3.select(this);
            svgElement.selectAll("*")
                .attr("fill", "var(--instance-color)");
        })
        .on('mouseleave', function() {
            const svgElement = d3.select(this);
            svgElement.selectAll("*")
                .attr("fill", "var(--foreground)");
        })
        .on('click', function(data) {
            const selection = d3.select(this);
            const d = selection.datum();
            const children = d._self.children || [];

            if (children.length > 0) {
                rootRender.collapseInstance(selection);
            } else {
                rootRender.expandInstance(selection);
            }
        });

        bgFullScreenSelections
            .on('mouseenter', function(_, data) {
                const svgElement = id2element.get(data.id);
                svgElement.selectAll("*")
                    .attr("fill", "var(--instance-color)");
            })
            .on('mouseleave', function(_, data) {
                const svgElement = id2element.get(data.id);
                svgElement.selectAll("*")
                    .attr("fill", "var(--foreground)");
            });

        if (globalSetting.renderAnimation) {
            instances
                .transition()
                .duration(1000)
                .attr('fill', d => {
                    const children = d._self.children || [];
                    return children.length > 0 ? 'rgba(203, 129, 218, 0.1)' : 'var(--instance-fill-color)';
                })
                .attr('stroke', 'var(--instance-color)')
                .attr('stroke-width', 2);

        } else {
            instances
                .attr('fill', d => {
                    const children = d._self.children || [];
                    return children.length > 0 ? 'rgba(203, 129, 218, 0.1)' : 'var(--instance-fill-color)';
                })
                .attr('stroke', 'var(--instance-color)')
                .attr('stroke-width', 2)            
        }

        instantiationSelections
            .attr('class', 'grab')
            .each(function (data) {                    
                const portSelection = d3.select(this);
                // const manager = _this.createDataManager(portSelection, data);

                // TODO: 实现拖拽
                // registerDragEvent(manager, rootRender);       
            });
            
        this.selections = instantiationSelections;
        return instantiationSelections;
    }

    /**
     * 
     * @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;
    }
}