namespace uniflash {

	export class DSCore implements IDSService {
		constructor(
			protected coreName: string,
			protected ds: DS) {
		}

		setProperties() {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				return core.settings.set(this.ds.getUfPropertiesValue().getPropertiesValuesMap(this.ds.getContext().getCurrentCore().name));
			}, this.coreName);
		}

		getProperties() {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				return core.settings.get([])
					.then((ret) => {
						return ret.nameValueMap;
					});
			}, this.coreName);
		}

		getTargetInfo() {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				return core.getTargetInfo();
			}, this.coreName);
		}

		getResets(optionsObj?) {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				const keepAllResets = (optionsObj && optionsObj.keepAllResets) ? optionsObj.keepAllResets : false;
				return core.targetState.getResets()
					.then((ret) => {
						let availableResetOptions = [];
						_.forOwn(ret.nameValueMap, (value, key) => {
							if (keepAllResets || value) {
								availableResetOptions.push(key);
							}
						});
						return availableResetOptions;
					});
			}, this.coreName);
		}

		performReset(reset: string, optionsObj?) {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				if (optionsObj && optionsObj.DisconnectBeforeReset) {
					return core.targetState.disconnect()
						.then(() => {
							return this.ds._q((resolve, reject) => {
								setTimeout(() => {
									resolve(core.targetState.reset(reset));
								}, 1000);
							});
						})
						.finally(() => {
							this.ds.dsImpl.disconnectAllCores();
						});
				}
				else {
					return core.targetState.reset(reset);
				}
			}, this.coreName, optionsObj);
		}

		program(programs: uniflash.programs.Program[]) {
			let options = {};

			// set the runAfterOp flag (do not run for serial devices or if user checkbox is off)
			options['runAfterOp'] = (!this.ds.isSerial() && this.ds.getRunTargetAfterProgramLoad());

			return this.ds.dsImpl.completeWorkflowAction((core) => {
				let propsMap = this.ds.getUfPropertiesValue().getPropertiesValuesMap(this.ds.getContext().getCurrentCore().name);
				if (!(this.ds.dsImpl instanceof DSLprfImpl)) {
					propsMap['ResetOnRestart'] = 1;
				}
				let finalRet;
				return core.settings.set(propsMap)
					.then(() => {
						return this.ds.writeFiles(programs);
					})
					.then((dsProgramFiles: uniflash.programs.Program[]) => {
						return core.symbols.loadProgramOnly(dsProgramFiles);
					})
					.then((ret) => {
						this.ds.getConsole().addMessage(this.ds.getConsole().msgTypes.STATUS, 'Program Load completed successfully.');
						finalRet = ret;
						const reset = this.ds._ufResets.getReset();
						if (reset.resetAfterLoad && reset.selectedResetOption) {
							// since this reset is part of a program operation, add [disconnect = false] option
							// to prevent disconnecting right after the reset is issued
							return this.performReset(reset.selectedResetOption, { disconnect: false });
						}
					})
					.then(() => {
						// wait for idle (ie; OnFileLoaded() to complete) before disconnecting from the device
						return this.ds.agent.DS.waitForIdle();
					})
					.then(() => {
						return finalRet;
					});

			}, this.coreName, options);
		}

		verify(programs: uniflash.programs.Program[]) {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				return this.ds.writeFiles(programs)
					.then((dsProgramFiles) => {
						return core.symbols.verifyProgram(dsProgramFiles);
					});
			}, this.coreName);
		}

		flashOperation(opStr) {
			let options = {};
			if (opStr.substring(0, 4) === 'NCO_') {
				// support for Non-connect operations (NCO) by setting connect option to false
				options['connect'] = false;
			}

			// set the runAfterOp flag (do not run for serial devices, if user checkbox is off, or operation is unlock)
			options['runAfterOp'] = (!this.ds.isSerial() && this.ds.getRunTargetAfterProgramLoad() && (opStr.toLowerCase().indexOf('unlock') === -1));

			return this.ds.dsImpl.completeWorkflowAction((core) => {
				return core.settings.set(this.ds.getUfPropertiesValue().getPropertiesValuesMap(this.ds.getContext().getCurrentCore().name))
					.then(() => {
						return core.flash.performOperation(opStr);
					});
			}, this.coreName, options);
		}

		readMemory(addr, typeSize, length, format, optionsObj?) {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				return core.memory.read(addr, typeSize, length, format);
			}, this.coreName);
		}

		exportMemoryAsCoff(ranges: uniflash.memory.Range[], filePath: string, optionsObj?) {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				filePath = filePath ? filePath : 'export_memory.out';
				let fullPath;
				return this.ds.dsImpl.writeLocalFile(filePath, '')
					.then((obj) => {
						fullPath = obj.path;
						return core.memory.exportAsCoff(ranges, fullPath);
					}).then((result) => {
						let len = 0;
						_.each(ranges, function (range) {
							len += range.length;
						});
						return { res: result, path: fullPath, length: len };
					});
			}, this.coreName);
		}

		exportMemoryAsBin(range: uniflash.memory.Range, mauSizeInBits, filePath: string, optionsObj?) {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				filePath = filePath ? filePath : 'export_memory.bin';
				let fullPath;
				return this.ds.dsImpl.writeLocalFile(filePath, '')
					.then((obj) => {
						fullPath = obj.path;
						return core.memory.exportAsBin(range, mauSizeInBits, fullPath);
					}).then((result) => {
						return { res: result, path: fullPath, length: range.length };
					});
			}, this.coreName);
		}

		evaluate(expression: string) {
			return this.ds.dsImpl.completeWorkflowAction((core) => {
				return core.expressions.evaluate(expression);
			}, this.coreName);
		}
	}
}
