import { Gesture } from "./Gesture";
import { Transform } from "./Transform";
import { InputConnector, SelectInputConnector, OutputConnector, NodeEditor, Connector } from "./index";

/**
 * Represent a node with all its input and ouputs.
 */
export class Node{
    
    protected titleElement: HTMLElement;
    protected element: HTMLElement;
    protected data: Record<string, any>;
    protected name: string;
    protected editor: NodeEditor;
    protected inputs: InputConnector[] = [];
    protected outputs: OutputConnector[] = [];
    protected transform: Transform;
    protected gesture: Gesture;
    protected type: string;
    protected description: string;
    
    /**
     * Creates a new node.
     * @param type Type definition for the node.
     * @param data Data for this node, like position, values
     * @param editor THe node editor.
     * @param name Name of the node.
     */
    constructor(type: Record<string, any>, data: Record<string, any>, editor: NodeEditor, name: string){
        this.data = data;
        this.name = name;
        this.element = document.createElement("div");
        this.element.Node = this;
        this.element.className = "e-node";
        //Title (e-node-label)
        this.titleElement = document.createElement("div");
        this.titleElement.className = "e-node-title";
        this.titleElement.innerHTML = (data.userdata && data.userdata.label) || data.type;
        this.type = data.type;
        this.element.appendChild(this.titleElement);
        //this.element.onchange = this.changeListener;
        
        this.gesture = new Gesture(this.element, {
            scale: false
        });
        
        this.transform = this.gesture.getTransform();
        this.transform.setPosition(data.x, data.y);
        
        this.element.addEventListener('Gesture.move', e => this.update());
        
        this.editor = editor;
        
        let output = type.out;
        if(data.userdata && data.userdata.out){
            output = Object.assign({}, output, data.userdata.out);
        }
        for (var key in output) {
            this.addOutput(key, output[key]);
        }
        let input = type.in;
        if(data.userdata && data.userdata.in){
            Object.assign(input, data.userdata.in);
        }
        for (var key in input) {
            this.addInput(key, input[key], data.values && data.values[key] || null);
            
        }

        this.titleElement.addEventListener("click", () => {
            editor.selectNode(this);
        });
        this.description = type.description;
        editor.getContainerElement().appendChild(this.element);
    }
    
    /**
     * Adds an output, and creates a connector for it.
     * @param key name of the output
     * @param data Data for the connector
     */
    addOutput(key: string, data){
        new OutputConnector(this, key, data);
    }
    
    addInput(key, data, value){
        if(window[data.type + "InputConnector"]){
            new window[data.type + "InputConnector"](this, key, data, value);
        }else if(data.type == "select"){
            new SelectInputConnector(this, key, data, value);
        }else{
            new InputConnector(this, key, data, value);
        }
    }
    
    getInputs(){
        return this.inputs;
    }
    
    update(){
        for(let k in this.inputs){
            this.inputs[k].update();
        }
        for(let k in this.outputs){
            this.outputs[k].update();
        }
    }
    
    getTransform(){
        return this.transform;
    }

    getGesture(){
        return this.gesture;
    }
    
    getEditor(){
        return this.editor;
    }
    
    getName(){
        return this.name;
    }
    
    getData(){
        let data = {
            type: this.type,
            x: this.transform.getX(),
            y: this.transform.getY(),
            values: {}
        };
        if(this.data.userdata){
            data.userdata = this.data.userdata
        }
        for(let k in this.inputs){
            data.values[this.inputs[k].getKey()] = this.inputs[k].getValue();
        }
        return data;
    }

    getTestData(){
        return this.editor.getTestData()[this.name];
    }

    setSelected(select = true){
        if(select){
            this.element.classList.add("e-node-selected");
        }else{
            this.element.classList.remove("e-node-selected");
        }
    }

    getElement(){
        return this.element;
    }

    getDescription(){
        return this.description;
    }

    remove(){
        for(const input of Object.values(this.inputs)){
            input.remove();
        }
        for(const output of Object.values(this.outputs)){
            output.remove();
        }
        this.element.remove();
        delete this.getEditor().nodes[this.name];
    }
}