/**
 * @author Martin Karkowski
 * @email m.karkowski@zema.de
 * @create date 2020-11-25 08:19:39
 * @modify date 2020-11-25 12:05:20
 * @desc [description]
 */

import * as React from "react";
import { Button, ListGroup } from "react-bootstrap";
import { v4 as generateID } from "uuid";
import { parseWithFunctions, stringifyWithFunctions } from "../../../lib/helpers/jsonMethods";
import { deepClone, rgetattr, rsetattr } from "../../../lib/helpers/objectMethods";
import { IJsonSchema } from "../../../lib/types/IJSONSchema";
import { readDataFromClipboard, writeToClipboard } from "../helpers/clipboard";
import { getCurrentThemeColors } from "../helpers/colors";
import DynamicLayout, { IDynamicLayoutProps } from "../layout/dynamicLayout";
import { ILayout } from "../layout/interfaces/ILayout";
import { ISelection } from "../layout/interfaces/ISelection";
import { ITab } from "../layout/interfaces/ITab";
import { ITabEntry } from "../layout/interfaces/ITabEntry";
import Selection from "../layout/selection";
import TabEntry, { ITabProps } from "../layout/tabs";
// Import Addons:
import { makeMeMultiSelect } from "./addons/selectionbox.extension";
import { generateDefaultSelection } from "./defaults/default.elements";
import { generateGraphOptions } from "./defaults/default.graph-options";
import { defaultHotkeys } from "./defaults/default.hotkeys";
import { defaultToolbar } from "./defaults/default.toolbar";
// Graph related imports:
import { UndoRedoGraph } from "./graph";
import { generateColors } from "./helpers/color.theme";
import { adaptIDS, adaptPositions, getSelectedElements } from "./helpers/data.handlers";
import { IBaseEdgeOptions } from "./interfaces/IBaseEdgeOptions";
import { IBaseNodeOptions } from "./interfaces/IBaseNodeOptions";
import { IBasicTemplate } from "./interfaces/IBasicTemplate";
import { IEdgeTypeDefinition } from "./interfaces/IEdgeTypeDefinition";
import { IEvent } from "./interfaces/IEvents";
import { IUndoRedoGraph } from "./interfaces/IGraph";
import { IGraphCallbackData } from "./interfaces/IGraphCallbackData";
import { DEFAULT_NETWORK, INetwork } from "./interfaces/INetwork";
import { IVisjsOptions } from "./interfaces/IVisjsOptions";


export interface GraphicalEditorComponentProps<N, E> {
    graphOptions?: IVisjsOptions;
    network?: INetwork<N, E>;
    addEdgeCallback?: (edgeData: E, callback: (edgeData: E) => void) => void;
    editOnSelect?: boolean;
    editOnChange?: boolean;
    parseFunctions?: boolean;
    enableContextMenu?: boolean;
    enableEditing?: boolean;
    hidePanelOnDeselect?: boolean;
    hideToolbar?: boolean;
    hideRightPanel?: boolean;
    allowSelfConnection?: boolean;
    allowDoubleConnections?: boolean;
    edgeTypes?: IEdgeTypeDefinition<N, E>[];
    showEdgeSelction?: boolean
}

export interface GraphicalEditorComponentState {
    update: null
}

class GraphicalEditorComponent<N, E> extends React.Component<GraphicalEditorComponentProps<N, E>, GraphicalEditorComponentState> {

    /**
     * The Element containing the Network.
     */
    public Network: IUndoRedoGraph<N, E>;
    public LayoutHandler: ILayout;
    public TabHandler: ITabEntry;

    public graphOptions: IVisjsOptions;
    public theme: {
        colors: {
            primary: string;
            secondary: string;
            success: string;
            info: string;
            warning: string;
            danger: string;
            light: string;
            dark: string;
        };
        font: {
            size: number;
            type: string;
        };
    }
    constructor(props) {
        super(props);
        this.theme = getCurrentThemeColors();
    }

    public tabs: { [index: string]: INetwork<N, E> } = {};

    /**
     * Function to add the Addons of the Graph:
     * @param network 
     */
    public addGraphAddons(network: IUndoRedoGraph<N, E>) {
        makeMeMultiSelect(network);
    }

