import {
	FileDialogType,
	MessageBoxType,
	WidgetType,
} from "qrc:/js/lib/generated/enum";
import {
	isWidgetResultCalcParamEditor,
	isWidgetResultFileDialog,
	isWidgetResultFormEditor,
	isWidgetResultGeometryColorSelector,
	isWidgetResultLayeredImportDialog,
	isWidgetResultMessageBox,
} from "qrc:/js/lib/generated/typeguard";

import {
	TableTypeMap,
} from "./table_utils";
import {
	assert,
	isNumber,
	readSetting,
	writeSetting,
} from "./utils";

/**
 * Create unique id for SheetMaterial
 *
 * @param row A row of the sheetMaterial table
 * @returns Unique string of the table row
 */
export function sheetMaterialToId(row: Readonly<SheetMaterial>): string {
	return row.identifier;
}

/**
 * Create string representation for SheetMaterial
 *
 * @param row A row of the sheetMaterial table
 * @returns Unique string representation of the table row
 */
export function sheetMaterialToString(row: SheetMaterial) : string {
	return `${row.name} (id: ${row.identifier})${row.description.length > 0 ? ":" : ""} ${row.description}`;
}

export function extractTableDropDownResult<Key extends keyof TableTypeMap>(
	table: readonly Readonly<TableTypeMap[Key]>[],
	computeId: (rows: Readonly<TableTypeMap[Key]>) => string,
	entryId: string,
): Readonly<TableTypeMap[Key]> {
	const row = table.find(row => computeId(row) === entryId);
	assert(row !== undefined, "Result's id \"" + entryId + "\" does not match any row in submitted table");
	return row;
}

/**
 * Show a form widget and process the result in case the used accepted
 *
 * @param rows Dialog content definition
 */
export function showFormWidget(rows: FormRowConfig[]): WidgetResultFormEditor|undefined {
	const result = wsi4.ui.show({
		type: WidgetType.formEditor,
		content: {
			rows: rows,
		},
	});

	if (result === undefined) {
		// User canceled
		return;
	}

	return result.type === WidgetType.formEditor && isWidgetResultFormEditor(result.content) ? result.content : wsi4.throwError("Return value invalid");
}

function showMessageBox(type: MessageBoxType, title: string, text: string): boolean {
	const dialogResult = wsi4.ui.show({
		type: WidgetType.messageBox,
		content: {
			type: type,
			title: title,
			text: text,
		},
	});
	if (dialogResult === undefined || dialogResult.type !== WidgetType.messageBox || !isWidgetResultMessageBox(dialogResult.content)) {
		// Expecting valid result in any case - even if user canceled.
		return wsi4.throwError("Value invalid");
	}
	return dialogResult.content.accepted;
}

/**
 * Show dialog with submitted title and text and yes / no buttons
 *
 * @param title Dialog title
 * @param text Dialog text
 * @returns true if user clicked yes; flase otherwise
 */
export function askQuestion(title: string, text: string): boolean {
	return showMessageBox(MessageBoxType.question, title, text);
}

/**
 * Show info dialog with submitted title and text
 *
 * @param title Dialog title
 * @param text Dialog text
 */
export function showInfo(title: string, text: string): void {
	showMessageBox(MessageBoxType.info, title, text);
}

/**
 * Show warning dialog with submitted title and text
 *
 * @param title Dialog title
 * @param text Dialog text
 */
export function showWarning(title: string, text: string): void {
	showMessageBox(MessageBoxType.warning, title, text);
}

/**
 * Show error dialog with submitted title and text
 *
 * @param title Dialog title
 * @param text Dialog text
 */
export function showError(title: string, text: string): void {
	showMessageBox(MessageBoxType.error, title, text);
}

function showFileDialog(type: FileDialogType, title: string, defaultPath: string, filter: string): string[] {
	const dialogResult = wsi4.ui.show({
		type: WidgetType.fileDialog,
		content: {
			type: type,
			title: title,
			defaultPath: defaultPath,
			filter: filter,
		},
	});
	if (dialogResult === undefined || dialogResult.type !== WidgetType.fileDialog || !isWidgetResultFileDialog(dialogResult.content)) {
		return wsi4.throwError("Dialog result invalid");
	}
	return dialogResult.content.paths;
}

/**
 * Query paths of existing file
 *
 * @param title Dialog title
 * @param defaultPath Default directory for the dialog
 * @param filter Limit selectable files to defined file extentions
 * @returns Selected file paths if successful; empty array else
 */
