import {
	ProcessType,
	TableType,
} from "qrc:/js/lib/generated/enum";
import {
	isAutomaticMechanicalDeburringMaterial,
	isAutomaticMechanicalDeburringParameters,
	isBendDeduction,
	isBendLineConstraint,
	isBendRateParameters,
	isBendTime,
	isBendTimeParameters,
	isDieGroupPriority,
	isDimensionConstraints,
	isSheetMaterial,
	isSheetMaterialScrapValue,
	isLaserSheetCuttingGas,
	isLaserSheetCuttingMaxThickness,
	isLaserSheetCuttingMinArea,
	isLaserSheetCuttingPierceTime,
	isLaserSheetCuttingRate,
	isLaserSheetCuttingSpeed,
	isLowerDie,
	isLowerDieGroup,
	isLowerDieUnit,
	isSheetMaterialDensity,
	isPackaging,
	isProcess,
	isProcessHandlingTime,
	isProcessIdlePeriod,
	isProcessRate,
	isProcessSetupTimeFallback,
	isProcessUnitTimeFallback,
	isScrewThread,
	isSetting,
	isSheet,
	isSheetBendingMaterial,
	isSheetBendingMaterialMapping,
	isSheetCuttingMaterial,
	isSheetCuttingMaterialMapping,
	isSheetModulus,
	isSheetPrice,
	isSheetPriority,
	isSheetStock,
	isStringIndexedInterface,
	isSurcharge,
	isTableType,
	isTappingTimeParameters,
	isTransportationCosts,
	isTube,
	isTubeMaterial,
	isTubeMaterialDensity,
	isTubeProfile,
	isUpperDie,
	isUpperDieGroup,
	isUpperDieUnit,
	isTubeCuttingSpeed,
	isTubeCuttingPierceTime,
	isTubeCuttingProcess,
	isTubeCuttingProcessMapping,
	isTubePrice,
	isTubeStock,
	isTubeMaterialScrapValue,
	isSheetCuttingProcess,
	isSheetCuttingProcessMapping,
	isSheetCuttingMotionParameters,
	isSheetCuttingProcessToLaserCuttingGas,
	isPurchasePartMaterial,
	isProcessConstraintsSheetMaterial,
	isProcessConstraintsTubeMaterial,
	isProcessConstraintsPurchasePartMaterial,
	isConsumable,
	isProcessConsumableRate,
	isSheetCuttingProcessConsumableRate,
	isTubeCuttingProcessConsumableRate,
	isSheetCuttingThicknessConstraints,
} from "qrc:/js/lib/generated/typeguard";
import {
	createCheckBoxRow,
	createDropDownRow,
	createLabelRow,
	createLineEditRow,
	createSpinBoxRow,
} from "qrc:/js/lib/gui_form_widget";
import {
	showFormWidget,
} from "qrc:/js/lib/gui_utils";
import {
	getTable,
	TableTypeMap,
} from "qrc:/js/lib/table_utils";
import {
	getKeysOfObject,
	isArray,
	isBoolean,
	isNumber,
	isString,
	parseJson,
} from "qrc:/js/lib/utils";

/**
 * Can be considered an AnyTable with unparsed and potentially incomplete content
 */
export interface PotentialAnyTable {
	type: TableType;
	content: StringIndexedInterface[];
}

export function isPotentialAnyTable(input: unknown): input is PotentialAnyTable {
	return isStringIndexedInterface(input) && isTableType(input["type"]) && isArray(input["content"], isStringIndexedInterface);
}

type TypeGuard<Row> = {
	(arg: unknown): arg is Row;
}

type Parsers<Row> = {
	[key in keyof Row]: (arg: unknown) => Row[key]|undefined;
};

type ConfigGenerators<Row> = {
	[key in keyof Row]: (key: string, value: unknown) => FormRowConfig;
};

type ResultExtractors<Row> = {
	[key in keyof Row]: (value: unknown) => Row[key];
};

type AllAliases<Row> = {
	[key in keyof Row]: string[];
};

type Aliases<Row> = Partial<AllAliases<Row>>;

interface ImportUtils<Row> {
	isRow: TypeGuard<Row>;
	parsers: Parsers<Row>;
	configGenerators: ConfigGenerators<Row>;
	resultExtractors: ResultExtractors<Row>;
	aliases?: Aliases<Row>;
}

function parseString(arg: unknown): string|undefined {
	return isString(arg) ? arg : undefined;
}

function parseDouble(arg: unknown): number|undefined {
	if (isNumber(arg)) {
		return arg;
	}
	if (!isString(arg)) {
		return undefined;
	}
	// replace all , to . to make sure the decimal point is a .
	const str = arg.replace(/,/gi, ".");
	const float = Number.parseFloat(str);
	return Number.isNaN(float) ? undefined : float;
}

function parseInt(arg: unknown): number|undefined {
	return (isNumber(arg) && Number.isInteger(arg)) ? arg : isString(arg) ? (Number.isNaN(Number.parseInt(arg)) ? undefined : Number.parseInt(arg)) : undefined;
}

function parseBoolean(arg: unknown): boolean|undefined {
	return isBoolean(arg) ? arg : isString(arg) ? parseJson(arg, isBoolean) : undefined;
}

function isProcessType(arg: unknown): arg is ProcessType {
	return isString(arg) && Array.from(ProcessType)
		.some(pt => pt === arg);
}

function parseProcessType(arg: unknown): ProcessType|undefined {
	return isProcessType(arg) ? arg : undefined;
}

function createStringWidgetConfig(key: string, value: unknown): FormRowConfig {
	if (value === undefined) {
		return createLineEditRow(
			key,
			key,
			"",
		);
	} else if (typeof value === "string") {
		return createLabelRow(
			key,
			key,
			value,
		);
	} else {
		return wsi4.throwError("Expecting undefined or string");
	}
}

function createDoubleWidgetConfig(key: string, value: unknown): FormRowConfig {
	if (value === undefined) {
		return createSpinBoxRow({
			key: key,
			name: key,
			initialValue: 0,
			min: Number.MIN_VALUE,
			max: Number.MAX_VALUE,
			decimals: 3,
		});
	} else if (typeof value === "number") {
		return createLabelRow(
			key,
			key,
			value.toFixed(3),
		);
	} else {
		return wsi4.throwError("Expecting undefined or number");
	}
}

function createIntWidgetConfig(key: string, value: unknown): FormRowConfig {
	if (value === undefined) {
		return createSpinBoxRow({
			key: key,
			name: key,
			initialValue: 0,
			min: Number.MIN_SAFE_INTEGER,
			max: Number.MAX_SAFE_INTEGER,
			decimals: 0,
		});
	} else if (typeof value === "number") {
		return createLabelRow(
			key,
			key,
			value.toFixed(0),
		);
	} else {
		return wsi4.throwError("Expecting undefined or number");
	}
}

function createBooleanWidgetConfig(key: string, value: unknown): FormRowConfig {
	if (value === undefined) {
		return createCheckBoxRow(
			key,
			key,
			true,
		);
	} else if (typeof value === "boolean") {
		return createLabelRow(
			key,
			key,
			JSON.stringify(value),
		);
	} else {
		return wsi4.throwError("Expecting undefined or boolean");
	}
}

function createTableWidgetConfig<Index extends keyof TableTypeMap>(
	key: string,
	value: unknown,
	tableType: Index,
	rowToId: (row: Readonly<TableTypeMap[Index]>) => string,
	rowToText: (row: Readonly<TableTypeMap[Index]>) => string,
): FormRowConfig {
	if (value === undefined) {
		return createDropDownRow({
			key: key,
			name: key,
			values: getTable(tableType),
			toId: rowToId,
			toName: rowToText,
			computeInitialValueIndex: () => 0,
		});
	} else if (isString(value)) {
		return createLabelRow(
			key,
			key,
			value,
		);
	} else {
		return wsi4.throwError("Expecting undefiend or StringIndexedInterface");
	}
}

function createDropDownWidget(key: string, values: string[], value: unknown): FormRowConfig {
	if (value === undefined) {
		return createDropDownRow({
			key: key,
			name: key,
			values: values,
			toId: (arg: string) => arg,
			toName: (arg: string) => arg,
			computeInitialValueIndex: () => 0,
		});
	} else if (typeof value === "string") {
		return createLabelRow(
			key,
			key,
			value,
		);
	} else {
		return wsi4.throwError("Expecting undefined or string");
	}
}

