import { Connector, NodeEditor, InputConnector, OutputConnector } from "./";

export class Connection{
    protected source: OutputConnector;
    protected target: InputConnector;
    protected element: SVGElement;
    protected editor: NodeEditor;
    protected mouseMoveListener: (event: MouseEvent) => void;
    start = {
        x: 0,
        y: 0
    };
    end = {
        x: 0,
        y: 0
    };
    constructor(source: OutputConnector, target: InputConnector){
        this.element = document.createElementNS("http://www.w3.org/2000/svg", "path");
        this.element.classList.add("e-node-connection");
        this.element.Connection = this;
        if(source){
            this.setSource(source);
            this.editor = source.getNode().getEditor();
        }
        if(target){
            this.setTarget(target);
            if(!this.editor){
                this.editor = target.getNode().getEditor();
            }
        }
        if(source && target){
            this.update();
        }else{
            this.editor.setActiveConnection(this);
            this.editor.getElement().addEventListener("mouseup", this.mouseUp.bind(this),{once: true});
            this.mouseMoveListener = this.mouseMove.bind(this);
            this.editor.getElement().addEventListener("mousemove", this.mouseMoveListener);
        }
        this.editor.getSvg().appendChild(this.element);
    }
    
    mouseMove(event: MouseEvent){
        const coords = this.editor.getGesture().inverseTransform(event.clientX, event.clientY);
        const clientRect = this.editor.getElement().getBoundingClientRect()
        this.update(coords.x - clientRect.x, coords.y - clientRect.y);
    }
    
    mouseUp(event){
        this.editor.getElement().removeEventListener("mousemove", this.mouseMoveListener);
        if(!this.source || !this.target){
            this.remove();
        }
        this.editor.setActiveConnection(null);
    }
    
    update(x?: number, y?: number){
        if(this.source){
            let sourcePos = this.source.getNode().getTransform().getPosition();
            let sourceElement = this.source.element;
            this.start.x = sourcePos.x + sourceElement.offsetWidth;
            this.start.y = sourcePos.y + sourceElement.offsetTop + sourceElement.offsetHeight/2;
        }else{
            this.start.x = x;
            this.start.y = y;
        }

        if(this.target){
            let targetPos = this.target.getNode().getTransform().getPosition();
            let targetElement = this.target.element;
            this.end.x = targetPos.x;
            this.end.y = targetPos.y + targetElement.offsetTop + targetElement.offsetHeight/2;
        }else{
            this.end.x = x;
            this.end.y = y;
        }
        let c = (this.start.x + this.end.x)/2;
        this.element.setAttribute("d", "M"+this.start.x+","+this.start.y+" C"+c+","+this.start.y+" "+c+","+this.end.y+" "+this.end.x+","+this.end.y);
    }
    
    getSource(): OutputConnector{
        return this.source;
    }
    
    /**
     * @param source
     */
    setSource(source: OutputConnector){
        if(source){
            source.addConnection(this);
        }
        if(this.source){
            this.source.removeConnection(this);
        }
        this.source = source;
        this.updateColor();
    }
    
    getTarget(): InputConnector{
        return this.target;
    }

    updateColor(){
        if(this.source && this.target){
            this.element.setAttribute("stroke", this.getOrCreateGradient(this.source.getColor(), this.target.getColor()));
        }else if(this.source){
            this.element.setAttribute("stroke", this.source.getColor());
        }else if(this.target){
            this.element.setAttribute("stroke", this.target.getColor());
        }
    }
    
    /**
     * @param target
     */
    setTarget(target: InputConnector){
        if(target){
            target.addConnection(this);
        }
        if(this.target){
            this.target.removeConnection(this);
        }
        this.target = target;
        this.updateColor();
    }

    getOrCreateGradient(firstColor: string, secondColor: string): string {
        const svg = this.editor.getSvg();
        const defs = svg.querySelector('defs') || document.createElementNS("http://www.w3.org/2000/svg", 'defs');
        if (!svg.querySelector('defs')) {
            svg.appendChild(defs);
        }
    
        const gradientId = `connection-gradient-${firstColor.slice(1)}-${secondColor.slice(1)}`;
        let gradient = svg.querySelector(`#${gradientId}`) as SVGLinearGradientElement;
    
        if (!gradient) {
            // Create new gradient
            gradient = document.createElementNS("http://www.w3.org/2000/svg", 'linearGradient');
            gradient.id = gradientId;
    
            const stop1 = document.createElementNS("http://www.w3.org/2000/svg", 'stop');
            stop1.setAttribute('offset', '0%');
            stop1.setAttribute('stop-color', `${firstColor}`);
            gradient.appendChild(stop1);
    
            const stop2 = document.createElementNS("http://www.w3.org/2000/svg", 'stop');
            stop2.setAttribute('offset', '100%');
            stop2.setAttribute('stop-color', `${secondColor}`);
            gradient.appendChild(stop2);
    
            defs.appendChild(gradient);
        }
    
        return `url(#connection-gradient-${firstColor.slice(1)}-${secondColor.slice(1)})`;
    }
    
    
    remove(){
        this.setSource(null);
        this.setTarget(null);
        this.element.remove();
    }
}