import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { DocumentManager } from '../util/DocumentManager';
import { CompileScript, Transformer, ts } from '../util/Scripting';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { SettingsManager } from '../util/SettingsManager';
import { undoable } from '../util/UndoManager';
import { DocumentIconContainer } from './nodes/DocumentIcon';
import { OverlayView } from './OverlayView';
import './ScriptingRepl.scss';
@observer
export class ScriptingObjectDisplay extends React.Component<{ scrollToBottom: () => void; value: { [key: string]: any }; name?: string }> {
    @observable collapsed = true;
    @action
    toggle = () => {
        this.collapsed = !this.collapsed;
        this.props.scrollToBottom();
    };
    render() {
        const val = this.props.value;
        const proto = Object.getPrototypeOf(val);
        const name = (proto && proto.constructor && proto.constructor.name) || String(val);
        const title = this.props.name ? (
            <>
                {this.props.name} : 
                {name}
            >
        ) : (
            name
        );
        if (this.collapsed) {
            return (
                
                    
                        
                    
                    {title} (+{Object.keys(val).length})
                
            );
        } else {
            return (
                
                    
                        
                            
                        
                        {title}
                    
                    
                        {Object.keys(val).map(key => (
                            
                        ))}
                    
                 
            );
        }
    }
}
@observer
export class ScriptingValueDisplay extends React.Component<{ scrollToBottom: () => void; value: any; name?: string }> {
    render() {
        const val = this.props.name ? this.props.value[this.props.name] : this.props.value;
        if (typeof val === 'object') {
            return ;
        } else if (typeof val === 'function') {
            const name = '[Function]';
            const title = this.props.name ? (
                <>
                    {this.props.name} : 
                    {name}
                >
            ) : (
                name
            );
            return {title}
;
        } else {
            const name = String(val);
            const title = this.props.name ? (
                <>
                    {this.props.name} : 
                    {name}
                >
            ) : (
                name
            );
            return {title}
;
        }
    }
}
@observer
export class ScriptingRepl extends React.Component {
    @observable private commands: { command: string; result: any }[] = [];
    private commandsHistory: string[] = [];
    @observable private commandString: string = '';
    private commandBuffer: string = '';
    @observable private historyIndex: number = -1;
    private commandsRef = React.createRef();
    private args: any = {};
    getTransformer = (): Transformer => {
        return {
            transformer: context => {
                const knownVars: { [name: string]: number } = {};
                const usedDocuments: number[] = [];
                ScriptingGlobals.getGlobals().forEach((global: any) => (knownVars[global] = 1));
                return root => {
                    function visit(node: ts.Node) {
                        let skip = false;
                        if (ts.isIdentifier(node)) {
                            if (ts.isParameter(node.parent)) {
                                skip = true;
                                knownVars[node.text] = 1;
                            }
                        }
                        node = ts.visitEachChild(node, visit, context);
                        if (ts.isIdentifier(node)) {
                            const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
                            const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
                            if (ts.isParameter(node.parent)) {
                                // delete knownVars[node.text];
                            } else if (isntPropAccess && isntPropAssign && !(node.text in knownVars) && !(node.text in globalThis)) {
                                const match = node.text.match(/d([0-9]+)/);
                                if (match) {
                                    const m = parseInt(match[1]);
                                    usedDocuments.push(m);
                                } else {
                                    return ts.createPropertyAccess(ts.createIdentifier('args'), node);
                                }
                            }
                        }
                        return node;
                    }
                    return ts.visitNode(root, visit);
                };
            },
        };
    };
    @action
    onKeyDown = (e: React.KeyboardEvent) => {
        let stopProp = true;
        switch (e.key) {
            case 'Enter': {
                e.stopPropagation();
                const docGlobals: { [name: string]: any } = {};
                DocumentManager.Instance.DocumentViews.forEach((dv, i) => (docGlobals[`d${i}`] = dv.props.Document));
                const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals);
                const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: 'any' }, transformer: this.getTransformer(), globals });
                if (!script.compiled) {
                    this.commands.push({ command: this.commandString, result: script.errors });
                    return;
                }
                const result = undoable(() => script.run({ args: this.args }, e => this.commands.push({ command: this.commandString, result: e.toString() })), 'run:' + this.commandString)();
                if (result.success) {
                    this.commands.push({ command: this.commandString, result: result.result });
                    this.commandsHistory.push(this.commandString);
                    this.maybeScrollToBottom();
                    this.commandString = '';
                    this.commandBuffer = '';
                    this.historyIndex = -1;
                }
                break;
            }
            case 'ArrowUp': {
                if (this.historyIndex < this.commands.length - 1) {
                    this.historyIndex++;
                    if (this.historyIndex === 0) {
                        this.commandBuffer = this.commandString;
                    }
                    this.commandString = this.commandsHistory[this.commands.length - 1 - this.historyIndex];
                }
                break;
            }
            case 'ArrowDown': {
                if (this.historyIndex >= 0) {
                    this.historyIndex--;
                    if (this.historyIndex === -1) {
                        this.commandString = this.commandBuffer;
                        this.commandBuffer = '';
                    } else {
                        this.commandString = this.commandsHistory[this.commands.length - 1 - this.historyIndex];
                    }
                }
                break;
            }
            default:
                stopProp = false;
                break;
        }
        if (stopProp) {
            e.stopPropagation();
            e.preventDefault();
        }
    };
    @action
    onChange = (e: React.ChangeEvent) => {
        this.commandString = e.target.value;
    };
    private shouldScroll: boolean = false;
    private maybeScrollToBottom = () => {
        const ele = this.commandsRef.current;
        if (ele && ele.scrollTop === ele.scrollHeight - ele.offsetHeight) {
            this.shouldScroll = true;
            this.forceUpdate();
        }
    };
    private scrollToBottom() {
        const ele = this.commandsRef.current;
        ele && ele.scroll({ behavior: 'auto', top: ele.scrollHeight });
    }
    componentDidUpdate() {
        if (this.shouldScroll) {
            this.shouldScroll = false;
            this.scrollToBottom();
        }
    }
    overlayDisposer?: () => void;
    onFocus = () => {
        this.overlayDisposer?.();
        this.overlayDisposer = OverlayView.Instance.addElement(, { x: 0, y: 0 });
    };
    onBlur = () => this.overlayDisposer?.();
    render() {
        return (
            
        );
    }
}