function createProcessTypeWidgetConfig(key: string, value: unknown): FormRowConfig {
	return createDropDownWidget(key, Array.from(ProcessType), value);
}

function createSheetMaterialTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.sheetMaterial, row => row.identifier, row => row.name);
}

function createSheetCuttingMatrialTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.sheetCuttingMaterial, row => row.identifier, row => row.name);
}

function createLaserSheetCuttingGasTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.laserSheetCuttingGas, row => row.identifier, row => row.name);
}

function createSheetBendingMaterialTableWidget(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.sheetBendingMaterial, row => row.identifier, row => row.name);
}

function createAutomaticMechanicalDeburringMaterialTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(
		key, value, TableType.automaticMechanicalDeburringMaterial, row => row.automaticMechanicalDeburringMaterialId, row => row.automaticMechanicalDeburringMaterialId);
}

function createPackagingTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.packaging, row => row.identifier, row => row.name);
}

function createProcessTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.process, row => row.identifier, row => row.name);
}

function createConsumableTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.consumable, row => row.identifier, row => row.name);
}

function createScrewThreadTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.screwThread, row => row.identifier, row => row.name);
}

function createSheetTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.sheet, row => row.identifier, row => row.name);
}

function createUpperDieGroupTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.upperDieGroup, row => row.identifier, row => row.name);
}

function createUpperDieTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.upperDie, row => row.identifier, row => row.name);
}

function createLowerDieTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.lowerDie, row => row.identifier, row => row.name);
}

function createLowerDieGroupTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.lowerDieGroup, row => row.identifier, row => row.name);
}

function createTubeMaterialTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.tubeMaterial, row => row.identifier, row => row.name);
}

function createPurchasePartMaterialTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.purchasePartMaterial, row => row.identifier, row => row.name);
}

function createTubeProfileTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.tubeProfile, row => row.identifier, row => row.name);
}

function createTubeSpecificationTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.tubeSpecification, row => row.identifier, row => row.name);
}

function createTubeCuttingProcessTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.tubeCuttingProcess, row => row.identifier, row => row.name);
}

function createSheetCuttingProcessTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.sheetCuttingProcess, row => row.identifier, row => row.name);
}

function createTubeTableWidgetConfig(key: string, value: unknown) {
	return createTableWidgetConfig(key, value, TableType.tube, row => row.identifier, row => row.name);
}

function stringExtractor(input: unknown): string {
	return isString(input) ? input : wsi4.throwError("Type invalid");
}

function numberExtractor(input: unknown): number {
	return isNumber(input) ? input : wsi4.throwError("Type invalid");
}

function booleanExtractor(input: unknown): boolean {
	return isBoolean(input) ? input : wsi4.throwError("Type invalid");
}

function processTypeExtractor(input: unknown): ProcessType {
	return isProcessType(input) ? input : wsi4.throwError("Type invalid");
}

function readProprty<Row>(input: StringIndexedInterface, key: keyof Row, aliases?: Aliases<Row>): unknown {
	const keys = [ key as string ];
	if (aliases !== undefined && key in aliases) {
		keys.push(...(aliases[key] as string[]));
	}
	for (const key of keys) {
		if (key in input) {
			return input[key];
		}
	}
	return undefined;
}

function parseInput<Row>(input: StringIndexedInterface, parsers: Parsers<Row>, aliases?: Aliases<Row>): Partial<Row> {
	return getKeysOfObject(parsers)
		.reduce((incompleteRow: Partial<Row>, key) => {
			incompleteRow[key] = parsers[key](readProprty(input, key, aliases));
			return incompleteRow;
		}, {});
}

function completeRowInteractively<Row>(incompleteRow: Partial<Row>, importUtils: ImportUtils<Row>): Row|undefined {
	const dialogResult = showFormWidget(getKeysOfObject(importUtils.configGenerators)
		.map(key => importUtils.configGenerators[key](key, incompleteRow[key])));
	if (dialogResult !== undefined) {
		getKeysOfObject(importUtils.configGenerators)
			.forEach(key => {
				const value = dialogResult.values[key];
				if (incompleteRow[key] === undefined && value !== undefined) {
					incompleteRow[key] = importUtils.resultExtractors[key](value);
				}
			});
	}

	if (importUtils.isRow(incompleteRow)) {
		return incompleteRow;
	} else {
		return undefined;
	}
}

function convertToTable<Row>(input: Array<StringIndexedInterface>, importUtils: ImportUtils<Row>, interactive: boolean): Array<Row>|undefined {
	return input.reduce((acc: Array<Row>|undefined, input) => {
		if (acc === undefined) {
			return acc;
		}
		if (importUtils.isRow(input)) {
			acc.push(input);
			return acc;
		}

		const parsedRow = parseInput(input, importUtils.parsers, importUtils.aliases);
		if (importUtils.isRow(parsedRow)) {
			acc.push(parsedRow);
			return acc;
		}

		if (!interactive) {
			return undefined;
		}

		const completedRow = completeRowInteractively(parsedRow, importUtils);
		if (completedRow === undefined) {
			// user canceled
			return undefined;
		} else {
			acc.push(completedRow);
			return acc;
		}
	}, []);
}

const sheetMaterialImportUtils: ImportUtils<SheetMaterial> = {
	isRow: isSheetMaterial,
	parsers: {
		identifier: parseString,
		name: parseString,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		description: stringExtractor,
	},
};

const sheetMaterialDensityImportUtils: ImportUtils<SheetMaterialDensity> = {
	isRow: isSheetMaterialDensity,
	aliases: {
		sheetMaterialId: [ "globalMaterialId" ],
	},
	parsers: {
		sheetMaterialId: parseString,
		density: parseDouble,
	},
	configGenerators: {
		sheetMaterialId: createSheetMaterialTableWidgetConfig,
		density: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetMaterialId: stringExtractor,
		density: numberExtractor,
	},
};

const sheetCuttingMaterialMappingUtils: ImportUtils<SheetCuttingMaterialMapping> = {
	isRow: isSheetCuttingMaterialMapping,
	aliases: {
		sheetMaterialId: [ "globalMaterialId" ],
		sheetCuttingMaterialId: [ "laserSheetCuttingMaterialId" ],
	},
	parsers: {
		sheetMaterialId: parseString,
		sheetCuttingMaterialId: parseString,
	},
	configGenerators: {
		sheetMaterialId: createSheetMaterialTableWidgetConfig,
		sheetCuttingMaterialId: createStringWidgetConfig,
	},
	resultExtractors: {
		sheetMaterialId: stringExtractor,
		sheetCuttingMaterialId: stringExtractor,
	},
};

const sheetBendingMaterialMappingUtils: ImportUtils<SheetBendingMaterialMapping> = {
	isRow: isSheetBendingMaterialMapping,
	aliases: {
		sheetMaterialId: [ "globalMaterialId" ],
		sheetBendingMaterialId: [ "bendMaterialId" ],
	},
	parsers: {
		sheetMaterialId: parseString,
		sheetBendingMaterialId: parseString,
	},
	configGenerators: {
		sheetMaterialId: createSheetMaterialTableWidgetConfig,
		sheetBendingMaterialId: createStringWidgetConfig,
	},
	resultExtractors: {
		sheetMaterialId: stringExtractor,
		sheetBendingMaterialId: stringExtractor,
	},
};

const bendTimeImportUtils: ImportUtils<BendTime> = {
	isRow: isBendTime,
	parsers: {
		mass: parseDouble,
		setupTime: parseDouble,
		setupTimePerBend: parseDouble,
		unitTime: parseDouble,
		unitTimePerBend: parseDouble,
	},
	configGenerators: {
		mass: createDoubleWidgetConfig,
		setupTime: createDoubleWidgetConfig,
		setupTimePerBend: createDoubleWidgetConfig,
		unitTime: createDoubleWidgetConfig,
		unitTimePerBend: createDoubleWidgetConfig,
	},
	resultExtractors: {
		mass: numberExtractor,
		setupTime: numberExtractor,
		setupTimePerBend: numberExtractor,
		unitTime: numberExtractor,
		unitTimePerBend: numberExtractor,
	},
};