export function getOpenFilePath(title: string, defaultPath = "", filter = ""): string|undefined {
	const paths = showFileDialog(FileDialogType.openFile, title, defaultPath, filter);
	return paths.length === 1 ? paths[0] : undefined;
}

/**
 * Query paths of existing files
 *
 * @param title Dialog title
 * @param defaultPath Default directory for the dialog
 * @param filter Limit selectable files to defined file extentions
 * @returns Selected file paths if successful; empty array else
 */
export function getOpenFilePaths(title: string, defaultPath = "", filter = ""): string[] {
	return showFileDialog(FileDialogType.openFiles, title, defaultPath, filter);
}

/**
 * Query path of existing or non-existing file
 *
 * @param title Dialog title
 * @param defaultPath Default directory for the dialog
 * @param filter Limit selectable files to defined file extentions
 * @returns Selected file path if successful; undefined else
 */
export function getSaveFilePath(title: string, defaultPath = "", filter = ""): string|undefined {
	const paths = showFileDialog(FileDialogType.saveFile, title, defaultPath, filter);
	return paths.length === 1 ? paths[0] : undefined;
}

/**
 * Query path of existing or non-existing directory
 *
 * @param title Dialog title
 * @param defaultPath Default directory for the dialog
 * @returns Selected directory path if successful; undefined else
 */
export function getDirectoryPath(title: string, defaultPath = ""): string|undefined {
	const paths = showFileDialog(FileDialogType.directory, title, defaultPath, "");
	return paths.length !== 1 ? undefined : paths[0];
}

/**
 * Show layered import dialog
 *
 * @param layered The layered
 */
export function showLayeredImportDialog(layered: Layered, layeredExtraData: LayeredExtraData, title = ""): WidgetResultLayeredImportDialog|undefined {
	const thicknessSettingsKey = "layeredImportDialogSheetThickness";
	const defaultThickness = 1;

	const thickness = layeredExtraData.thickness === 0. ? readSetting<number>(thicknessSettingsKey, defaultThickness, isNumber) : layeredExtraData.thickness;

	const result = wsi4.ui.show({
		type: WidgetType.layeredImportDialog,
		content: {
			title: title,
			layered: layered,
			thickness: thickness,
			name: layeredExtraData.name,
			material: layeredExtraData.material,
		},
	});
	if (result === undefined) {
		// User canceled
		return undefined;
	}
	if (result.type !== WidgetType.layeredImportDialog || !isWidgetResultLayeredImportDialog(result.content)) {
		return wsi4.throwError("Result invalid");
	}

	writeSetting(thicknessSettingsKey, result.content.thickness);
	return result.content;
}

export function showGeometryColorSelector(
	assembly: Assembly, flags: GeometrySelectorConfigFlags, formConfig: WidgetConfigFormEditor, colorDataKey: string, initialValue: WidgetResultGeometryColorSelector):
	WidgetResultGeometryColorSelector|undefined {
	const result = wsi4.ui.show({
		type: WidgetType.geometryColorSelector,
		content: {
			flags: flags,
			assembly: assembly,
			formEditorConfig: formConfig,
			colorDataKey: colorDataKey,
			initialValue: initialValue,
		},
	});
	if (result === undefined) {
		// User canceled
		return undefined;
	}
	if (result.type !== WidgetType.geometryColorSelector || !isWidgetResultGeometryColorSelector(result.content)) {
		return wsi4.throwError("Result invalid");
	}
	return result.content;
}

export function showFaceColorEditor(assembly: Assembly, initialValue: WidgetResultGeometryColorSelector) : WidgetResultGeometryColorSelector | undefined {
	const flags: GeometrySelectorConfigFlags = {
		edge: false,
		face: true,
	};
	const formConfig: WidgetConfigFormEditor = {
		rows: [],
	};
	return showGeometryColorSelector(assembly, flags, formConfig, "color", initialValue);
}

export function showAssemblyView(assembly: Assembly) : WidgetResult | undefined {
	const config: WidgetConfigAssemblyView = {
		assembly: assembly,
	};
	return wsi4.ui.show({
		type: WidgetType.assemblyView,
		content: config,
	});
}

export function showCalcParamEditor(initialValue: WidgetResultCalcParamEditor) : WidgetResultCalcParamEditor|undefined {
	const config: WidgetConfigCalcParamEditor = {
		initialValue: initialValue,
	};

	const result = wsi4.ui.show({
		type: WidgetType.calcParamEditor,
		content: config,
	});

	if (result === undefined) {
		return undefined;
	} else {
		assert(isWidgetResultCalcParamEditor(result.content));
		return result.content;
	}
}
