/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the Source EULA. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import { EOL } from 'os';
import { ITool, NoteBookEnvironmentVariablePrefix } from '../interfaces';
import { setEnvironmentVariablesForInstallPaths, getRuntimeBinaryPathEnvironmentVariableName } from '../common/utils';
import { ToolsInstallPath } from '../constants';
import { delimiter } from 'path';


const NotebookEnvironmentVariablePrefixRegex = new RegExp(`^${NoteBookEnvironmentVariablePrefix}`);

export class Model {
	private propValueObject: { [s: string]: string | undefined } = {};

	public setPropertyValue(property: string, value: string | number | boolean | undefined): void {
		if (typeof value === 'boolean') {
			this.propValueObject[property] = value ? 'true' : 'false';
		} else if (typeof value === 'number') {
			this.propValueObject[property] = value.toString();
		} else {
			this.propValueObject[property] = value;
		}
	}

	public getIntegerValue(propName: string, defaultValue: number = 0): number {
		const value = this.propValueObject[propName];
		return value === undefined ? defaultValue : Number.parseInt(value);
	}

	public getStringValue(propName: string, defaultValue?: string): string | undefined {
		const value = this.propValueObject[propName];
		return value === undefined ? defaultValue : value;
	}

	public getBooleanValue(propName: string, defaultValue: boolean = false): boolean {
		const value = this.propValueObject[propName];
		return value === undefined ? defaultValue : value === 'true';
	}

	/**
	 * Returns python code statements for setting variables starting with {@see NoteBookEnvironmentVariablePrefix} as python variables.
	 * The prefix {@see NoteBookEnvironmentVariablePrefix} is removed and variable name changed to all lowercase to arrive at python variable name.
	 * The statements returned are escaped for use in cell of a python notebook.
	 *
	 * @param tools - optional set of tools for which variable value setting statements need to be generated;
	 * @param inputFilter - optional parameter to filter out setting of specific variable names. Every variable for which this function returns false is not included
	 * 						in the emitted code.
	 */
	public getCodeCellContentForNotebook(tools: ITool[] = [], inputFilter: (varName: string) => boolean = () => true): string[] {
		const statements: string[] = Object.keys(this.propValueObject)
			.filter(propertyName => propertyName.startsWith(NoteBookEnvironmentVariablePrefix) && inputFilter(propertyName))
			.map(propertyName => {
				const value = this.escapeForNotebookCodeCell(this.getStringValue(propertyName, ''));
				const varName = propertyName.replace(NotebookEnvironmentVariablePrefixRegex, '').toLocaleLowerCase();
				return `${varName} = '${value}'${EOL}`;
			});
		statements.push(`print('Variables have been set successfully.')${EOL}`);
		const env: NodeJS.ProcessEnv = {};
		setEnvironmentVariablesForInstallPaths(tools, env);
		tools.forEach(tool => {
			const envVarName: string = getRuntimeBinaryPathEnvironmentVariableName(tool.name);
			statements.push(`os.environ["${envVarName}"] = "${this.escapeForNotebookCodeCell(env[envVarName]!)}"${EOL}`);
		});
		if (env[ToolsInstallPath]) {
			statements.push(`os.environ["PATH"] = os.environ["PATH"] + "${delimiter}" + "${this.escapeForNotebookCodeCell(env[ToolsInstallPath])}"${EOL}`);
		}
		statements.push(`print('Environment Variables for tools have been set successfully.')${EOL}`);
		return statements;
	}

	protected escapeForNotebookCodeCell(original?: string): string | undefined {
		// Escape the \ character for the code cell string value
		return original && original.replace(/\\/g, '\\\\');
	}

	/**
	 * Sets the environment variable for each model variable that starts with {@see NoteBookEnvironmentVariablePrefix} in the
	 * current process.
	 *
	 * @param env - env variable object in which the environment variables are populated. Default: process.env
	 * @param inputFilter - an optional filter to further restrict the variables that are set into the env object.
	 *						Every variable for which this function returns false is not included does not get the env variable set.
	 * 						Default all variable meeting prefix requirements are set.
	 */
	public setEnvironmentVariables(env: NodeJS.ProcessEnv = process.env, inputFilter: (varName: string) => boolean = () => true): void {
		Object.keys(this.propValueObject)
			.filter(propertyName => propertyName.startsWith(NoteBookEnvironmentVariablePrefix) && inputFilter(propertyName))
			.forEach(propertyName => {
				const value = this.getStringValue(propertyName);
				env[propertyName] = value === undefined ? '' : value;
			});
	}
}