    /**
     * Function to add an Edge.
     * @param edgeData 
     * @param callback 
     */
    protected async _addEdge(edgeData: IBaseEdgeOptions, callback: (edge: IBaseEdgeOptions) => void) {

        // Defaulty prevent self connections.
        if (this.props.allowSelfConnection || edgeData.from !== edgeData.to) {

            let addEdge = true;

            // If multiple Connections from one node to the same node are
            // for bidden, test if there already exists such an edge.
            if (!this.props.allowDoubleConnections) {
                this.Network.edges.forEach((edge) => {
                    if (edge.from === edgeData.from && edge.to === edgeData.to) {
                        addEdge = false;
                    }
                });
            }

            // If adding an Edge is allowed => Add the Edge.
            if (addEdge) {

                // Manually add an ID to the Edge
                edgeData.id = generateID();

                // Extract the From and To Node.
                const from = this.Network.getNode(edgeData.from);
                const to = this.Network.getNode(edgeData.to);

                const _this = this;
                const _addEdge = (type: IEdgeTypeDefinition<N, E>) => {
                    if (typeof type.customAddFunction == "function") {

                        type.customAddFunction(from, to, _this.Network);

                    } else {
                        // Define the Edge Object
                        const edge = Object.assign(deepClone(type.visual), {
                            from: edgeData.from,
                            to: edgeData.to
                        });

                        // Add the Edge.
                        callback(edge);
                    }
                };

                switch (edgeData.type) {
                    case "automatic":
                        // Search for Possible Connection Types.
                        const possibleEdges: IEdgeTypeDefinition<N, E>[] = [];

                        for (const edgeType of this.props.edgeTypes || []) {
                            if ((edgeType.fromTypes.includes(from.type) || edgeType.fromTypes.length === 0) && (edgeType.toTypes.includes(to.type) || edgeType.toTypes.length === 0)) {
                                possibleEdges.push(edgeType);
                            }
                        }

                        // Test if Edges has been found
                        switch (possibleEdges.length) {
                            case 0:
                                this.LayoutHandler.showToast("No Matching Edge Type has been found. Please select the corresponding Edge Type manually", "info");
                                return;
                            case 1:
                                // Define the New Edge.
                                return _addEdge(possibleEdges[0]);
                            default:
                                if (this.props.showEdgeSelction) {
                                    // Create a Popup, on which the corresponding edge can be selected:
                                    try {
                                        const type: IEdgeTypeDefinition<N, E> = await this.LayoutHandler.getDialogData({
                                            content: {
                                                props: {
                                                    possibleEdges
                                                },
                                                component(props: {
                                                    possibleEdges: IEdgeTypeDefinition<N, E>[],
                                                    onSubmit: (data: IEdgeTypeDefinition<N, E>) => void;
                                                    onCancel: (error: any) => void
                                                }) {
                                                    return (
                                                        <>
                                                            Select a possible edge by clicking on the item
                                                            <ListGroup>
                                                                {
                                                                    props.possibleEdges.map((item, idx) => {
                                                                        <ListGroup.Item key={idx} onClick={_ => props.onSubmit(item)}>{item.type}</ListGroup.Item>;
                                                                    })
                                                                }
                                                            </ListGroup>
                                                            <Button variant="danger" onClick={e => props.onCancel(e)}>Cancel </Button>
                                                        </>
                                                    );
                                                },
                                            },
                                            header: "Please select an edge-type!",
                                            closeButton: true
                                        });

                                        // Add the Edge itself.
                                        return _addEdge(type);
                                    } catch (e) {
                                        // The User has aborted the selection.
                                        return;
                                    }
                                }
                                this.LayoutHandler.showToast("Mulitple Valid Edge Type has been found. Please select the corresponding Edge Type manually", "info");
                        }
                        return;
                    default:
                        for (const type of this.props.edgeTypes || []) {
                            if ((edgeData.type === type.type) && (type.fromTypes.includes(from.type) || type.fromTypes.length === 0) && (type.toTypes.includes(to.type) || type.toTypes.length === 0)) {
                                // Quit the Method
                                return _addEdge(type);
                            }
                        }
                        this.LayoutHandler.showToast("You are not allowed to connect the desired Nodes with this edges", "error");
                        break;
                }

            } else {
                this.LayoutHandler.showToast("Double Connections not allowed! You can not connect these elements again", "error");
            }
        } else {
            this.LayoutHandler.showToast("Self-Connections forbidden! You can not connect elements to itself", "error");
        }
    }