const bendTimeParametersImportUtils: ImportUtils<BendTimeParameters> = {
	isRow: isBendTimeParameters,
	aliases: {
		sheetBendingMaterialId: [ "bendMaterialId" ],
	},
	parsers: {
		sheetBendingMaterialId: parseString,
		thickness: parseDouble,
		bendLineNetLength: parseDouble,
		setupTimeFactor: parseDouble,
		setupTimeDelta: parseDouble,
		setupTimePerBendFactor: parseDouble,
		setupTimePerBendDelta: parseDouble,
		unitTimeFactor: parseDouble,
		unitTimeDelta: parseDouble,
		unitTimePerBendFactor: parseDouble,
		unitTimePerBendDelta: parseDouble,
	},
	configGenerators: {
		sheetBendingMaterialId: createSheetBendingMaterialTableWidget,
		thickness: createDoubleWidgetConfig,
		bendLineNetLength: createDoubleWidgetConfig,
		setupTimeFactor: createDoubleWidgetConfig,
		setupTimeDelta: createDoubleWidgetConfig,
		setupTimePerBendFactor: createDoubleWidgetConfig,
		setupTimePerBendDelta: createDoubleWidgetConfig,
		unitTimeFactor: createDoubleWidgetConfig,
		unitTimeDelta: createDoubleWidgetConfig,
		unitTimePerBendFactor: createDoubleWidgetConfig,
		unitTimePerBendDelta: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetBendingMaterialId: stringExtractor,
		thickness: numberExtractor,
		bendLineNetLength: numberExtractor,
		setupTimeFactor: numberExtractor,
		setupTimeDelta: numberExtractor,
		setupTimePerBendFactor: numberExtractor,
		setupTimePerBendDelta: numberExtractor,
		unitTimeFactor: numberExtractor,
		unitTimeDelta: numberExtractor,
		unitTimePerBendFactor: numberExtractor,
		unitTimePerBendDelta: numberExtractor,
	},
};

const bendRateParametersImportUtils: ImportUtils<BendRateParameters> = {
	isRow: isBendRateParameters,
	aliases: {
		sheetBendingMaterialId: [ "bendMaterialId" ],
	},
	parsers: {
		sheetBendingMaterialId: parseString,
		thickness: parseDouble,
		bendLineNetLength: parseDouble,
		hourlyRateFactor: parseDouble,
		hourlyRateDelta: parseDouble,
	},
	configGenerators: {
		sheetBendingMaterialId: createSheetBendingMaterialTableWidget,
		thickness: createDoubleWidgetConfig,
		bendLineNetLength: createDoubleWidgetConfig,
		hourlyRateFactor: createDoubleWidgetConfig,
		hourlyRateDelta: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetBendingMaterialId: stringExtractor,
		thickness: numberExtractor,
		bendLineNetLength: numberExtractor,
		hourlyRateFactor: numberExtractor,
		hourlyRateDelta: numberExtractor,
	},
};

const bendLineConstraintImportUtils: ImportUtils<BendLineConstraint> = {
	isRow: isBendLineConstraint,
	aliases: {
		sheetBendingMaterialId: [ "bendMaterialId" ],
	},
	parsers: {
		sheetBendingMaterialId: parseString,
		thickness: parseDouble,
		maxNetLength: parseDouble,
	},
	configGenerators: {
		sheetBendingMaterialId: createSheetBendingMaterialTableWidget,
		thickness: createDoubleWidgetConfig,
		maxNetLength: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetBendingMaterialId: stringExtractor,
		thickness: numberExtractor,
		maxNetLength: numberExtractor,
	},
};

const laserSheetCuttingGasImportUtils: ImportUtils<LaserSheetCuttingGas> = {
	isRow: isLaserSheetCuttingGas,
	parsers: {
		identifier: parseString,
		name: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
	},
};

const laserSheetCuttingSpeedImportUtils: ImportUtils<LaserSheetCuttingSpeed> = {
	isRow: isLaserSheetCuttingSpeed,
	aliases: {
		sheetCuttingMaterialId: [ "laserSheetCuttingMaterialId" ],
	},
	parsers: {
		sheetCuttingMaterialId: parseString,
		laserSheetCuttingGasId: parseString,
		thickness: parseDouble,
		speed: parseDouble,
	},
	configGenerators: {
		sheetCuttingMaterialId: createSheetCuttingMatrialTableWidgetConfig,
		laserSheetCuttingGasId: createLaserSheetCuttingGasTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		speed: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingMaterialId: stringExtractor,
		laserSheetCuttingGasId: stringExtractor,
		thickness: numberExtractor,
		speed: numberExtractor,
	},
};

const laserSheetCuttingPierceTimeImportUtils: ImportUtils<LaserSheetCuttingPierceTime> = {
	isRow: isLaserSheetCuttingPierceTime,
	aliases: {
		sheetCuttingMaterialId: [ "laserSheetCuttingMaterialId" ],
	},
	parsers: {
		sheetCuttingMaterialId: parseString,
		laserSheetCuttingGasId: parseString,
		thickness: parseDouble,
		time: parseDouble,
	},
	configGenerators: {
		sheetCuttingMaterialId: createSheetCuttingMatrialTableWidgetConfig,
		laserSheetCuttingGasId: createLaserSheetCuttingGasTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		time: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingMaterialId: stringExtractor,
		laserSheetCuttingGasId: stringExtractor,
		thickness: numberExtractor,
		time: numberExtractor,
	},
};

const laserSheetCuttingRateImportUtils: ImportUtils<LaserSheetCuttingRate> = {
	isRow: isLaserSheetCuttingRate,
	aliases: {
		sheetCuttingMaterialId: [ "laserSheetCuttingMaterialId" ],
	},
	parsers: {
		sheetCuttingMaterialId: parseString,
		laserSheetCuttingGasId: parseString,
		rate: parseDouble,
	},
	configGenerators: {
		sheetCuttingMaterialId: createSheetCuttingMatrialTableWidgetConfig,
		laserSheetCuttingGasId: createLaserSheetCuttingGasTableWidgetConfig,
		rate: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingMaterialId: stringExtractor,
		laserSheetCuttingGasId: stringExtractor,
		rate: numberExtractor,
	},
};

const laserSheetCuttingMinAreaImportUtils: ImportUtils<LaserSheetCuttingMinArea> = {
	isRow: isLaserSheetCuttingMinArea,
	aliases: {
		sheetCuttingMaterialId: [ "laserSheetCuttingMaterialId" ],
	},
	parsers: {
		sheetCuttingMaterialId: parseString,
		laserSheetCuttingGasId: parseString,
		thickness: parseDouble,
		area: parseDouble,
	},
	configGenerators: {
		sheetCuttingMaterialId: createSheetCuttingMatrialTableWidgetConfig,
		laserSheetCuttingGasId: createLaserSheetCuttingGasTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		area: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingMaterialId: stringExtractor,
		laserSheetCuttingGasId: stringExtractor,
		thickness: numberExtractor,
		area: numberExtractor,
	},
};

const laserSheetCuttingMaxThicknessImportUtils: ImportUtils<LaserSheetCuttingMaxThickness> = {
	isRow: isLaserSheetCuttingMaxThickness,
	aliases: {
		sheetCuttingMaterialId: [ "laserSheetCuttingMaterialId" ],
	},
	parsers: {
		sheetCuttingMaterialId: parseString,
		laserSheetCuttingGasId: parseString,
		maxThickness: parseDouble,
		minThickness: parseDouble,
	},
	configGenerators: {
		sheetCuttingMaterialId: createSheetCuttingMatrialTableWidgetConfig,
		laserSheetCuttingGasId: createLaserSheetCuttingGasTableWidgetConfig,
		maxThickness: createDoubleWidgetConfig,
		minThickness: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingMaterialId: stringExtractor,
		laserSheetCuttingGasId: stringExtractor,
		maxThickness: numberExtractor,
		minThickness: numberExtractor,
	},
};

