/*---------------------------------------------------------------------------------------------
* Copyright (c) 2020 Simon Waelti
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

import * as path from 'path';
import { readFileSync } from 'fs';

import { 
	Macrofile
} from './macroLanguageService/macroLanguageService';

import { 
	MacroFileType, 
	MacroFileProvider, 
	FileProviderParams,
} from './macroLanguageService/macroLanguageTypes';
import { Parser } from './macroLanguageService/parser/macroParser';
import * as glob  from 'glob';  

import {
	TextDocuments,
} from 'vscode-languageserver/node';

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

import { URI, Utils } from 'vscode-uri';

const ALL_FILES:string = '/**/*.{[sS][rR][cC],[dD][eE][fF]}';

export const parsedDocuments: Map<string, MacroFileType> = new Map<string, MacroFileType>();


export class FileProvider implements MacroFileProvider {

	constructor(private workspaceFolder:string, private documents: TextDocuments<TextDocument>, private connection?:any) {}

	public get(file: string): MacroFileType | undefined {
	
		let uri = this.resolveReference(file);
		if (uri) {
			
			let doc = getParsedDocument(this.documents, uri, (document => {
				let parser = new Parser(this);
				return parser.parseMacroFile(document);
			}));
			if (!doc) {		
				try {
					const file = readFileSync(URI.parse(uri).fsPath, 'utf-8');
					let document = TextDocument.create(uri!, 'macro', 1, file.toString());
					try {
						
						let macrofile = new Parser(this).parseMacroFile(document);
						doc = {
							macrofile: macrofile,
							document: document,
							version: 1
						};
						parsedDocuments.set(uri, doc);
					}
					catch (err){
						this.connection?.console.log(err);
					}
				}
				catch (err) {
					this.connection?.console.log(err);
					return undefined;
				}
			}
			return doc;
		}
		return undefined;
	}
	
	public getAll(param?:FileProviderParams) {
		let types:MacroFileType[] = [];
	
		try {
			const dir = URI.parse(this.workspaceFolder).fsPath;
			let files:string[] = [];
			if (param?.uris){
				files = param.uris;
			}
			else if (param?.glob) {
				files = glob.sync(dir + param.glob);
			}
			else {
				files = glob.sync(dir + ALL_FILES);
			}

			for (const file of files) {
				let type = this.get(file);
				if (type){
					types.push(type);
				}
			}
		} catch (err){
			this.connection.console.log(err);
		}
		return types;
	}

	public resolveReference(ref: string): string | undefined {

		if (ref.startsWith('file:///')) {
			return ref;
		}
		let absolutPath = ref;
		if (!path.isAbsolute(ref)) {
			absolutPath = Utils.resolvePath(URI.parse(this.workspaceFolder), ref).fsPath;
		}
		absolutPath = this.resolvePathCaseSensitive(absolutPath)
		return absolutPath ? URI.file(absolutPath).toString() :undefined;
	}

	private resolvePathCaseSensitive(file:string) {
		let norm = path.normalize(file);
		let root = path.parse(norm).root;
		let p = norm.slice(Math.max(root.length - 1, 0));
		return glob.sync(p, { nocase: true, cwd: root })[0];
	}
}

export function getParsedDocument(documents: TextDocuments<TextDocument>, uri: string, parser:((document:TextDocument) => Macrofile), parse:boolean=false) : MacroFileType | undefined {
	let document = documents.get(uri);
	if (document) {
		let parsed = parsedDocuments.get(uri);
		if (parsed) {
			if (document.version !== parsed.version || parse) {
				parsedDocuments.set(uri , {
					macrofile: parser(document),
					document: document,
					version: document.version
				});
			}
		}
		else {
			parsedDocuments.set(uri, {
				macrofile: parser(document),
				document: document,
				version: document.version
			});
		}	
	}
	return parsedDocuments.get(uri);
}