    protected async _editNodes(_template: IBasicTemplate<N,E>, mode: "add" | "update" = "update") {

        // Defaulty prevent self connections.
        if (_template?.nodes.length > 0) {

            try {
                if (_template.nodes[0].editorComponentSelector && false){
    
                } else {
                    // Open up the corresponding Node Panel:
                    _template.nodes[0] = await this.LayoutHandler.getDialogData({
                        content: {
                            component: "DynamicForm",
                            props: {
                                schema: {
                                    type: "object",
                                    properties:{
                                        label: {
                                            description: "Label of the Node",
                                            type: "string"
                                        },
                                        title: {
                                            description: "Tooltip, presented on Hovering",
                                            type: "string"
                                        },
                                        // color: {
                                        //     description: 'Color of the Node.',
                                        //     type: 'string'
                                        // }
                                    },
                                    required: [
                                        "label"
                                    ]
                                } as IJsonSchema,
                                uiSchema: {
                                    // color: {
                                    //     "ui:widget": "color"
                                    // }
                                },
                                data: Object.assign(generateColors(_template.nodes[0].shape, this.theme), _template.nodes[0])
                            }
                        }
                    });                    
                }       
                
                
                switch (mode) {
                    case "add":
                        this.Network.addNode(_template.nodes);
                        this.Network.addEdge(_template.edges);
                        break;
                    case "update":
                        this.Network.updateNode(_template.nodes);
                        this.Network.updateEdge(_template.edges);
                        break;
                }

                // Tabs
                this.tabs[this.TabSettings.tabs.active] = this.Network.getData();
            } catch(error) {
                console.log(error);
            }
        } else {
            this.LayoutHandler.showToast("Please Select a Node Before.", "error");
        }
    }