const packagingImportUtils: ImportUtils<Packaging> = {
	isRow: isPackaging,
	parsers: {
		identifier: parseString,
		name: parseString,
		dimX: parseDouble,
		dimY: parseDouble,
		dimZ: parseDouble,
		maxWeight: parseDouble,
		price: parseDouble,
		tr: parseDouble,
		tep: parseDouble,
		tea: parseDouble,
		packagingWeight: parseDouble,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		dimX: createDoubleWidgetConfig,
		dimY: createDoubleWidgetConfig,
		dimZ: createDoubleWidgetConfig,
		maxWeight: createDoubleWidgetConfig,
		price: createDoubleWidgetConfig,
		tr: createDoubleWidgetConfig,
		tep: createDoubleWidgetConfig,
		tea: createDoubleWidgetConfig,
		packagingWeight: createDoubleWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		dimX: numberExtractor,
		dimY: numberExtractor,
		dimZ: numberExtractor,
		maxWeight: numberExtractor,
		price: numberExtractor,
		tr: numberExtractor,
		tep: numberExtractor,
		tea: numberExtractor,
		packagingWeight: numberExtractor,
	},
};

const transportationCostsImportUtils: ImportUtils<TransportationCosts> = {
	isRow: isTransportationCosts,
	parsers: {
		identifier: parseString,
		name: parseString,
		packagingId: parseString,
		fixedCosts: parseDouble,
		minCosts: parseDouble,
		kmKgFactor: parseDouble,
		kmFactor: parseDouble,
		minDistance: parseDouble,
		maxDistance: parseDouble,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		packagingId: createPackagingTableWidgetConfig,
		fixedCosts: createDoubleWidgetConfig,
		minCosts: createDoubleWidgetConfig,
		kmKgFactor: createDoubleWidgetConfig,
		kmFactor: createDoubleWidgetConfig,
		minDistance: createDoubleWidgetConfig,
		maxDistance: createDoubleWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		packagingId: stringExtractor,
		fixedCosts: numberExtractor,
		minCosts: numberExtractor,
		kmKgFactor: numberExtractor,
		kmFactor: numberExtractor,
		minDistance: numberExtractor,
		maxDistance: numberExtractor,
	},
};

const surchargeImportUtils: ImportUtils<Surcharge> = {
	isRow: isSurcharge,
	parsers: {
		name: parseString,
		type: parseString,
		value: parseDouble,
		description: parseString,
	},
	configGenerators: {
		name: createStringWidgetConfig,
		type: createStringWidgetConfig,
		value: createDoubleWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		name: stringExtractor,
		type: stringExtractor,
		value: numberExtractor,
		description: stringExtractor,
	},
};

const processImportUtils: ImportUtils<Process> = {
	isRow: isProcess,
	parsers: {
		identifier: parseString,
		parentIdentifier: parseString,
		type: parseProcessType,
		name: parseString,
		costCenter: parseString,
		priority: parseInt,
		active: parseBoolean,
		childrenActive: parseBoolean,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		parentIdentifier: createStringWidgetConfig,
		type: createProcessTypeWidgetConfig,
		name: createStringWidgetConfig,
		costCenter: createStringWidgetConfig,
		priority: createIntWidgetConfig,
		active: createBooleanWidgetConfig,
		childrenActive: createBooleanWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		parentIdentifier: stringExtractor,
		type: processTypeExtractor,
		name: stringExtractor,
		costCenter: stringExtractor,
		priority: numberExtractor,
		active: booleanExtractor,
		childrenActive: booleanExtractor,
		description: stringExtractor,
	},
};

const processRateImportUtils: ImportUtils<ProcessRate> = {
	isRow: isProcessRate,
	parsers: {
		processId: parseString,
		rate: parseDouble,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		rate: createDoubleWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		rate: numberExtractor,
	},
};

const processSetupTimeFallbackImportUtils: ImportUtils<ProcessSetupTimeFallback> = {
	isRow: isProcessSetupTimeFallback,
	parsers: {
		processId: parseString,
		time: parseDouble,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		time: createDoubleWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		time: numberExtractor,
	},
};

const processUnitTimeFallbackImportUtils: ImportUtils<ProcessUnitTimeFallback> = {
	isRow: isProcessUnitTimeFallback,
	parsers: {
		processId: parseString,
		time: parseDouble,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		time: createDoubleWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		time: numberExtractor,
	},
};

const sheetImportUtils: ImportUtils<Sheet> = {
	isRow: isSheet,
	aliases: {
		sheetMaterialId: [ "globalMaterialId" ],
	},
	parsers: {
		identifier: parseString,
		name: parseString,
		sheetMaterialId: parseString,
		dimX: parseDouble,
		dimY: parseDouble,
		thickness: parseDouble,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		sheetMaterialId: createSheetMaterialTableWidgetConfig,
		dimX: createDoubleWidgetConfig,
		dimY: createDoubleWidgetConfig,
		thickness: createDoubleWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		sheetMaterialId: stringExtractor,
		dimX: numberExtractor,
		dimY: numberExtractor,
		thickness: numberExtractor,
		description: stringExtractor,
	},
};

const sheetModulusImportUtils: ImportUtils<SheetModulus> = {
	isRow: isSheetModulus,
	parsers: {
		sheetId: parseString,
		xModulus: parseDouble,
		yModulus: parseDouble,
		applyToAll: parseBoolean,
	},
	configGenerators: {
		sheetId: createSheetTableWidgetConfig,
		xModulus: createDoubleWidgetConfig,
		yModulus: createDoubleWidgetConfig,
		applyToAll: createBooleanWidgetConfig,
	},
	resultExtractors: {
		sheetId: stringExtractor,
		xModulus: numberExtractor,
		yModulus: numberExtractor,
		applyToAll: booleanExtractor,
	},
};

const sheetPriceImportUtils: ImportUtils<SheetPrice> = {
	isRow: isSheetPrice,
	parsers: {
		sheetId: parseString,
		pricePerSheet: parseDouble,
	},
	configGenerators: {
		sheetId: createSheetTableWidgetConfig,
		pricePerSheet: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetId: stringExtractor,
		pricePerSheet: numberExtractor,
	},
};

const upperDieGroupImportUtils: ImportUtils<UpperDieGroup> = {
	isRow: isUpperDieGroup,
	parsers: {
		identifier: parseString,
		name: parseString,
		exportIdentifier: parseString,
		radius: parseDouble,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		exportIdentifier: createStringWidgetConfig,
		radius: createDoubleWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		exportIdentifier: stringExtractor,
		radius: numberExtractor,
	},
};

const lowerDieGroupImportUtils: ImportUtils<LowerDieGroup> = {
	isRow: isLowerDieGroup,
	parsers: {
		identifier: parseString,
		name: parseString,
		exportIdentifier: parseString,
		openingWidth: parseDouble,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		exportIdentifier: createStringWidgetConfig,
		openingWidth: createDoubleWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		exportIdentifier: stringExtractor,
		openingWidth: numberExtractor,
	},
};

const bendDeductionImportUtils: ImportUtils<BendDeduction> = {
	isRow: isBendDeduction,
	aliases: {
		sheetBendingMaterialId: [ "bendMaterialId" ],
	},
	parsers: {
		sheetBendingMaterialId: parseString,
		upperDieGroupId: parseString,
		lowerDieGroupId: parseString,
		thickness: parseDouble,
		bendAngle: parseDouble,
		innerRadius: parseDouble,
		sharpDeduction: parseDouble,
	},
	configGenerators: {
		sheetBendingMaterialId: createSheetBendingMaterialTableWidget,
		upperDieGroupId: createUpperDieGroupTableWidgetConfig,
		lowerDieGroupId: createLowerDieGroupTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		bendAngle: createDoubleWidgetConfig,
		innerRadius: createDoubleWidgetConfig,
		sharpDeduction: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetBendingMaterialId: stringExtractor,
		upperDieGroupId: stringExtractor,
		lowerDieGroupId: stringExtractor,
		thickness: numberExtractor,
		bendAngle: numberExtractor,
		innerRadius: numberExtractor,
		sharpDeduction: numberExtractor,
	},
};

const settingImportUtils: ImportUtils<Setting> = {
	isRow: isSetting,
	parsers: {
		key: parseString,
		value: parseString,
	},
	configGenerators: {
		key: createStringWidgetConfig,
		value: createStringWidgetConfig,
	},
	resultExtractors: {
		key: stringExtractor,
		value: stringExtractor,
	},
};

