import { window, workspace, ViewColumn, commands, Uri } from 'vscode';
import { spawnSync } from 'child_process';
import { DepGraphReq } from './Requests';

/**
 * Class for creating new dependency graph requests and displaying them if the search was succesful
 */
export class DepGraphView{
    /** Data directory of the extension */
    private _dataDir:string|undefined;
    /** Type of the last selected graph */
    private _selectedType:string="";
    /** Data of the latest graph */
    private _storedData:{Type:string, Data:string}={Type:"", Data:""};
    private _dotPath:string="";
    private fs =  require('fs');

    constructor(){
        this._dataDir = workspace.getConfiguration('RefactorErl').get('DataDirectory');
        workspace.onDidChangeConfiguration(() => { 
            this._dataDir = workspace.getConfiguration('RefactorErl').get('DataDirectory');
        });
    }

    /** Gets the params for a new graph request from the user 
     * @returns Promise for new dep graph request params
    */
    public async newGraph():Promise<string|undefined>{
        let level = await window.showQuickPick(["Module", "Function"],{placeHolder:"Select level"});
        if(level === undefined){ return undefined; }
        let SMprompt = level === "Module" ? "Enter the starting modules separated by commas."
                            : "Enter the starting functions separated by commas. Format: {module}:{fun}/{arity}";
        let starting_modules = await window.showInputBox({prompt:SMprompt});
        if(starting_modules === undefined){ return undefined; }
        let type = await window.showQuickPick(
                    ["Plain Text", "Smart Graph"], {placeHolder:"Select display type"}
                );
        if(type === undefined){ return undefined; }
        this._selectedType = type;
        if(type === "Smart Graph"){
            if(this._dataDir === undefined || !this.fs.existsSync(this._dataDir)){
                window.showErrorMessage("Data directory does not exist.");
                return undefined;
            }
            this._dotPath = this._dataDir+"/"+Date.now().toString()+".dot";
        }
        return DepGraphReq(level, type, starting_modules, this._dotPath);
    }

    /**
     * Processes the data of the received result
     * @param Data Result sent by the RefactorErl
     */
    public UpdateGraph(Data:any):void{
        DeleteFile(this._storedData.Data);
        let newData = this._selectedType === "Plain Text" ? DrawPlainText(Data) : CreatePng(this._dotPath);
        if(newData.state !== "ok"){
            window.showErrorMessage(newData.result);
            return;
        }
        this._storedData = {Type: this._selectedType, Data: newData.result};
        this.Display();
    }

    /** Displayes the stored graph */
    public Display():void{
        if(this._storedData.Data === ""){ 
            window.showErrorMessage("There are no saved graph. Run an analysis first.");
            return;
        }
        if(this._storedData.Type === "Plain Text"){
            let panel = window.createWebviewPanel(
                        "depgraph", "Dependency Graph",
                        ViewColumn.Beside, {enableScripts:true}
                    );
            panel.webview.html = this._storedData.Data;
        }
        else{
            if(this.fs.existsSync(this._storedData.Data)){
                commands.executeCommand('vscode.open',
                                Uri.file(this._storedData.Data), ViewColumn.Beside);
            }
        }
    }

    /** Deletes the last graph file upon closing the extension */
    public clean():void{
        DeleteFile(this._storedData.Data);
    }
}

/**
 * Draws a plain text graph from the received data
 * @param Data
 * @returns String of the HTML page with the graph
 */
function DrawPlainText(Data:any[]):{state:string, result:string}{
    let body = ``;
    for(let Elem of Data){
        let target = Elem[1] === "" ? "[]" : JSON.stringify(Elem[1]);
        body += `<p>${Elem[0]} --> ${target}</p>`;
    }
    
    let html = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dependency Graph - Plain Text</title></head><body>`+body+`</body></html>`;

    return {state:"ok", result:html};
}

/**
 * Draws a graph png file from the dot file received
 * @param DotPath Path of the dot file created by RefactorErl
 * @returns Path of the created Png file if succesful, error message otherwise
 */
function CreatePng(DotPath:string):{state:string, result:string}{
    const path = require('path');
    let PngPath = path.dirname(DotPath)+"/"+path.basename(DotPath, '.dot')+".png";
    let Result = spawnSync("dot", ["-Tpng", "-o"+PngPath, DotPath]);
    DeleteFile(DotPath);
    if(Result.status !== 0){
        return {state:"error", result:"Error in creating png file"};
    }
    return {state:"ok", result:PngPath};
}

/**
 * Deletes the received file
 * @param Path Path of the file to delete
 */
function DeleteFile(Path:string):void{
    const fs = require('fs');
    if(fs.existsSync(Path)){ fs.unlinkSync(Path); }
}