    public initEditor(ref: React.RefObject<any>) {

        if (process.browser){
            const _this = this;

            // Based on the Provided Options =>
            // select the corresponding graph options,
            // either use th provided one or use the 
            // default options.
            if (this.props.graphOptions) {
                this.graphOptions = this.props.graphOptions;
            } else {
                this.graphOptions = generateGraphOptions();
            }
    
            if (!rgetattr(this.graphOptions, "manipulation.enabled", false)) {
                rsetattr(this.graphOptions, "manipulation.enabled", false);
            }

            // Generate the Default Callback for adding an Edge
            rsetattr(
                this.graphOptions,
                "manipulation.addEdge",
                (data,callback) => {
                    _this._addEdge(data,callback).catch(console.error);
                }                
            );

            // Generate the Default Callback for adding a Node
            rsetattr(
                this.graphOptions,
                "manipulation.addNode",
                (data,callback) => {
                    _this._addNode(data).catch(console.error);
                }                
            );
    
            // Create the Network
            this.Network = new UndoRedoGraph(ref, this.graphOptions);
    
            // Add a new item if possible. 
            const editItemIfPossible = (params) => {
                if (params.nodes.length === 1) {
                    // A Node was Selected. Figure out, whether it is a cluster or not.
                    const nodeID = params.nodes[0];
    
                    if (_this.Network.network.isCluster(nodeID)) {
                        // Uncluster the Nodes
                        _this.Network.network.openCluster(nodeID);
                    } else {
                        // Open up the Settings Tab
                        const nodes  = getSelectedElements(_this.Network, false);
                        _this._editNodes({
                            edges: [],
                            nodes,
                            type: "element"
                        });
                    }
                } else if (params.edges.length === 1) {
                    // A Node was Selected. Figure out, whether it is a cluster or not.
                    const edges = _this.Network.data.edges.get(params.edges[0]);
                    _this.updateEdges(edges);
                }
            };

            if (!this.Network){
                throw Error("Something went wrong");
            }
    
            // Make the Default behaviour adding new Nodes
            this.Network.on("doubleClick", (params) => {
                if (_this.props.enableEditing || true) {
                    // Test if no Element was Selected
                    if (params.nodes.length === 0 && params.edges.length === 0) {
                        // If so, just add the new Element
                        _this._addNode(params.pointer.canvas);
                    } else {
                        editItemIfPossible(params);
                    }
                }
            });
    
            this.Network.on("oncopy", event => {
                try {
                    _this.LayoutHandler.showToast("Copied");
                    writeToClipboard(stringifyWithFunctions(_this.createTemplateOfSelectedElements()));
                } catch (e) {
    
                }
            });
    
            this.Network.on("onpaste", async event => {
                try {
                    const data = await readDataFromClipboard();
                    const mousePosition = _this.Network.network.DOMtoCanvas({
                        x: _this.LayoutHandler.currentMousePosition.offsetX,
                        y: _this.LayoutHandler.currentMousePosition.offsetY
                    });
                    _this.paste(parseWithFunctions(data), mousePosition, false);
                    _this.LayoutHandler.showToast("Pasted");
                } catch (e) {
                    _this.LayoutHandler.showToast("Failed Pasting","error");
                    console.error(e);
                }
            });    
    
            // // Make shure the Sidepanel is working correctly
            this.Network.on("select", (params) => {
                // if (_this.graphOptions.enableEditing){
                //     if (_this.graphOptions.editOnSelect && _this.graphOptions.hidePanelOnDeselect && params.nodes.length === 0 && params.edges.length === 0){
                //         _this.layout.panels.right.hide();
                //     } else if (_this.graphOptions.editOnSelect){
                //         editItemIfPossible(params);
                //     }
                // }
            });
    
            // enableClusterPreview(this);
    
            // this.network.addNode(this._nodes);
            // this.network.addEdge(this._edges);
    
            // editor.resize();
            console.log("TABS",this.tabs);
            this.Network.loadData(this.tabs[this.TabSettings?.tabs?.active] || DEFAULT_NETWORK());
    
            // Add all Addons as required.
            this.addGraphAddons(this.Network);
        }        

        return this.Network;
    }
    /**
     * Function, which is used to provide a 
     */
    public createTemplateOfSelectedElements() {
        const selected: IBasicTemplate<N,E> = {
            nodes: [],
            edges: [],
            type: "element"
        };

        const selection = this.Network.network.getSelection(true);

        selected.nodes = deepClone(this.Network.nodes.filter(item => selection.nodes.indexOf(item.id) !== -1));
        selected.edges = deepClone(this.Network.edges.filter(item => selection.edges.indexOf(item.id) !== -1));

        return selected;
    }

    public paste(
        template: IBasicTemplate<N,E>,
        position: {
            x: number,
            y: number
        },
        useExistingNodesForEdges: boolean) {

        const data = adaptPositions(adaptIDS(template, useExistingNodesForEdges), position);

        this.Network.addNode(data.nodes);
        this.Network.addEdge(data.edges);
    }

    /**
     * Function to Update the Data of a Node.
     * @param selection The Selected Node.
     */
    public async updateNode(selection: Array<IBaseNodeOptions<N>>) {
        const _self = this;

        /** Extract the Component, which should be used in the Prompt */
        let componentSelector = "default";
        if (selection.length > 0 && selection[0].editorComponentSelector) {
            componentSelector = selection[0].editorComponentSelector;
        }

        // const comp = this.editPanelDict.nodes[componentSelector];

        // if (comp) {
        //     /** Open the Window, with the Edit-Prompt */
        //     this.openEditInterface(this.editPanelDict.nodes[componentSelector], {
        //         inputTemplate: {
        //             nodes: selection,
        //             edges: [],
        //             type: 'elements'
        //         },
        //     },
        //         'Edit Node',
        //         (data) => {
        //             _self.network.updateNode(data.template.nodes);
        //         });
        // } else {
        //     this.layout.showToast('Editor is Trying to open an Unkown Edit-Component', 'error');
        // }
    }

    protected async _addNode(pos: { x: number, y: number }) {
         // Defaulty prevent self connections.
         if (this._template?.nodes.length > 0) {
            try {
                // Adapt its IDs. (Creates a Copy)
                let _template = adaptIDS(this._template);
                // Adapt its Position
                _template = adaptPositions(_template, pos);

                // Remove the Initial Label:
                delete _template.nodes[0].label;

                await this._editNodes(_template, "add");
            } catch(error) {
                console.error(error);
            }
        } else {
            this.LayoutHandler.showToast("Please Select a Node Before.", "error");
        }
    }