const automaticMechanicalDeburringMaterialImportUtils: ImportUtils<AutomaticMechanicalDeburringMaterial> = {
	isRow: isAutomaticMechanicalDeburringMaterial,
	aliases: {
		sheetMaterialId: [ "globalMaterialId" ],
	},
	parsers: {
		sheetMaterialId: parseString,
		automaticMechanicalDeburringMaterialId: parseString,
	},
	configGenerators: {
		sheetMaterialId: createSheetMaterialTableWidgetConfig,
		automaticMechanicalDeburringMaterialId: createStringWidgetConfig,
	},
	resultExtractors: {
		sheetMaterialId: stringExtractor,
		automaticMechanicalDeburringMaterialId: stringExtractor,
	},
};

const automaticMechanicalDeburringParametersImportUtils: ImportUtils<AutomaticMechanicalDeburringParameters> = {
	isRow: isAutomaticMechanicalDeburringParameters,
	parsers: {
		automaticMechanicalDeburringMaterialId: parseString,
		maxDimY: parseDouble,
		unitTimeBase: parseDouble,
		speed: parseDouble,
	},
	configGenerators: {
		automaticMechanicalDeburringMaterialId: createAutomaticMechanicalDeburringMaterialTableWidgetConfig,
		maxDimY: createDoubleWidgetConfig,
		unitTimeBase: createDoubleWidgetConfig,
		speed: createDoubleWidgetConfig,
	},
	resultExtractors: {
		automaticMechanicalDeburringMaterialId: stringExtractor,
		maxDimY: numberExtractor,
		unitTimeBase: numberExtractor,
		speed: numberExtractor,
	},
};

const dimensionConstraintsImportUtils: ImportUtils<DimensionConstraints> = {
	isRow: isDimensionConstraints,
	parsers: {
		processId: parseString,
		minX: parseDouble,
		minY: parseDouble,
		minZ: parseDouble,
		maxX: parseDouble,
		maxY: parseDouble,
		maxZ: parseDouble,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		minX: createDoubleWidgetConfig,
		minY: createDoubleWidgetConfig,
		minZ: createDoubleWidgetConfig,
		maxX: createDoubleWidgetConfig,
		maxY: createDoubleWidgetConfig,
		maxZ: createDoubleWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		minX: numberExtractor,
		minY: numberExtractor,
		minZ: numberExtractor,
		maxX: numberExtractor,
		maxY: numberExtractor,
		maxZ: numberExtractor,
	},
};

const screwThreadImportUtils: ImportUtils<ScrewThread> = {
	isRow: isScrewThread,
	parsers: {
		identifier: parseString,
		name: parseString,
		coreHoleDiameter: parseDouble,
		minDepth: parseDouble,
		symmetricTolerance: parseDouble,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		coreHoleDiameter: createDoubleWidgetConfig,
		minDepth: createDoubleWidgetConfig,
		symmetricTolerance: createDoubleWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		coreHoleDiameter: numberExtractor,
		minDepth: numberExtractor,
		symmetricTolerance: numberExtractor,
	},
};

const tappingTimeParametersImportUtils: ImportUtils<TappingTimeParameters> = {
	isRow: isTappingTimeParameters,
	parsers: {
		processId: parseString,
		screwThreadId: parseString,
		unitTimePerMm: parseDouble,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		screwThreadId: createScrewThreadTableWidgetConfig,
		unitTimePerMm: createDoubleWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		screwThreadId: stringExtractor,
		unitTimePerMm: numberExtractor,
	},
};

const tubeMaterialImportUtils: ImportUtils<TubeMaterial> = {
	isRow: isTubeMaterial,
	parsers: {
		identifier: parseString,
		name: parseString,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		description: stringExtractor,
	},
};

const tubeMaterialDensityImportUtils: ImportUtils<TubeMaterialDensity> = {
	isRow: isTubeMaterialDensity,
	parsers: {
		tubeMaterialId: parseString,
		density: parseDouble,
	},
	configGenerators: {
		tubeMaterialId: createTubeMaterialTableWidgetConfig,
		density: createDoubleWidgetConfig,
	},
	resultExtractors: {
		tubeMaterialId: stringExtractor,
		density: numberExtractor,
	},
};

const tubeProfileImportUtils: ImportUtils<TubeProfile> = {
	isRow: isTubeProfile,
	parsers: {
		identifier: parseString,
		name: parseString,
		description: parseString,
		geometryJson: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		description: createStringWidgetConfig,
		geometryJson: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		description: stringExtractor,
		geometryJson: stringExtractor,
	},
};

const tubeSpecificationImportUtils: ImportUtils<TubeSpecification> = {
	isRow: isTubeMaterial,
	parsers: {
		identifier: parseString,
		name: parseString,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		description: stringExtractor,
	},
};

const tubeImportUtils: ImportUtils<Tube> = {
	isRow: isTube,
	parsers: {
		identifier: parseString,
		name: parseString,
		tubeMaterialId: parseString,
		tubeProfileId: parseString,
		tubeSpecificationId: parseString,
		dimX: parseDouble,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		tubeMaterialId: createTubeMaterialTableWidgetConfig,
		tubeProfileId: createTubeProfileTableWidgetConfig,
		tubeSpecificationId: createTubeSpecificationTableWidgetConfig,
		dimX: createDoubleWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		tubeMaterialId: stringExtractor,
		tubeProfileId: stringExtractor,
		tubeSpecificationId: stringExtractor,
		dimX: numberExtractor,
	},
};

const upperDieImportUtils: ImportUtils<UpperDie> = {
	isRow: isUpperDie,
	parsers: {
		identifier: parseString,
		name: parseString,
		upperDieGroupId: parseString,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		upperDieGroupId: createUpperDieGroupTableWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		upperDieGroupId: stringExtractor,
		description: stringExtractor,
	},
};

const lowerDieImportUtils: ImportUtils<LowerDie> = {
	isRow: isLowerDie,
	parsers: {
		identifier: parseString,
		name: parseString,
		lowerDieGroupId: parseString,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		lowerDieGroupId: createLowerDieGroupTableWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		lowerDieGroupId: stringExtractor,
		description: stringExtractor,
	},
};

const upperDieUnitImportUtils: ImportUtils<UpperDieUnit> = {
	isRow: isUpperDieUnit,
	parsers: {
		upperDieId: parseString,
		dimX: parseDouble,
		multiplicity: parseInt,
	},
	configGenerators: {
		upperDieId: createUpperDieTableWidgetConfig,
		dimX: createDoubleWidgetConfig,
		multiplicity: createIntWidgetConfig,
	},
	resultExtractors: {
		upperDieId: stringExtractor,
		dimX: numberExtractor,
		multiplicity: numberExtractor,
	},
};

const lowerDieUnitImportUtils: ImportUtils<LowerDieUnit> = {
	isRow: isLowerDieUnit,
	parsers: {
		lowerDieId: parseString,
		dimX: parseDouble,
		multiplicity: parseInt,
	},
	configGenerators: {
		lowerDieId: createLowerDieTableWidgetConfig,
		dimX: createDoubleWidgetConfig,
		multiplicity: createIntWidgetConfig,
	},
	resultExtractors: {
		lowerDieId: stringExtractor,
		dimX: numberExtractor,
		multiplicity: numberExtractor,
	},
};

const processHandlingTimeImportUtils: ImportUtils<ProcessHandlingTime> = {
	isRow: isProcessHandlingTime,
	parsers: {
		processId: parseString,
		mass: parseDouble,
		setupTimeDelta: parseDouble,
		unitTimeDelta: parseDouble,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		mass: createDoubleWidgetConfig,
		setupTimeDelta: createDoubleWidgetConfig,
		unitTimeDelta: createDoubleWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		mass: numberExtractor,
		setupTimeDelta: numberExtractor,
		unitTimeDelta: numberExtractor,
	},
};

const sheetStockImportUtils: ImportUtils<SheetStock> = {
	isRow: isSheetStock,
	parsers: {
		sheetId: parseString,
		count: parseInt,
	},
	configGenerators: {
		sheetId: createSheetTableWidgetConfig,
		count: createIntWidgetConfig,
	},
	resultExtractors: {
		sheetId: stringExtractor,
		count: numberExtractor,
	},
};

