import {Notebook, INotebookModel} from "@jupyterlab/notebook";
import {OutputArea} from "@jupyterlab/outputarea";
import {CodeCell, isCodeCellModel} from "@jupyterlab/cells";
import {DocumentRegistry} from '@jupyterlab/docregistry';
import {JSONObject} from "@lumino/coreutils";
export class NotebookRunner {
    constructor() {
    }

    private static getMode(metadata: any): string {
        let vadalog = metadata.get("vadalog");
        if (vadalog !== undefined) {
            vadalog = vadalog as JSONObject;
            let mode = vadalog["mode"];
            if (mode !== undefined)
                return "" + mode;
        }
        return undefined;
    }
    /**
     * Handles a reduced version of the notebook's JSON data to the kernel and executes the code on the output area
     * @param widget the Notebook object to execute on
     * @param mode string which indicates how notebook gets executed by kernel
     * @param context the context for executing the code
     */
    public run(widget: Notebook, mode: string, forceMode: boolean, context: DocumentRegistry.IContext<INotebookModel>): Promise<boolean> {
        // something went wrong if there is no active code cell
        if (!widget.activeCell || !isCodeCellModel(widget.activeCell.model)) {
            return Promise.resolve(false);
        }
        let sendData = Object();
        let activeCell = (<CodeCell>widget.activeCell);
        let activeCellIndex = widget.activeCellIndex;

        // Magic directives functionality removed

        let cellMetadata = activeCell.model.metadata;
        let cellMode = NotebookRunner.getMode(cellMetadata);
        console.log("runner, cell mode = " + mode);

        if (!forceMode && cellMode !== undefined)
            mode = cellMode;

        let metadata = JSON.parse(JSON.stringify(cellMetadata));
        let vadalog_meta = metadata["vadalog"];
        if (vadalog_meta !== undefined) {
            if (vadalog_meta["vadalog_endpoint"] !== undefined)
                sendData["vadalog_endpoint"] = vadalog_meta["vadalog_endpoint"];
            // bmp_endpoint removed - no longer used
            if (vadalog_meta["response_timeout"] !== undefined)
                sendData["response_timeout"] = vadalog_meta["response_timeout"];

            if (mode == "run" || mode == "runparammodule") {
                if (vadalog_meta["parameters"] !== undefined)
                    sendData["parameters"] = vadalog_meta["parameters"];
            }
            if (mode == "runparammodule") {
                if (vadalog_meta["parallelisation"] !== undefined)
                    sendData["parallelisation"] = vadalog_meta["parallelisation"];
            }
            if (mode == "getMetaProgram") {
                if (vadalog_meta["program_id"] !== undefined)
                    sendData["program_id"] = vadalog_meta["program_id"];
            }
            if (mode == "explain" || mode == "explainrewriting") {
                if (vadalog_meta["atoms"] !== undefined)
                    sendData["atoms"] = vadalog_meta["atoms"];
            }
        }

        let session = context.sessionContext.session;

        // check if there is a notebook model, kernel and valid code
        if (!widget.model || !session || !session.kernel || activeCell.model.sharedModel.getSource().trim().length === 0) {
            activeCell.model.executionCount = null;
            activeCell.model.outputs.clear();
            return Promise.resolve(false);
        }
        widget.mode = 'command';

        let otherCellContents: string[] = [];
        let otherCells: CodeCell[] = [];
        let selected: CodeCell[] = [];

        for (let i=0; i<widget.widgets.length; ++i) {
          let child = widget.widgets[i];
          if (activeCellIndex != i && isCodeCellModel(child.model)) {
              otherCells.push(child as CodeCell);
          }
          if (widget.isSelectedOrActive(child)) {
            selected.push(child as CodeCell);
          }
        };

        for (let otherCell of otherCells) {
            otherCellContents.push(otherCell.model.sharedModel.getSource());
        }
        
        sendData["code"] = activeCell.model.sharedModel.getSource();
        sendData["other_code"] = otherCellContents;
        sendData["mode"] = mode;

        sendData = JSON.stringify(sendData);

        // preparations for execution
        activeCell.model.executionCount = null;
        activeCell.outputHidden = false;
        
        // Set initial prompt and prepare for execution
        const kernelName = session.kernel?.name || '';
        const isVadalogKernel = kernelName.toLowerCase().indexOf("vadalog") >= 0;
        
        if (!isVadalogKernel) {
            // Use standard prompt for other kernels
            activeCell.setPrompt('*');
        }
        
        activeCell.model.trusted = true;

        // give the reduced notebook to the kernel and execute it
        const executePromise = OutputArea.execute(sendData, activeCell.outputArea, context.sessionContext);
        
        return executePromise
            .then((msg: any) => {
                
                activeCell.model.executionCount = msg.content.execution_count;

                //TODO: apply predicate_rename_mapping
                //var predicate_rename_mapping = msg.content.predicate_rename_mapping
                
                if (widget.isDisposed) {
                    return false;
                } else {
                    widget.update();
                    return true;
                }
            }).catch((e: any) => {
                if (e.message === 'Canceled') {
                    activeCell.setPrompt('');
                }
                throw e;
            });
    }

    public dispose(): void {
    }
}