    public EditorSettings: GraphicalEditorComponentProps<N, E>
    public LayoutSettings: IDynamicLayoutProps<IGraphCallbackData<N,E>>
    public TabSettings: ITabProps;



    protected _template: IBasicTemplate<N,E>
    public selectTemplate(template: IBasicTemplate<N,E>){
        this._template = template;
    }

    public initializeGraph() {

        const _this = this;

        // Assign a default Network:
        const networkToRender: INetwork<N,E> = this.props.network || this.tabs[this.TabSettings?.tabs?.active] || DEFAULT_NETWORK();

        // Initally store the Network to Render
        this.tabs[networkToRender.id] = networkToRender;

        this.TabSettings = {
            onMount(item){
                _this.TabHandler = item;
            },

            async onNewTab() {
                // Function to create a new tab.
                try {

                    if (_this.TabHandler.tabs.length == 0) {
                        const tab = {
                            delteable: true,
                            id: generateID(),
                            label: "Unsaved Content"
                        };

                        await _this.TabHandler.createTab(tab);

                        // Store the Content of the tab:
                        _this.tabs[tab.id] = _this.Network.getData();
                    }

                    const label = await _this.LayoutHandler.getDialogData({
                        header: "Enter Network-Name",
                        content: {
                            component: "DynamicForm",
                            props: {
                                schema: {
                                    type: "string",
                                    description: "Name of the Element"
                                }
                            }
                        }
                    });

                    const id = Date.now().toString();
                    // const label = id;

                    _this.tabs[id] = DEFAULT_NETWORK();

                    return {
                        id,
                        label,
                        delteable: true
                    };
                } catch (error) {
                    return false;
                }
            },

            async onTabSelect(oldTab: ITab, newTab: ITab) {
                if (_this.Network) {
                    // Contains the Main Logic for selecting another tab
                    // Therefore 
                    if (oldTab) {
                        _this.tabs[oldTab.id] = _this.Network.getData();
                    }
                    // Use the content of the new Tab as rendering:
                    _this.Network.loadData(_this.tabs[newTab.id]);
                }
                return true;
            },

            async onTabDelete(tab: ITab, force: boolean) {
                // A Tab should be deleted

                if (force) {
                    return true;
                }

                try {
                    const saveData = await _this.LayoutHandler.getDialogData<boolean>({
                        header: "Close Tab",
                        content: {
                            component: (props) => {
                                return (<>
                                    <p>
                                        You are about to close the tab "{tab.label}". Do you want to save changes?
                                </p>
                                    <Button variant="success" onClick={_ => props.onSubmit(true)}>Yes</Button>{" "}
                                    <Button variant="danger" onClick={_ => props.onSubmit(false)}>No</Button>{" "}
                                    <Button variant="secondary" onClick={_ => props.onCancel(new Error("Canceled"))}>Cancel</Button>{" "}
                                </>);
                            },
                            props: {}
                        }
                    });

                    // The variable save Data contains the info, whether the user wants to save or discard the changes
                    return true;

                } catch (error) {
                    return false;
                }
            },

            async onNoTabSelected() {
                // Nothing to Display!

                if (_this.Network) {
                    console.log("No Tab");
                    // Use the content of the new Tab as rendering:
                    _this.Network.loadData(DEFAULT_NETWORK());
                }
                    
            },

            async onTabEdit(tab){
                const label = await _this.LayoutHandler.getDialogData<string>({
                    header: "Enter Network-Name",
                    content: {
                        component: "DynamicForm",
                        props: {
                            schema: {
                                type: "string",
                                description: "Name of the Element"
                            },
                            data: tab.label
                        }
                    }
                });

                tab.label = label;

                return tab;
            },

            tabs: {
                active: "-1",
                allowNewTabs: true,
                items: [],
            }
        };

        this.LayoutSettings = {
            components: [
                {
                    component() {

                        const graph = React.useRef(null);
                        // Similar to componentDidMount and componentDidUpdate:  
                        // Createa a Function, that if mounted defines the Network.
                        React.useEffect(() => {
                            
                            // Define the Network.
                            _this.initEditor(graph);

                            // Return a Function, that will destroy the network.
                            return () => {
                                _this.tabs[_this.TabSettings.tabs.active] = _this.Network.getData();
                                // Destroy the Network.
                                _this.Network.destroy();
                                _this.Network = null;
                                
                            };
                        });

                        return (<>
                            {React.createElement(TabEntry, { ..._this.TabSettings })}
                            <div ref={graph} style={{ height: "100%" }} onMouseEnter={e => {
                                // Enable Hotkeys
                                _this.LayoutHandler.hotkeysEnabled = true;
                            }} onMouseLeave={e => {
                                // Disable Hotkeys
                                _this.LayoutHandler.hotkeysEnabled = false;
                            }}></div>
                        </>
                        );
                    },
                    gridSettings: {
                        h: 6,
                        w: 11,
                        x: 5,
                        y: 0,
                        minW: 3,
                        minH: 3,
                    },
                    id: "main",
                    label: "Editor",
                    props: {
                    },
                    visible: true,
                    bg: "light",
                    text: "dark"
                },
                {
                    component(props: {selection: ISelection<IBasicTemplate<N,E>>}){
                        return (<Selection<IBasicTemplate<N,E>> selection={props.selection} allowUserSelect={true} onItemSelected={template =>_this.selectTemplate(template)}></Selection>);                        
                    },
                    gridSettings: {
                        h: 3,
                        w: 4,
                        x: 0,
                        y: 0,
                        maxW: 5,
                    },
                    id: "selection",
                    label: "Selection",
                    props: {
                        selection: generateDefaultSelection()
                    },
                    hideable: true,
                    bg:"light",
                    visible: true,
                    text: "dark",
                    showLabel: true,
                },
                {
                    component() {
                        const [img, setImg] = React.useState();
                        const [pos, setPos] = React.useState<{left: number, top: number, height: number, width: number}>({
                            left: 0,
                            top: 0,
                            width: 0,
                            height: 0,
                        });
                        const [style, setStyle] = React.useState<any>();
                        const radar = React.useRef(null);
                        const image = React.useRef(null);


                        // Similar to componentDidMount and componentDidUpdate:  
                        // Createa a Function, that if mounted subscribe to changes
                        // of the Nework.
                        React.useEffect(() => {

                            const wait = () => {
                                return new Promise((resolve, reject) => {
                                    _this.Network.once("afterDrawing", resolve);
                                });
                            };

                            const updateImage = async () => {

                                // Store the current View Settings
                                const scale = _this.Network.network.getScale();
                                const translate = _this.Network.network.getViewPosition();

                                // Zoom out for the Image.
                                _this.Network.fit({animation: false});

                                // Wait for the Network to Redraw
                                await wait();

                                const canvas = _this.Network.network.body.container.getElementsByTagName("canvas")[0];
                                const imgData = canvas.toDataURL("image/png");
                                setImg(imgData);

                                /**
                                 * Extract the Widht and Height
                                 */
                                const {
                                    clientWidth,
                                    clientHeight
                                } = _this.Network.network.body.container;                                

                                // Store the Position.
                                setPos({
                                    left: translate.x - clientWidth/ scale / 2,
                                    top: translate.y - clientHeight/ scale / 2,
                                    width: clientWidth/ scale,
                                    height: clientHeight / scale,
                                });

                                // Use inital View
                                _this.Network.network.moveTo({
                                    position: translate,
                                    scale: scale,
                                    animation: false
                                });
                            };

                            const getScaling = (start,size, current, scale = true) => {
                                if (scale){
                                    return Math.min(1, Math.max(0, (current - start) / size));
                                } 
                                return (current - start) / size;
                            };

                            /** 
                             * Function to draw the current Positon of the View.
                             */
                            const drawRadar = () => {
                                if (radar?.current && image?.current){

                                    const displayedWidth = image.current.clientWidth;
                                    const displayedHeight = image.current.clientHeight;

                                    /**
                                     * Extract the Widht and Height
                                     */
                                    const {
                                        clientWidth,
                                        clientHeight
                                    } = _this.Network.network.body.container;

                                    // Determine visible Nodes:
                                    const scale = _this.Network.network.getScale();
                                    const translate = _this.Network.network.getViewPosition();

                                    // Contains the Left Position of the View.
                                    // The following Transformation is used:
                                    //   _____
                                    //  |(top,left)-Original = 0,0
                                    //  |     ___
                                    //  |    |(top, left of the View)
                                    // 

                                    const current = {
                                        left: translate.x - clientWidth/ scale / 2,
                                        top: translate.y - clientHeight/ scale / 2,
                                        width: clientWidth/ scale,
                                        height: clientHeight / scale,
                                    };

                                    const top = getScaling(pos.top, pos.height, current.top);
                                    const bottom = getScaling(pos.top, pos.height, current.top + current.height);
                                    const height = bottom-top;
                                    const left = getScaling(pos.left, pos.width, current.left);
                                    const right = getScaling(pos.left, pos.width, current.left + current.width);
                                    const width = right - left;

                                    setStyle({
                                        position: "absolute", 
                                        backgroundColor: "rgba(16, 84, 154, 0.26)", 
                                        top: (image.current.offsetTop + top * displayedHeight).toString() + "px", 
                                        left: (image.current.offsetLeft + left * displayedWidth).toString() + "px",
                                        height: (height* displayedHeight).toString() + "px",
                                        width: Math.max(0,width * displayedWidth- image.current.offsetLeft).toString() + "px"
                                    });
                                }
                                
                            };

                            const listeners: Partial<{[K in IEvent]: any}> = {
                                "dataUpdate": updateImage,
                                "afterDrawing" : drawRadar
                            };

                            const subscriptions: Array<() => void> = [];

                            if (_this.Network) {
                                for (const key in listeners){
                                    subscriptions.push(_this.Network.on(key as IEvent, listeners[key]));
                                }
                            }
                            // Return a Function, that will destroy the network.
                            return () => {
                                for (const unsubscribe of subscriptions){
                                    unsubscribe();
                                }
                            };
                        });

                        return (
                        <div style={{overflow: "hidden"}}>
                            <img ref={image} src={img} style={{position: "absolute", maxHeight:"100%", maxWidth: "100%"}}></img>
                            <div ref={radar} style={style}></div>
                        </div>
                        );
                },
                    gridSettings: {
                        h: 2,
                        w: 4,
                        x: 0,
                        y: 4,
                        maxW: 5,
                    },
                    id: "minimap",
                    label: "Minimap",
                    props: {},
                    hideable: true,
                    bg:"light",
                    visible: true,
                    text: "dark",
                    showLabel: true,
                },
                {
                    component(){
                        return (<>Preview</>);                        
                    },
                    gridSettings: {
                        h: 3,
                        w: 3,
                        x: 3,
                        y: 5,
                    },
                    id: "preview",
                    label: "Preview",
                    props: {},
                    hideable: true,
                    bg:"light",
                    visible: false,
                    text: "dark"
                },
            ],
            layoutSettings: {
                width: process.browser ? window.innerWidth : 1920,
                autoSize: false,
                preventCollision: false,
                cols: 15,
                compactType: "horizontal",
                onLayoutChange(){
                    if (_this.Network) {
                        _this.Network.resize();
                    }
                }
            },
            generateData(){
                // Get the Current Selection.
                const selection = _this.Network.network.getSelection(true);

                const ret: IGraphCallbackData<N,E> = {
                    network: _this.Network,
                    layout: _this.LayoutHandler,
                    selectedEdges: selection.edges,
                    selectedNodes: selection.nodes,
                    tabs: _this.TabHandler
                };

                Object.defineProperty(ret, "network", {
                    get(){
                        return _this.Network;
                    },
                });

                Object.defineProperty(ret, "layout", {
                    get(){
                        return _this.LayoutHandler;
                    },
                });

                return ret;
            },
            onResize() {
                if (_this.Network) {
                    _this.Network.resize();
                }
            },
            onMount(layout){
                _this.LayoutHandler = layout;
            },
            toolbar: defaultToolbar(),
            hotkeys: defaultHotkeys()
        };

        return this.LayoutSettings;
    }

    public requestRerender() {
        this.setState({
            update: null
        });
    }

    public componentDidMount() {
        const _this = this;
    }

    public render() {
        this.initializeGraph();
        return React.createElement(DynamicLayout, {... this.LayoutSettings});
    }
}

export default GraphicalEditorComponent;