import React from "react";
import PropTypes, { InferType } from "prop-types";
import { State } from "../state";
import * as Interactions from "../interactions";
import { Graph } from "../structures/graph/graph";
import ForceGraph from "force-graph";
import { loadGraphJson } from "../../../datasets";

export class Editor extends React.PureComponent<
    InferType<typeof Editor.propTypes>,
    InferType<typeof Editor.stateTypes>
> {
    static propTypes = {};

    static stateTypes = {
        state: State,
        graph: Graph,
        renderer: PropTypes.any,
    };

    // TODO: Not a long term solution!
    public static globalState: State;
    public static globalGraph: Graph;
    public static globalRenderer: any;

    constructor(props: InferType<typeof Editor.propTypes>) {
        super(props);

        Interactions.initInteractions();
    }

    /**
     * Loads a space from the database to the editor.
     * @param spaceId Id of space to load.
     * @returns Promise with boolean value that is true, if successful.
     */
    public loadSpace(spaceId: string): any {
        return loadGraphJson(spaceId).then(this.loadGraph);
    }

    /**
     * Loads another graph based on the data supplied. Note: Naming currently suggests that this only loads a GRAPH, not a SPACE. Needs further work and implementation to see if that makes sense or not.
     * @param data Serialized graph data.
     * @returns True, if successful.
     */
    public loadGraph(data: any): boolean {
        // Create global objects
        const state = new State();
        const graph = Graph.parse(data);

        // Is valid and parsed successfully?
        if (graph == undefined) {
            return false;
        }

        // Create renderer
        const renderTarget = document.getElementById("2d-graph");
        const renderWidth = renderTarget.offsetWidth;
        const renderer = ForceGraph()(renderTarget);

        // Subscribe to interactions
        renderer
            .height(600)
            .width(renderWidth)
            .graphData(graph.data)
            .nodeLabel("label")
            .linkColor((link) => state.linkColor(link))
            .nodeColor((node) => state.nodeColor(node))
            .onNodeClick((node) => state.onNodeClick(node))
            .onNodeDragEnd((node, translate) =>
                state.onNodeDragEnd(node, translate)
            )
            .autoPauseRedraw(false) // keep redrawing after engine has stopped
            .linkWidth((link) => state.linkWidth(link))
            .linkDirectionalParticles(state.linkDirectionalParticles())
            .linkDirectionalParticleWidth((link) =>
                state.linkDirectionalParticleWidth(link)
            )
            .onBackgroundClick((event) =>
                state.onBackgroundClick(event, this.extractPositions(event))
            )
            .nodeCanvasObjectMode((node) => state.nodeCanvasObjectMode(node))
            .nodeCanvasObject((node, ctx, globalScale) =>
                state.nodeCanvasObject(node, ctx, globalScale)
            )
            .linkCanvasObjectMode((link) => state.linkCanvasObjectMode(link))
            .linkCanvasObject((link, ctx, globalScale) =>
                state.linkCanvasObject(link, ctx, globalScale)
            )
            .onLinkClick((link) => state.onLinkClick(link));

        // Connect update event
        graph.onChangeCallbacks.push((data) => {
            renderer.graphData(data);
        });

        // Set as new state
        this.setState({
            state: state,
            graph: graph,
            renderer: renderer,
        });
        Editor.globalState = state;
        Editor.globalGraph = graph;
        Editor.globalRenderer = renderer;

        // Subscribe to global key-press events
        document.onkeydown = this.state.state.onKeyDown;
        document.onkeyup = this.state.state.onKeyUp;

        return true;
    }

    /**
     * Calculates the corresponding coordinates for a click event for easier further processing.
     * @param event The corresponding click event.
     * @returns Coordinates in graph and coordinates in browser window.
     */
    private extractPositions(event: any): {
        graph: { x: number; y: number };
        window: { x: number; y: number };
    } {
        return {
            graph: this.state.renderer.screen2GraphCoords(
                event.layerX,
                event.layerY
            ),
            window: { x: event.clientX, y: event.clientY },
        };
    }

    render(): React.ReactNode {
        // The id "ks-editor" indicates, that the javascript associated with this should automatically be executed
        return (
            <div id="ks-editor">
                <h1>Interface</h1>
                <div id="box-select-layer">
                    <div id="2d-graph"></div>
                </div>
                <section id="toolbar"></section>
                <section id="tool-menu">
                    <div id="delete-menu" className="hidden">
                        <p>
                            Drag and drop while pressing SHIFT to delete all the
                            nodes that are being selected.
                        </p>
                    </div>
                    <div id="collect-menu" className="hidden">
                        <h3>Collected items</h3>
                        <button id="clear-collection">Clear</button>
                        <ul id="selected-items"></ul>
                    </div>
                    <div id="select-menu" className="">
                        <p id="nothing-selected">Nothing selected</p>
                        <div id="node-selected" className="hidden">
                            <label htmlFor="node-name" hidden>
                                Name
                            </label>
                            <br />
                            <input
                                type="text"
                                id="node-name"
                                name="node-name"
                                placeholder="Enter name"
                                className="bottom-space"
                            ></input>
                            <br />
                            <label htmlFor="node-description">
                                Description
                            </label>
                            <br />
                            <textarea
                                id="node-description"
                                name="node-description"
                                className="bottom-space"
                            ></textarea>
                            <br />
                            <label htmlFor="node-image">Node Image</label>
                            <br />
                            <img
                                id="node-image-preview"
                                className="preview-image"
                                src=""
                            />
                            <br />
                            <input
                                type="text"
                                id="node-image"
                                name="node-image"
                                placeholder="Enter file name or URL"
                                className="bottom-space"
                            />
                            <br />
                            <label htmlFor="node-detail-image">
                                Info Image
                            </label>
                            <br />
                            <img
                                id="node-detail-image-preview"
                                className="preview-image"
                                src=""
                            />
                            <br />
                            <input
                                type="text"
                                id="node-detail-image"
                                name="node-detail-image"
                                placeholder="Enter file name or URL"
                                className="bottom-space"
                            />
                            <br />
                            <label htmlFor="node-type">Type</label>
                            <br />
                            <select
                                id="node-type"
                                name="node-type"
                                className="bottom-space"
                            >
                                <option value="Vorlesung">Vorlesung</option>
                                <option value="Algorithmus">Algorithmus</option>
                                <option value="Definition">Definition</option>
                                <option value="Beispiel">Beispiel</option>
                                <option value="Übung">Übung</option>
                                <option value="Kapitel">Kapitel</option>
                            </select>
                            <br />
                            <label htmlFor="node-video">Video</label>
                            <br />
                            <input
                                type="text"
                                placeholder="Video URL"
                                id="node-video"
                                name="node-video"
                            ></input>
                            <br />
                            <label htmlFor="node-references">
                                References
                            </label>{" "}
                            <small>One URL per line</small>
                            <br />
                            <textarea
                                id="node-references"
                                name="node-references"
                                className="bottom-space"
                            ></textarea>
                        </div>
                        <div id="link-selected" className="hidden">
                            <h3 id="link-name"></h3>
                        </div>
                    </div>
                    <div id="settings-menu" className="hidden">
                        <label htmlFor="label-toggle" className="bottom-space">
                            <input
                                type="checkbox"
                                checked
                                id="label-toggle"
                                name="label-toggle"
                            ></input>
                            Show labels in graph
                        </label>
                        <br />
                        <br />

                        <h3>Space</h3>
                        <label htmlFor="space-id-select">Currently open</label>
                        <br />
                        <select
                            id="space-id-select"
                            name="space-id-select"
                            className="bottom-space"
                        ></select>

                        <br />
                        <br />

                        <h3>Physics Simulation</h3>

                        <button
                            id="reanimate-button"
                            name="reanimate-button"
                            className="bottom-space"
                        >
                            Re-simulate
                        </button>

                        <br />

                        <label htmlFor="stop-physics-delay">
                            Amount of time [in seconds] after which the physics
                            simulation is stopped
                        </label>
                        <br />
                        <input
                            type="number"
                            onKeyPress={(event) =>
                                (event.charCode != 8 && event.charCode == 0) ||
                                (event.charCode >= 48 && event.charCode <= 57)
                            }
                            value="5"
                            id="stop-physics-delay"
                            name="stop-physics-delay"
                            className="small-width"
                        ></input>
                        <br />
                        <br />

                        <h3>Import Space</h3>
                        <label htmlFor="import-space-area">Space JSON</label>
                        <br />
                        <textarea
                            id="import-space-area"
                            name="import-space-area"
                            className="bottom-space"
                        ></textarea>
                        <br />
                        <label htmlFor="import-space-name-text">
                            Space Name
                        </label>
                        <br />
                        <input
                            type="text"
                            id="import-space-name-text"
                            name="import-space-name-text"
                            className="bottom-space"
                        ></input>
                        <br />
                        <button
                            id="import-space-btn"
                            name="import-space-btn"
                            className="bottom-space"
                        >
                            Import
                        </button>

                        <br />
                        <br />
                    </div>
                </section>
            </div>
        );
    }
}