/* --------------------------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for license information.
 * ------------------------------------------------------------------------------------------ */

import {
	createConnection,
	TextDocuments,
	Diagnostic,
	ProposedFeatures,
	InitializeParams,
	DidChangeConfigurationNotification,
	CompletionItem,
	CompletionItemKind,
	TextDocumentPositionParams,
	TextDocumentSyncKind,
	InitializeResult,
} from 'vscode-languageserver';

import {
	TextDocument
} from 'vscode-languageserver-textdocument';

import { URI } from 'vscode-uri';
import * as fs from 'fs';
import {ErrorMatcher} from './ErrorMatcher';
import * as wordComplition from './wordCompletion'
import * as path from 'path';



// Create a connection for the server. The connection uses Node's IPC as a transport.
// Also include all preview / proposed LSP features.
let connection = createConnection(ProposedFeatures.all);

// Create a simple text document manager. The text document manager
// supports full document sync only
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);

let hasConfigurationCapability: boolean = false;
let hasWorkspaceFolderCapability: boolean = false;
let hasDiagnosticRelatedInformationCapability: boolean = false;


connection.onInitialize((params: InitializeParams) => {
	let capabilities = params.capabilities;

	// Does the client support the `workspace/configuration` request?
	// If not, we will fall back using global settings
	hasConfigurationCapability = !!(
		capabilities.workspace && !!capabilities.workspace.configuration
	);
	hasWorkspaceFolderCapability = !!(
		capabilities.workspace && !!capabilities.workspace.workspaceFolders
	);
	hasDiagnosticRelatedInformationCapability = !!(
		capabilities.textDocument &&
		capabilities.textDocument.publishDiagnostics &&
		capabilities.textDocument.publishDiagnostics.relatedInformation
	);

	const result: InitializeResult = {
		capabilities: {
			textDocumentSync: TextDocumentSyncKind.Full,
			// Tell the client that the server supports code completion
			completionProvider: {
				resolveProvider: true
			}
		}
	};
	if (hasWorkspaceFolderCapability) {
		result.capabilities.workspace = {
			workspaceFolders: {
				supported: true
			}
		};
	}
	return result;
});

connection.onInitialized(() => {
	if (hasConfigurationCapability) {
		// Register for all configuration changes.
		connection.client.register(DidChangeConfigurationNotification.type, undefined);
	}
	if (hasWorkspaceFolderCapability) {
		connection.workspace.onDidChangeWorkspaceFolders(_event => {
			connection.console.log('Workspace folder change event received.');
		});
	}
});

// The settings
interface Settings {
	maxNumberOfProblems: number;
	probHome : string;
}


const defaultSettings: Settings = { maxNumberOfProblems: 1000, probHome: "/home/sebastian/prob_prolog/probcli.sh" };
let globalSettings: Settings = defaultSettings;

// Cache the settings of all open documents
let documentSettings: Map<string, Thenable<Settings>> = new Map();

connection.onDidChangeConfiguration(change => {
	if (hasConfigurationCapability) {
		// Reset all cached document settings
		documentSettings.clear();
	} else {
		globalSettings = <Settings>(
			(change.settings.languageServer || defaultSettings)
		);
	}

	// Revalidate all open text documents
	documents.all().forEach(validateTextDocument);
});

function getDocumentSettings(resource: string): Thenable<Settings> {
	if (!hasConfigurationCapability) {
		return Promise.resolve(globalSettings);
	}
	let result = documentSettings.get(resource);
	if (!result) {
		result = connection.workspace.getConfiguration({
			scopeUri: resource,
			section: 'languageServer'
		});
		documentSettings.set(resource, result);
	}
	return result;
}

// Only keep settings for open documents
documents.onDidClose(e => {
	documentSettings.delete(e.document.uri);
});

documents.onDidSave(change => {

	validateTextDocument(change.document)
})


async function validateTextDocument(textDocument: TextDocument): Promise<void> {
	let settings = await getDocumentSettings(textDocument.uri);
	
	let pathy:path.ParsedPath = path.parse(URI.parse(textDocument.uri).path);

	let dic:string = pathy.dir

	console.log(settings.probHome)
	
	let probCliHome:string = settings.probHome


	let ndjson:string = 'NDJSON_ERROR_LOG_FILE '
	let errorPath:string = dic+'/_error.json'
	const {exec} = require('child_process');
	let command:string = probCliHome + ' -p MAX_INITIALISATIONS 0 -version -p STRICT_CLASH_CHECKING TRUE -p TYPE_CHECK_DEFINITIONS TRUE '
	
	fs.writeFile(errorPath, "", () =>{}) //Insure a clean error dictonary
	let command2:string = command +  URI.parse(textDocument.uri).path + " -p " + ndjson + errorPath;
	
	
	let diagnostics : Array<Diagnostic> = new Array()
	let diagnosticsPromise : Promise<Set<Diagnostic>>

	
	if(correctPath(probCliHome))
	{
		exec(command2, (err:string, stdout:string, stderr:string) => {
		let bla = new ErrorMatcher()
	    diagnosticsPromise =  bla.matchError(textDocument, errorPath)

		diagnosticsPromise.then(function(result:Set<Diagnostic>){
			diagnostics = Array.from(result)
			connection.sendDiagnostics({ uri: textDocument.uri, diagnostics});	
		}, function(err){
			connection.sendNotification("parse_error_prob", "there are things wrong with the parse results " + err)
		})
	  });
	}

}

function correctPath(path:string): boolean{
	try{
		fs.accessSync(path)
	}catch(e){
		connection.sendNotification("path_error_prob", path + " seems to be the wrong path to ProB")
		return false
	}
	return true
}


// This handler provides the initial list of the completion items.
connection.onCompletion(
	(textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
		return wordComplition.selectCompletion(textDocumentPosition)
	}
);


//Can be used to enrich completion with more infos
connection.onCompletionResolve(
	(item : CompletionItem) => {return item})

// for open, change and close text document events
documents.listen(connection);

// Listen on the connection
connection.listen();