const processIdlePeriodUtils: ImportUtils<ProcessIdlePeriod> = {
	isRow: isProcessIdlePeriod,
	parsers: {
		processId: parseString,
		time: parseDouble,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		time: createDoubleWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		time: numberExtractor,
	},
};

const sheetMaterialScrapValueUtils: ImportUtils<SheetMaterialScrapValue> = {
	isRow: isSheetMaterialScrapValue,
	parsers: {
		sheetMaterialId: parseString,
		scrapValue: parseDouble,
	},
	configGenerators: {
		sheetMaterialId: createSheetMaterialTableWidgetConfig,
		scrapValue: createIntWidgetConfig,
	},
	resultExtractors: {
		sheetMaterialId: stringExtractor,
		scrapValue: numberExtractor,
	},
};

const sheetPriorityUtils: ImportUtils<SheetPriority> = {
	isRow: isSheetPriority,
	parsers: {
		sheetId: parseString,
		priority: parseInt,
	},
	configGenerators: {
		sheetId: createSheetTableWidgetConfig,
		priority: createIntWidgetConfig,
	},
	resultExtractors: {
		sheetId: stringExtractor,
		priority: numberExtractor,
	},
};

const dieGroupPriorityUtils: ImportUtils<DieGroupPriority> = {
	isRow: isDieGroupPriority,
	aliases: {
		sheetBendingMaterialId: [ "bendMaterialId" ],
	},
	parsers: {
		upperDieGroupId: parseString,
		lowerDieGroupId: parseString,
		sheetBendingMaterialId: parseString,
		sheetThickness: parseDouble,
		priority: parseInt,
	},
	configGenerators: {
		upperDieGroupId: createUpperDieGroupTableWidgetConfig,
		lowerDieGroupId: createLowerDieGroupTableWidgetConfig,
		sheetBendingMaterialId: createSheetCuttingMatrialTableWidgetConfig,
		sheetThickness: createDoubleWidgetConfig,
		priority: createIntWidgetConfig,
	},
	resultExtractors: {
		upperDieGroupId: stringExtractor,
		lowerDieGroupId: stringExtractor,
		sheetBendingMaterialId: stringExtractor,
		sheetThickness: numberExtractor,
		priority: numberExtractor,
	},
};

const sheetCuttingMaterialUtils: ImportUtils<SheetCuttingMaterial> = {
	isRow: isSheetCuttingMaterial,
	parsers: {
		identifier: parseString,
		name: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
	},
};

const sheetBendingMaterialUtils: ImportUtils<SheetBendingMaterial> = {
	isRow: isSheetBendingMaterial,
	parsers: {
		identifier: parseString,
		name: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
	},
};

const tubeCuttingProcessUtils: ImportUtils<TubeCuttingProcess> = {
	isRow: isTubeCuttingProcess,
	parsers: {
		identifier: parseString,
		name: parseString,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		description: stringExtractor,
	},
};

const tubeCuttingProcessMappingUtils: ImportUtils<TubeCuttingProcessMapping> = {
	isRow: isTubeCuttingProcessMapping,
	parsers: {
		processId: parseString,
		tubeMaterialId: parseString,
		tubeCuttingProcessId: parseString,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		tubeMaterialId: createTubeMaterialTableWidgetConfig,
		tubeCuttingProcessId: createTubeCuttingProcessTableWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		tubeMaterialId: stringExtractor,
		tubeCuttingProcessId: stringExtractor,
	},
};

const tubeCuttingSpeedUtils: ImportUtils<TubeCuttingSpeed> = {
	isRow: isTubeCuttingSpeed,
	parsers: {
		tubeCuttingProcessId: parseString,
		thickness: parseDouble,
		speed: parseDouble,
	},
	configGenerators: {
		tubeCuttingProcessId: createProcessTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		speed: createDoubleWidgetConfig,
	},
	resultExtractors: {
		tubeCuttingProcessId: stringExtractor,
		thickness: numberExtractor,
		speed: numberExtractor,
	},
};

const tubeCuttingPierceTimeUtils: ImportUtils<TubeCuttingPierceTime> = {
	isRow: isTubeCuttingPierceTime,
	parsers: {
		tubeCuttingProcessId: parseString,
		thickness: parseDouble,
		time: parseDouble,
	},
	configGenerators: {
		tubeCuttingProcessId: createProcessTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		time: createDoubleWidgetConfig,
	},
	resultExtractors: {
		tubeCuttingProcessId: stringExtractor,
		thickness: numberExtractor,
		time: numberExtractor,
	},
};

const tubePriceUtils: ImportUtils<TubePrice> = {
	isRow: isTubePrice,
	parsers: {
		tubeId: parseString,
		pricePerTube: parseDouble,
	},
	configGenerators: {
		tubeId: createTubeTableWidgetConfig,
		pricePerTube: createDoubleWidgetConfig,
	},
	resultExtractors: {
		tubeId: stringExtractor,
		pricePerTube: numberExtractor,
	},
};

const tubeStockUtils: ImportUtils<TubeStock> = {
	isRow: isTubeStock,
	parsers: {
		tubeId: parseString,
		count: parseInt,
	},
	configGenerators: {
		tubeId: createTubeTableWidgetConfig,
		count: createIntWidgetConfig,
	},
	resultExtractors: {
		tubeId: stringExtractor,
		count: numberExtractor,
	},
};

const tubeMaterialScrapValue: ImportUtils<TubeMaterialScrapValue> = {
	isRow: isTubeMaterialScrapValue,
	parsers: {
		tubeMaterialId: parseString,
		scrapValue: parseDouble,
	},
	configGenerators: {
		tubeMaterialId: createTubeTableWidgetConfig,
		scrapValue: createDoubleWidgetConfig,
	},
	resultExtractors: {
		tubeMaterialId: stringExtractor,
		scrapValue: numberExtractor,
	},
};

const sheetCuttingProcessUtils: ImportUtils<SheetCuttingProcess> = {
	isRow: isSheetCuttingProcess,
	parsers: {
		identifier: parseString,
		name: parseString,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		description: stringExtractor,
	},
};

const sheetCuttingProcessMappingUtils: ImportUtils<SheetCuttingProcessMapping> = {
	isRow: isSheetCuttingProcessMapping,
	parsers: {
		processId: parseString,
		sheetMaterialId: parseString,
		sheetCuttingProcessId: parseString,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		sheetMaterialId: createSheetMaterialTableWidgetConfig,
		sheetCuttingProcessId: createSheetCuttingProcessTableWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		sheetMaterialId: stringExtractor,
		sheetCuttingProcessId: stringExtractor,
	},
};

const sheetCuttingMotionParametersUtils: ImportUtils<SheetCuttingMotionParameters> = {
	isRow: isSheetCuttingMotionParameters,
	parsers: {
		sheetCuttingProcessId: parseString,
		thickness: parseDouble,
		contourArea: parseDouble,
		speed: parseDouble,
		acceleration: parseDouble,
		pierceTime: parseDouble,
	},
	configGenerators: {
		sheetCuttingProcessId: createSheetCuttingProcessTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		contourArea: createDoubleWidgetConfig,
		speed: createDoubleWidgetConfig,
		acceleration: createDoubleWidgetConfig,
		pierceTime: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingProcessId: stringExtractor,
		thickness: numberExtractor,
		contourArea: numberExtractor,
		speed: numberExtractor,
		acceleration: numberExtractor,
		pierceTime: numberExtractor,
	},
};

const sheetCuttingProcessToLaserCuttingGasUtils: ImportUtils<SheetCuttingProcessToLaserCuttingGas> = {
	isRow: isSheetCuttingProcessToLaserCuttingGas,
	parsers: {
		sheetCuttingProcessId: parseString,
		laserSheetCuttingGasId: parseString,
	},
	configGenerators: {
		sheetCuttingProcessId: createSheetCuttingProcessTableWidgetConfig,
		laserSheetCuttingGasId: createLaserSheetCuttingGasTableWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingProcessId: stringExtractor,
		laserSheetCuttingGasId: stringExtractor,
	},
};

