/* eslint-disable */

import * as d3 from 'd3';
import { globalSetting } from '../global';
import { NetlistRender } from '.';
import { getMarginParamter, LAYOUT_CONSTANT, LINE_WIDTH } from './layout';
import { svgResource } from '../skin/draw';
import { PulseLine } from '../skin/plusation';

export class WireRender {
    /**
     * 
     * @param {d3.Selection} selection
     * @param {NetlistRender} rootRender  
     */
    constructor(selection, rootRender) {
        this.selection = selection;
        this.rootRender = rootRender;
        
        /**
         * @type {BasicD3DataItem[]}
         */
        this.data = [];

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

        this.idCounter = 0;

        this.arrowHeight = 12;
        this.arrowWidth = 12;
    }

    /**
     * @description 将 elknode 关于 wire 的数据添加为 d3 数据项目
     * @param {import('../jsdoc').ElkPoint[]} points 长度至少为 2 的数组，代表经历过的点集
     * @param {import('../jsdoc').ElkEdge} edge 
     * @param {Map<string, import('../jsdoc').ElkPort>} id2port
     */
    addAsD3DataItems(points, edge, id2port) {
        const linePaths = [];

        const beginPoint = points.at(0);
        const endPoint = points.at(-1);

        const sourcePort = id2port.get(edge.sourcePort);
        const targetPort = id2port.get(edge.targetPort);
        const sourceMargin = getMarginParamter(sourcePort);
        const targetMargin = getMarginParamter(targetPort);

        beginPoint.x -= sourceMargin.rightMargin;

        // 特殊情况： targetPort 为 undefined
        if (targetPort === undefined) {
            const port = id2port.get(edge.orginalTarget);
            // TODO: 检查正确性
            endPoint.x -= LAYOUT_CONSTANT.INSTANCE_RIGHT_MARGIN;
        } else {
            endPoint.x += targetMargin.leftMargin;
        }

        for (let i = 0; i < points.length; ++ i) {
            // 根据点的信息创建 path
            const command = i === 0 ? 'M': 'L';
            const x = points[i].x;
            const y = points[i].y;
            linePaths.push(`${command}${x},${y}`);
        }

        const lineSvg = linePaths.join(' ');

        // 判断当前的朝向
        const direction = endPoint.x > points.at(-2).x ? 'right' : 'left';
        const arrowX = direction === 'right' ? endPoint.x - LAYOUT_CONSTANT.CELL_PORT_WIDTH - this.arrowWidth + 2.5:
                                               endPoint.x + LAYOUT_CONSTANT.CELL_PORT_WIDTH - 2.5

        const arrowY = endPoint.y - this.arrowHeight / 2;
        
        this.data.push({
            id: this.idCounter,
            svg: lineSvg,
            endPoint: points.at(-1),
            arrow: {
                icon: direction + '-arrow',
                x: arrowX,
                y: arrowY,
                width: this.arrowWidth,
                height: this.arrowHeight
            },
            active: false
        });

        this.idCounter ++;
    }

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

        const arrowHeight = 12;
        const arrowWidth = 12;


        let lineSelections = this.selection.selectAll('path.lines')
            .data(data)
            .enter()
            .append('path')
            .attr('d', d => d.svg)
            .attr('class', 'connection-line')
            .attr("fill", "none")
            .attr('stroke', 'var(--wire-color)');
        
        const arrows = new Map();

        let arrowSelections = this.selection.selectAll('svg.line-arrow')
            .data(data)
            .enter()
            .append(data => {
                const arrowInfo = data.arrow;

                // 获取箭头 svg
                const element = svgResource.get(arrowInfo.icon);

                element.setAttribute('x', arrowInfo.x);
                element.setAttribute('y', arrowInfo.y);
                element.setAttribute('width', arrowInfo.width);
                element.setAttribute('height', arrowInfo.height);
                element.setAttribute('opacity', 'var(--line-arrow-opacity)');
                
                return element;
            })
            .each(function(data) {
                const selection = d3.select(this);
                arrows.set(data.id, selection);
            });

        arrowSelections.raise();

        const id2animation = new Map();
        const id2PluseLine = new Map();

        lineSelections.on('click', function(_, data) {
            data.active = !data.active;

            if (data.active) {
                const pathSelection = d3.select(this);
                const pulse = new PulseLine();
                id2PluseLine.set(data.id, pulse);

                // 开启脉冲动画
                pulse.loadToSelection(_this.selection, data);
                pulse.startAnimation();
                
            } else {
                if (id2PluseLine.has(data.id)) {
                    const pulse = id2PluseLine.get(data.id);
                    pulse.destory();
                    id2PluseLine.delete(data.id);

                    // 关闭高亮
                    const selection = d3.select(this);
                    selection.attr('stroke', 'var(--wire-color)');
                    const arrowSelection = arrows.get(data.id);
                    arrowSelection.selectAll('path').attr('fill', 'var(--wire-color)');
                }
            }
        });

        lineSelections.on('mouseenter', function(_, data) {
            const selection = d3.select(this);
            selection.raise();
            selection.attr('stroke', 'var(--wire-active-color)');

            const arrowSelection = arrows.get(data.id);            
            arrowSelection.raise();
            arrowSelection.selectAll('path').attr('fill', 'var(--wire-active-color)');
        });

        lineSelections.on('mouseleave', function(_, data) {
            if (!id2PluseLine.has(data.id)) {
                const selection = d3.select(this);
                selection.attr('stroke', 'var(--wire-color)');
    
                const arrowSelection = arrows.get(data.id);
                arrowSelection.selectAll('path').attr('fill', 'var(--wire-color)');
            }
        });

        
        if (globalSetting.renderAnimation) {
            lineSelections = lineSelections
                .transition()
                .duration(1000);
        }

        lineSelections
            .attr('stroke-width', LINE_WIDTH)
            .each(function (data) {
                const selection = d3.select(this);
                // const manager = _this.createDataManager(selection, data);
            });
    }

    /**
     * 
     * @param {d3.Selection} selection 
     * @param {BasicD3DataItem} data 
     * @returns {BasicD3ManagmentItem}
     */
    createDataManager(selection, data) {
        const id2manager = this.id2manager;
        // wire 不需要拖拽上下文
        const managerItem = {
            data,
            selection,
            type: 'wire'
        };

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

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

/**
 * @description 计算贝塞尔插值，输入 0 - 1 的值 (delta)，输出 [oldVal, newVal] 中间的插值
 * @param {*} delta 
 * @param {*} oldVal 
 * @param {*} newVal 
 * @returns 
 */
function cubicBezierAnimation(delta, oldVal, newVal) {
    delta = 3 * (1 - delta) * (1 - delta) * delta + 3 * (1 - delta) * delta * delta + delta * delta * delta;
    return (1 - delta) * oldVal + delta * newVal;
}