namespace uniflash {
	export class DSLprfImpl extends DSDefaultImpl {
		private static deviceFilter = [/cc13\d\d/i, /cc26\d\d/i, /cc2538/i];
		private static icePickName = 'IcePick_C';
		public static matches(partnum: string): boolean {
			for (let filter of DSLprfImpl.deviceFilter) {
				if (filter.test(partnum)) {
					return true;
				}
			}
			//nothing matches
			return false;
		}

		constructor(
			protected ds,
			protected $q,
			protected $rootScope,
			protected $log,
			protected ticAlerts,
			protected ufSession,
			protected ufPrograms,
			protected ufPropertiesValue,
			protected ufAgentInstallDialog,
			protected ufPreferencesSrv,
			protected ufContext,
			protected ufTargetConfig,
			protected ufConsole,
			protected x2js
		) {
			super(
				ds,
				$q,
				$rootScope,
				$log,
				ticAlerts,
				ufSession,
				ufPrograms,
				ufPropertiesValue,
				ufAgentInstallDialog,
				ufPreferencesSrv,
				ufContext,
				ufTargetConfig,
				ufConsole,
				x2js);
		}

		// use a ccxml that can allow us to detect for unlock and to perform a mass erase on the icepick core
		private modifyCCXML() {
			const connection = this.ufSession.getConnectionXMLFile();
			let ccxmlName = "";

			if (connection === "TIXDS110_Connection") {
				ccxmlName = "cc13xx_cc26xx_2pin_cJTAG_XDS110.ccxml";
			} else if (connection === "TIXDS100v3_Dot7_Connection") {
				ccxmlName = "cc13xx_cc26xx_2pin_cJTAG_XDS100v3.ccxml";
			} else if (connection === "TIXDS2XXUSB_Connection" || connection === "TIXDS2XXUSB_onboard_Connection") {
				ccxmlName = "cc13xx_cc26xx_2pin_cJTAG_XDS200.ccxml";
			}

			return this.ufTargetConfig.getFileContent("ccs_base/arm/" + ccxmlName);
		}

		private handleLockedScenario(): angular.IPromise<string> {
			let m3CorePath;
			let customDsConfigObj;
			let modifiedCCXML;

			return this.modifyCCXML()
				.then((ccxmlContent: any) => {
					let deferred = this.$q.defer();
					if (ccxmlContent instanceof Blob) {
						var reader = new FileReader();
						reader.onload = function (readContent: any) {
							modifiedCCXML = readContent.currentTarget.result;
							deferred.resolve();
						};
						reader.readAsText(ccxmlContent);
						return deferred.promise;
					} else {
						modifiedCCXML = ccxmlContent;
					}
				})
				.then(() => {
					return this.deconfigureDS(true);
				})
				.then(() => {
					return this.configureDS(modifiedCCXML, 'uniflash_modified_temp.ccxml');
				})
				.then((dsconfigObj) => {
					// find icepick path and connect to icepick
					customDsConfigObj = dsconfigObj;
					m3CorePath = dsconfigObj.configData.cores[0];
					return this.connectToCore(DSLprfImpl.icePickName, dsconfigObj, true);
				})
				// read registers and figure out if it is a valid device and if it is locked
				.then((core) => {
					if (core) {
						return core.expressions.evaluate('IP_TAP_CNTL_0')
							.then((registerVal) => {
								this.$log.debug('IP_TAP_CNTL_0 = ' + registerVal.value);
								let isLocked = !((parseInt(registerVal.value, 16) >> 1) & 1);
								this.$log.debug('isLocked = ' + isLocked);
								return isLocked;
							});
					} else {
						throw { message: 'Cannot connect to IcePick.' };
					}
				})
				.then((isLocked) => {
					if (isLocked) {
						// reject need to be catched
						const modifiedCCXML2 = modifiedCCXML;
						return this.ticAlerts.showConfirm('This device is locked', 'Do you want to do a mass erase to unlock it? The flash memory on this device will be wiped.')
							.then(() => {
								return this.deconfigureDS(true);
							})
							.then(() => {
								return this.configureDS(modifiedCCXML2, 'uniflash_modified_temp.ccxml');
							})
							.then((dsconfigObj) => {
								// find icepick path and connect to icepick
								customDsConfigObj = dsconfigObj;
								m3CorePath = dsconfigObj.configData.cores[0];
								return this.massErase(m3CorePath, customDsConfigObj);
							});
					}
				})
				.finally(() => {
					return this.deconfigureDS(true);
				});
		}

		private massErase(path, dsConfigObj) {
			//run gel expression to massErase on cortex m3
			this.$log.debug('running massErase()');
			return this.getCore(path, dsConfigObj)
				.then((core) => {
					return core.expressions.evaluate('MassErase()');
				});
		}

		completeWorkflowAction(mainAction, coreName: string, optionObj: any) {
			this.$rootScope.$emit('ds.opStarted');

			let curCore;
			let handleLockIfNecessary = (optionObj && optionObj.DoNotHandleUnlock) ? this.$q.resolve('') : this.handleLockedScenario();
			return handleLockIfNecessary
				.then(() => {
					return this.configureDS(this.ufSession.getCCXML(), 'uniflash_temp.ccxml');
				})
				.then((ret) => {
					return this.connectToCore(coreName, ret);
				})
				.then((core) => {
					if (core && mainAction) {
						curCore = core;
						return core.targetState.halt()
							.catch((e) => {
								if (e.message !== 'Target is not running') {
									this.$log.error('Halt target failed: ' + e.message);
									throw e;
								}
							});
					}
				})
				.then(() => {
					if (curCore) {
						return mainAction(curCore);
					}
				})
				.catch((error) => {
					if (error !== 'backdrop click' && error !== 'No' &&
						!(error instanceof ti_common.CancelError)) {
						return this.ticAlerts.showError(error.message);
					}
				})
				.finally(() => {
					return this.$q((resolve) => {
						if (curCore) {
							resolve(curCore.settings.get([])
								.then((ret) => {
									this.ufPropertiesValue.updateFromDS(ret.nameValueMap, this.ufContext.getCurrentCore().name, 'lprf');
								}));
						} else {
							resolve();
						}
					})
						.then(() => {
							if (optionObj && (optionObj.connect === false)) {
								// don't need to run target if we are not connecting to the target (ie; NCOperation)
								return;
							}

							if (curCore && optionObj && optionObj.runAfterOp) {
								return curCore.targetState.run(1).catch((e) => {
									if (e) {
										this.$log.error('completeWorkflowAction curCore.targetState.run(1) ' + JSON.stringify(e));
									}
								});
							}
						})
						.then(() => {
							if (optionObj && (optionObj.connect === false || optionObj.disconnect === false)) {
								// don't need to disconnect if we did not connect (ie; NCOperation), or if we don't want to disconnect
								return;
							}
							if (this.ufPreferencesSrv.get('Auto Disconnect')) {
								return this.disconnectAllCores().catch((e) => {
									if (e) {
										this.$log.error('completeWorkflowAction this.disconnectAllCores() ' + JSON.stringify(e));
									}
								});
							}
						})
						.finally(() => {
							this.$rootScope.$emit('ds.opFinished');
						});
				});
		}
	}
}