const purchasePartMaterialUtils: ImportUtils<PurchasePartMaterial> = {
	isRow: isPurchasePartMaterial,
	parsers: {
		identifier: parseString,
		name: parseString,
		density: parseDouble,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		density: createDoubleWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		density: numberExtractor,
		description: stringExtractor,
	},
};

const processConstraintsSheetMaterialUtils: ImportUtils<ProcessConstraintsSheetMaterial> = {
	isRow: isProcessConstraintsSheetMaterial,
	parsers: {
		processId: parseString,
		sheetMaterialId: parseString,
		isAllowed: parseBoolean,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		sheetMaterialId: createSheetMaterialTableWidgetConfig,
		isAllowed: createBooleanWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		sheetMaterialId: stringExtractor,
		isAllowed: booleanExtractor,
	},
};

const processConstraintsTubeMaterialUtils: ImportUtils<ProcessConstraintsTubeMaterial> = {
	isRow: isProcessConstraintsTubeMaterial,
	parsers: {
		processId: parseString,
		tubeMaterialId: parseString,
		isAllowed: parseBoolean,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		tubeMaterialId: createTubeMaterialTableWidgetConfig,
		isAllowed: createBooleanWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		tubeMaterialId: stringExtractor,
		isAllowed: booleanExtractor,
	},
};

const processConstraintsPurchasePartMaterialUtils: ImportUtils<ProcessConstraintsPurchasePartMaterial> = {
	isRow: isProcessConstraintsPurchasePartMaterial,
	parsers: {
		processId: parseString,
		purchasePartMaterialId: parseString,
		isAllowed: parseBoolean,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		purchasePartMaterialId: createPurchasePartMaterialTableWidgetConfig,
		isAllowed: createBooleanWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		purchasePartMaterialId: stringExtractor,
		isAllowed: booleanExtractor,
	},
};

const consumableUtils: ImportUtils<Consumable> = {
	isRow: isConsumable,
	parsers: {
		identifier: parseString,
		name: parseString,
		unit: parseString,
		costsPerUnit: parseDouble,
		description: parseString,
	},
	configGenerators: {
		identifier: createStringWidgetConfig,
		name: createStringWidgetConfig,
		unit: createStringWidgetConfig,
		costsPerUnit: createDoubleWidgetConfig,
		description: createStringWidgetConfig,
	},
	resultExtractors: {
		identifier: stringExtractor,
		name: stringExtractor,
		unit: stringExtractor,
		costsPerUnit: numberExtractor,
		description: stringExtractor,
	},
};

const processConsumableRateUtils: ImportUtils<ProcessConsumableRate> = {
	isRow: isProcessConsumableRate,
	parsers: {
		processId: parseString,
		consumableId: parseString,
		unitsPerHour: parseDouble,
	},
	configGenerators: {
		processId: createProcessTableWidgetConfig,
		consumableId: createConsumableTableWidgetConfig,
		unitsPerHour: createDoubleWidgetConfig,
	},
	resultExtractors: {
		processId: stringExtractor,
		consumableId: stringExtractor,
		unitsPerHour: numberExtractor,
	},
};

const sheetCuttingProcessConsumableRateUtils: ImportUtils<SheetCuttingProcessConsumableRate> = {
	isRow: isSheetCuttingProcessConsumableRate,
	parsers: {
		sheetCuttingProcessId: parseString,
		consumableId: parseString,
		thickness: parseDouble,
		unitsPerHour: parseDouble,
	},
	configGenerators: {
		sheetCuttingProcessId: createSheetCuttingProcessTableWidgetConfig,
		consumableId: createConsumableTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		unitsPerHour: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingProcessId: stringExtractor,
		consumableId: stringExtractor,
		thickness: numberExtractor,
		unitsPerHour: numberExtractor,
	},
};

const tubeCuttingProcessConsumableRateUtils: ImportUtils<TubeCuttingProcessConsumableRate> = {
	isRow: isTubeCuttingProcessConsumableRate,
	parsers: {
		tubeCuttingProcessId: parseString,
		consumableId: parseString,
		thickness: parseDouble,
		unitsPerHour: parseDouble,
	},
	configGenerators: {
		tubeCuttingProcessId: createTubeCuttingProcessTableWidgetConfig,
		consumableId: createConsumableTableWidgetConfig,
		thickness: createDoubleWidgetConfig,
		unitsPerHour: createDoubleWidgetConfig,
	},
	resultExtractors: {
		tubeCuttingProcessId: stringExtractor,
		consumableId: stringExtractor,
		thickness: numberExtractor,
		unitsPerHour: numberExtractor,
	},
};

const sheetCuttingThicknessConstraintsUtils: ImportUtils<SheetCuttingThicknessConstraints> = {
	isRow: isSheetCuttingThicknessConstraints,
	parsers: {
		sheetCuttingProcessId: parseString,
		minThickness: parseDouble,
		maxThickness: parseDouble,
	},
	configGenerators: {
		sheetCuttingProcessId: createSheetCuttingProcessTableWidgetConfig,
		minThickness: createDoubleWidgetConfig,
		maxThickness: createDoubleWidgetConfig,
	},
	resultExtractors: {
		sheetCuttingProcessId: stringExtractor,
		minThickness: numberExtractor,
		maxThickness: numberExtractor,
	},
};

/**
 * Create AnyTable from potentially invalid / incomplete input
 *
 * If input cannot be deduced the user will be asked to complete the input.
 * @param input The input
 * @returns undefined if table cannot be completed and user canceles; completed AnyTable else
 */
