import * as React from 'react';
import { LayoutProps, ModalSettings } from '../layout/layout';
import Layout from '../layout/layout'; 

import { IVisjsOptions } from './interfaces/IVisjsOptions';
import { INetwork } from './interfaces/INetwork';
import { IUndoRedoGraph } from './interfaces/IGraph';

// Graph related imports:
import { UndoRedoGraph } from './graph';
import { generateGraphOptions } from './defaults/default.graph-options';

// Import Addons:
import { makeMeMultiSelect } from './addons/selectionbox.extension';
import DynamicRenderer from '../dynamic/dynamicRenderer';
import { rgetattr, rsetattr } from '../../../lib/helpers/objectMethods';
import { Button } from 'react-bootstrap';


export interface GraphicalEditorComponentProps<N,E> extends Partial<LayoutProps<any>>{
    options?: IVisjsOptions
    network?: INetwork<N,E>
}

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 layout: Layout<any>;

    constructor(props) {
        super(props);
    }

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

    public addGraphAddons(network: IUndoRedoGraph<N,E>){
        makeMeMultiSelect(network);
    }

    // public async initEditor() {
    //     const _this = this;

    //     /** Generate the Default Callback for adding a Node */
    //     rsetattr(
    //         this._visjsOptions,
    //         'manipulation.addEdge',
    //         generateAddFunction(_this, (data, callback) => {

    //             /** Test if an Edge-Template exists */
    //             if (_this.template.type === 'elements' && _this.template.edges.length === 1 && _this.template.nodes.length === 0) {
    //                 const edge = Object.assign(
    //                     _this.template.edges[0],
    //                     data
    //                 );

    //                 if (typeof _this.options.addEdgeCallback === 'function') {
    //                     _this.options.addEdgeCallback(edge, callback);
    //                 } else {
    //                     callback(edge);
    //                 }
    //             }
    //         }
    //         )
    //     );

    //     if (!rgetattr(this._visjsOptions, 'manipulation.enabled', false)) {
    //         rsetattr(this._visjsOptions, 'manipulation.enabled', false);
    //     }
      
    //     const editor = initEditor<N,E,D, BaseGraphEditor<N,E,D>>({
    //         component: this,
    //         element: this.layout.panels.main,
    //         networkOptions: this.visjsOptions,
    //         renderMinimap: false            
    //     });

    //     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 selection = getSubElements(this.network, false);
    //                 _this.updateNode(selection);
    //             }
    //         } 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);
    //         }
    //     }
        
    //     // Make the Default behaviour adding new Nodes
    //     this.network.on('doubleClick', (params) => {
    //         if (this.options.enableEditing){
    //             // 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 {
                
    //         } catch (e) {
                
    //         }
    //     });
        
    //     this.network.on('onpaste', async event => {
    //         console.log(event)
    //         try {
    //             const data = await _this.readDataFromClipboard()
    //             _this.paste(parseWithFunctions(data), _this.network.network.DOMtoCanvas(_this.mousePos), false);
    //             _this.zemaService.showToast('success', 'Clipboard', 'Copied Content to Clipboard');
    //         } catch (e) {
    //             _this.zemaService.showToast('warning', 'Clipboard', 'Failed Copying To Clipboard');
    //         }

    //     });

    //     let _mousePosOnContextMenu = this.mousePos;
    //     let rightClickActions: IRigthClickActions<ICallbackData<N,E>> = [];
    //     const _contextHandler = (params) => {
    //         let nodeID = null;
    //         if (params.nodes.length === 1) {
    //             nodeID = _this.network.network.getNodeAt(params.pointer.DOM);
    //             if (!nodeID)
    //                 nodeID = params.nodes[0];
    //         }

    //         let edgeID = null;
    //         if (params.edges.length === 1) {
    //             edgeID = params.edges[0];
    //         }

    //         // If nothing is selected, try to select the element,
    //         // which is underneath the Pointer
    //         if (params.nodes.length === 0) {
    //             nodeID = _this.network.network.getNodeAt(params.pointer.DOM);

    //             // Test if a Node is underneath the Pointer
    //             if (nodeID) {
    //                 // Make shure the Element is selected
    //                 _this.network.network.selectNodes([nodeID]);
    //             }
    //         }

    //         // If nothing is selected, try to select the element,
    //         // which is underneath the Pointer
    //         if (params.edges.length === 0) {
    //             edgeID = _this.network.network.getEdgeAt(params.pointer.DOM);

    //             // Test if a Node is underneath the Pointer
    //             if (edgeID) {
    //                 // Make shure the Element is selected
    //                 _this.network.network.selectEdges([edgeID]);
    //             }
    //         }

    //         // Test whether multiple Nodes or just a single Node is selected.
    //         if (nodeID) {
    //             // A single Node is selected
    //             rightClickActions = _this.contextMenuGenerator.node(_this, params.pointer.DOM, nodeID);
    //         } else if (params.nodes.length > 1) {
    //             // The Default Right-Clickmenu must be selected
    //             rightClickActions = _this.contextMenuGenerator.default(_this, params.pointer.DOM, params.nodes, params.edges);
    //         } else if (edgeID) {
    //             // Only 1 Edge is selected
    //             rightClickActions = _this.contextMenuGenerator.edge(_this, params.pointer.DOM, edgeID);
    //         } else {
    //             rightClickActions = _this.contextMenuGenerator.background(_this, params.pointer.DOM);
    //         }
    //     }

    //     this.network.on('oncontext', (params) => {
    //         if (_this.options.enableContextMenu){
    //             // Make shure the Context-Menu is only Opened, if
    //             // The event isnt prevented
    //             if (params.event.prevent) {
    //                 return;
    //             }

    //             // Call creating the Menu Entries.
    //             _contextHandler(params)

    //             // Decide, whether an Element is selected or not
    //             params.event.preventDefault();

    //             // Store the current Position
    //             _mousePosOnContextMenu = _this.mousePos;

    //             // Check after a View seconds whether the Mouse has been move            
    //             // setTimeout(_contextHandler, 200, params);
    //             setTimeout(() => {
    //                 if (Math.abs(_this.mousePos.x - _mousePosOnContextMenu.x) < 5 && Math.abs(_this.mousePos.y - _mousePosOnContextMenu.y) < 5) {
    //                     if (rightClickActions.length > 0) {
    //                         _this.layout.openContextMenu(params.event, rightClickActions);
    //                     } else {
    //                         _this.network.network.unselectAll();
    //                     }
    //                 } else {
    //                     rightClickActions = [];
    //                 }
    //             }, 200)
    //         }            
    //     });

    //     // Make shure the Sidepanel is working correctly
    //     this.network.on('select', (params) => {
    //         if (_this.options.enableEditing){
    //             if (_this.options.editOnSelect && _this.options.hidePanelOnDeselect && params.nodes.length === 0 && params.edges.length === 0){
    //                 _this.layout.panels.right.hide();
    //             } else if (_this.options.editOnSelect){
    //                 editItemIfPossible(params);
    //             }
    //         }
    //     });

    //     enableClusterPreview(this);

    //     this.network.addNode(this._nodes);
    //     this.network.addEdge(this._edges);

    //     editor.resize();        
    // }

    public initializeGraph(){

        const _this = this;

        // Assign a default Network:
        const networkToRender = this.props.network || {
            nodes: [],
            edges: [],
            clusters: [],
            version: "20200713",
            name: "new",
            id: "new"
        }
        
        // Initally store the Network to Render
        this.tabs[networkToRender.id] = networkToRender;

        // Define the Options
        const visjsOptions = this.props.options || generateGraphOptions();

        const settings: GraphicalEditorComponentProps<N,E> = {
            allowUserSelect: true,

            network: networkToRender,

            onItemSelected(item){

            },

            onMount(ref, layout){
                // Define the Network.
                _this.network = new UndoRedoGraph(ref, visjsOptions);

                // Load the Networ:
                _this.network.loadData(networkToRender);

                _this.layout = layout;

                // Resize the Network.
                // _this.network.resize();
            },

            onResize(){
                if (_this.network) {
                    _this.network.resize();
                }
            },

            async onNewTab(){
                // Function to create a new tab.
                try {
                    const label = await _this.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] = {
                        nodes: [{
                            id: 'test',
                            label
                        }],
                        edges: [],
                        clusters: [],
                        version: "20200713",
                        name: label,
                        id: id
                    };

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

            async onTabSelect(oldTabId: string, newTabId: string){
                // Contains the Main Logic for selecting another tab
                // Therefore 
                _this.tabs[oldTabId] = _this.network.getData();

                // Use the content of the new Tab as rendering:
                _this.network.loadData(_this.tabs[newTabId]);

                return true;                
            },

            async onTabDelete(tabId: string) {
                // A Tab should be deleted

                try {
                    const saveData = await _this.getDialogData<boolean>({
                        header: 'Close Tab',
                        content: {
                            component: (props) => {
                                return (<>
                                    <p>
                                        You are about to close the tab. 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!

                // Use the content of the new Tab as rendering:
                _this.network.loadData({
                    nodes: [],
                    edges: [],
                    clusters: [],
                    version: "20200713",
                    name: 'empty',
                    id: 'empty'
                });

            },

            onUnmount(){
                if (_this.network){
                    // Destroy the Network.
                    _this.network.destroy();
                }
            },

            tabs: {
                active: 'start',
                items: [{
                    id:  networkToRender.id,
                    label: networkToRender.name,
                    delteable: true
                }],
                allowNewTabs: true
            }
        };

        return settings;
    }

    public getDialogData<T = any>(settings: ModalSettings) {
        const _this = this;
        let close: () => void;

        return new Promise<T>((resolve, reject) => {
            // Adapt the Settings:
            const _onSubmit = rgetattr<(data) => Promise<void>>(settings, 'content.props.onSubmit', async (data) => { });
            const _onCancel = rgetattr<(err) => Promise<void>>(settings, 'content.props.onCancel', async (err) => { });

            const onSubmit = async (data) => {
                close();
                _onSubmit(data);
                resolve(data);
            }

            const onCancel = async (data) => {
                close();
                _onCancel(data);
                reject(data);
            }

            rsetattr(settings, 'content.props.onSubmit', onSubmit);
            rsetattr(settings, 'content.props.onCancel', onCancel);

            close = this.layout.openPopup(settings)
        });
    }

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

    public componentDidMount() {
        const _this = this;

        console.log('editor',this.props.children)
    }


    public render() {

        const settings = this.initializeGraph();



        const template: {
            groupName: string,
            items: {
                label: string,
                keywords: string,
                item: string,
                id: string | number,
            }[]
        }[] = [
                {
                    groupName: 'GRP 1',
                    items: [
                        {
                            item: 'hello',
                            keywords: '1',
                            label: 'Hello',
                            id: 1,
                        },
                        {
                            item: 'world',
                            keywords: '2',
                            label: 'world',
                            id: 2,

                        }, {
                            item: '!',
                            keywords: '3',
                            label: '!',
                            id: 3,
                        }

                    ]
                },
                {
                    groupName: 'GRP 2',
                    items: [
                        {
                            item: 'world',
                            keywords: '4',
                            label: 'world',
                            id: 4,
                        }
                    ]
                }
            ];

        return (
            <>
                <Layout
                    selection={template}
                    allowUserSelect
                    onMount={settings.onMount}                    
                    onResize={settings.onResize}
                    onNewTab={settings.onNewTab}
                    onTabSelect={settings.onTabSelect}
                    onUnmount={settings.onUnmount}
                    onNoTabSelected={settings.onNoTabSelected}
                    onTabDelete={settings.onTabDelete}
                    tabs={settings.tabs}
                ></Layout>
            </>
        )
    }
}

export default GraphicalEditorComponent;