export function convertToAnyTable(input: PotentialAnyTable, interactive = false): AnyTable|undefined {
	const buildAnyTable = <Type extends keyof TableTypeMap>(type: Type, content: undefined | TableTypeMap[Type][]) => {
		if (content === undefined) {
			return undefined;
		}
		return {
			type: type,
			content: content,
		};
	};
	switch (input.type) {
		case "sheetMaterial": return buildAnyTable("sheetMaterial", convertToTable(input.content, sheetMaterialImportUtils, interactive));
		case "sheetMaterialDensity": return buildAnyTable("sheetMaterialDensity", convertToTable(input.content, sheetMaterialDensityImportUtils, interactive));
		case "sheetCuttingMaterialMapping": return buildAnyTable("sheetCuttingMaterialMapping", convertToTable(input.content, sheetCuttingMaterialMappingUtils, interactive));
		case "sheetBendingMaterialMapping": return buildAnyTable("sheetBendingMaterialMapping", convertToTable(input.content, sheetBendingMaterialMappingUtils, interactive));
		case "bendTime": return buildAnyTable("bendTime", convertToTable(input.content, bendTimeImportUtils, interactive));
		case "bendTimeParameters": return buildAnyTable("bendTimeParameters", convertToTable(input.content, bendTimeParametersImportUtils, interactive));
		case "bendRateParameters": return buildAnyTable("bendRateParameters", convertToTable(input.content, bendRateParametersImportUtils, interactive));
		case "bendLineConstraint": return buildAnyTable("bendLineConstraint", convertToTable(input.content, bendLineConstraintImportUtils, interactive));
		case "laserSheetCuttingGas": return buildAnyTable("laserSheetCuttingGas", convertToTable(input.content, laserSheetCuttingGasImportUtils, interactive));
		case "laserSheetCuttingSpeed": return buildAnyTable("laserSheetCuttingSpeed", convertToTable(input.content, laserSheetCuttingSpeedImportUtils, interactive));
		case "laserSheetCuttingPierceTime": return buildAnyTable("laserSheetCuttingPierceTime", convertToTable(input.content, laserSheetCuttingPierceTimeImportUtils, interactive));
		case "laserSheetCuttingRate": return buildAnyTable("laserSheetCuttingRate", convertToTable(input.content, laserSheetCuttingRateImportUtils, interactive));
		case "laserSheetCuttingMinArea": return buildAnyTable("laserSheetCuttingMinArea", convertToTable(input.content, laserSheetCuttingMinAreaImportUtils, interactive));
		case "laserSheetCuttingMaxThickness": return buildAnyTable("laserSheetCuttingMaxThickness", convertToTable(input.content, laserSheetCuttingMaxThicknessImportUtils, interactive));
		case "packaging": return buildAnyTable("packaging", convertToTable(input.content, packagingImportUtils, interactive));
		case "transportationCosts": return buildAnyTable("transportationCosts", convertToTable(input.content, transportationCostsImportUtils, interactive));
		case "surcharge": return buildAnyTable("surcharge", convertToTable(input.content, surchargeImportUtils, interactive));
		case "process": return buildAnyTable("process", convertToTable(input.content, processImportUtils, interactive));
		case "processRate": return buildAnyTable("processRate", convertToTable(input.content, processRateImportUtils, interactive));
		case "processSetupTimeFallback": return buildAnyTable("processSetupTimeFallback", convertToTable(input.content, processSetupTimeFallbackImportUtils, interactive));
		case "processUnitTimeFallback": return buildAnyTable("processUnitTimeFallback", convertToTable(input.content, processUnitTimeFallbackImportUtils, interactive));
		case "sheet": return buildAnyTable("sheet", convertToTable(input.content, sheetImportUtils, interactive));
		case "sheetModulus": return buildAnyTable("sheetModulus", convertToTable(input.content, sheetModulusImportUtils, interactive));
		case "sheetPrice": return buildAnyTable("sheetPrice", convertToTable(input.content, sheetPriceImportUtils, interactive));
		case "upperDieGroup": return buildAnyTable("upperDieGroup", convertToTable(input.content, upperDieGroupImportUtils, interactive));
		case "lowerDieGroup": return buildAnyTable("lowerDieGroup", convertToTable(input.content, lowerDieGroupImportUtils, interactive));
		case "bendDeduction": return buildAnyTable("bendDeduction", convertToTable(input.content, bendDeductionImportUtils, interactive));
		case "setting": return buildAnyTable("setting", convertToTable(input.content, settingImportUtils, interactive));
		case "automaticMechanicalDeburringMaterial": return buildAnyTable("automaticMechanicalDeburringMaterial", convertToTable(input.content, automaticMechanicalDeburringMaterialImportUtils, interactive));
		case "automaticMechanicalDeburringParameters":
			return buildAnyTable("automaticMechanicalDeburringParameters", convertToTable(input.content, automaticMechanicalDeburringParametersImportUtils, interactive));
		case "dimensionConstraints": return buildAnyTable("dimensionConstraints", convertToTable(input.content, dimensionConstraintsImportUtils, interactive));
		case "screwThread": return buildAnyTable("screwThread", convertToTable(input.content, screwThreadImportUtils, interactive));
		case "tappingTimeParameters": return buildAnyTable("tappingTimeParameters", convertToTable(input.content, tappingTimeParametersImportUtils, interactive));
		case "tubeMaterial": return buildAnyTable("tubeMaterial", convertToTable(input.content, tubeMaterialImportUtils, interactive));
		case "tubeMaterialDensity": return buildAnyTable("tubeMaterialDensity", convertToTable(input.content, tubeMaterialDensityImportUtils, interactive));
		case "tubeProfile": return buildAnyTable("tubeProfile", convertToTable(input.content, tubeProfileImportUtils, interactive));
		case "tubeSpecification": return buildAnyTable("tubeSpecification", convertToTable(input.content, tubeSpecificationImportUtils, interactive));
		case "tube": return buildAnyTable("tube", convertToTable(input.content, tubeImportUtils, interactive));
		case "upperDie": return buildAnyTable("upperDie", convertToTable(input.content, upperDieImportUtils, interactive));
		case "lowerDie": return buildAnyTable("lowerDie", convertToTable(input.content, lowerDieImportUtils, interactive));
		case "upperDieUnit": return buildAnyTable("upperDieUnit", convertToTable(input.content, upperDieUnitImportUtils, interactive));
		case "lowerDieUnit": return buildAnyTable("lowerDieUnit", convertToTable(input.content, lowerDieUnitImportUtils, interactive));
		case "processHandlingTime": return buildAnyTable("processHandlingTime", convertToTable(input.content, processHandlingTimeImportUtils, interactive));
		case "sheetStock": return buildAnyTable("sheetStock", convertToTable(input.content, sheetStockImportUtils, interactive));
		case "processIdlePeriod": return buildAnyTable("processIdlePeriod", convertToTable(input.content, processIdlePeriodUtils, interactive));
		case "sheetMaterialScrapValue": return buildAnyTable("sheetMaterialScrapValue", convertToTable(input.content, sheetMaterialScrapValueUtils, interactive));
		case "sheetPriority": return buildAnyTable("sheetPriority", convertToTable(input.content, sheetPriorityUtils, interactive));
		case "dieGroupPriority": return buildAnyTable("dieGroupPriority", convertToTable(input.content, dieGroupPriorityUtils, interactive));
		case "sheetCuttingMaterial": return buildAnyTable("sheetCuttingMaterial", convertToTable(input.content, sheetCuttingMaterialUtils, interactive));
		case "sheetBendingMaterial": return buildAnyTable("sheetBendingMaterial", convertToTable(input.content, sheetBendingMaterialUtils, interactive));
		case "tubeCuttingProcess": return buildAnyTable("tubeCuttingProcess", convertToTable(input.content, tubeCuttingProcessUtils, interactive));
		case "tubeCuttingProcessMapping": return buildAnyTable("tubeCuttingProcessMapping", convertToTable(input.content, tubeCuttingProcessMappingUtils, interactive));
		case "tubeCuttingSpeed": return buildAnyTable("tubeCuttingSpeed", convertToTable(input.content, tubeCuttingSpeedUtils, interactive));
		case "tubeCuttingPierceTime": return buildAnyTable("tubeCuttingPierceTime", convertToTable(input.content, tubeCuttingPierceTimeUtils, interactive));
		case "tubePrice": return buildAnyTable("tubePrice", convertToTable(input.content, tubePriceUtils, interactive));
		case "tubeStock": return buildAnyTable("tubeStock", convertToTable(input.content, tubeStockUtils, interactive));
		case "tubeMaterialScrapValue": return buildAnyTable("tubeMaterialScrapValue", convertToTable(input.content, tubeMaterialScrapValue, interactive));
		case "sheetCuttingProcess": return buildAnyTable("sheetCuttingProcess", convertToTable(input.content, sheetCuttingProcessUtils, interactive));
		case "sheetCuttingProcessMapping": return buildAnyTable("sheetCuttingProcessMapping", convertToTable(input.content, sheetCuttingProcessMappingUtils, interactive));
		case "sheetCuttingMotionParameters": return buildAnyTable("sheetCuttingMotionParameters", convertToTable(input.content, sheetCuttingMotionParametersUtils, interactive));
		case "sheetCuttingProcessToLaserCuttingGas": return buildAnyTable("sheetCuttingProcessToLaserCuttingGas", convertToTable(input.content, sheetCuttingProcessToLaserCuttingGasUtils, interactive));
		case "purchasePartMaterial": return buildAnyTable("purchasePartMaterial", convertToTable(input.content, purchasePartMaterialUtils, interactive));
		case "processConstraintsSheetMaterial": return buildAnyTable("processConstraintsSheetMaterial", convertToTable(input.content, processConstraintsSheetMaterialUtils, interactive));
		case "processConstraintsTubeMaterial": return buildAnyTable("processConstraintsTubeMaterial", convertToTable(input.content, processConstraintsTubeMaterialUtils, interactive));
		case "processConstraintsPurchasePartMaterial": return buildAnyTable("processConstraintsPurchasePartMaterial", convertToTable(input.content, processConstraintsPurchasePartMaterialUtils, interactive));
		case "consumable": return buildAnyTable("consumable", convertToTable(input.content, consumableUtils, interactive));
		case "processConsumableRate": return buildAnyTable("processConsumableRate", convertToTable(input.content, processConsumableRateUtils, interactive));
		case "sheetCuttingProcessConsumableRate": return buildAnyTable("sheetCuttingProcessConsumableRate", convertToTable(input.content, sheetCuttingProcessConsumableRateUtils, interactive));
		case "tubeCuttingProcessConsumableRate": return buildAnyTable("tubeCuttingProcessConsumableRate", convertToTable(input.content, tubeCuttingProcessConsumableRateUtils, interactive));
		case "sheetCuttingThicknessConstraints": return buildAnyTable("sheetCuttingThicknessConstraints", convertToTable(input.content, sheetCuttingThicknessConstraintsUtils, interactive));
	}
}
