class Transcription {
	constructor(values) {
		this.idProject = values.idProject;
		this.idCall = values.idCall;
		this.path = values.path;
		this.fileName = values.fileName;
		this.audioFile = values.audioFile;
		this.azureCognitiveLenguajeKEY = values.azureCognitiveLenguajeKEY;
		this.azureCognitiveLenguajeRegion = values.azureCognitiveLenguajeRegion;
		this.recognizer;
		this.getAudioData = {};
		this.totalTextArray = [];
		this.reTranscription = values.reTranscription;
		this.manualStop = 0;
	}

	sendAudioTranscription() {
		let self = this;

		let JSONuser = sessionStorage['STCMBackEnd:user'];
		let user = JSON.parse(JSONuser);
		let location = user.location;
		$('#languageOptions').val(location);

		$('#transcriptionSend').prop('disabled', true);
		$('#transcriptionStart').show();
		$('#transcriptionStop').hide();
		$('#transcriptionModal').modal({backdrop: 'static', keyboard: false});
		$('#transcriptionModal').modal('toggle');
		$('#transcriptionText').html('');
		$('#transcriptionTextHidden').text('');

		// Iniciar transcripción
		$('#transcriptionStart').off().on('click', function() {
			//transcriptionModalClose
			$('#transcriptionModalClose').attr('title', pf.const.language.RSC2098);
			$('#transcriptionModalClose').prop('disabled', true);
			$('#languageOptions').prop('disabled', true);
			self.manualStop = 0;
			$('#transcriptionEnd').prop('checked', false);
			$('#transcriptionText').html('');
			$('#transcriptionTextHidden').text('');
			$('#transcriptionStart').hide();
			$('#transcriptionStop').prop('disabled', false);
			$('#transcriptionStop').show();

			// Elemento transcriptor
			let SpeechSDK = window.SpeechSDK;
			let speechConfig = SpeechSDK.SpeechConfig.fromSubscription(self.azureCognitiveLenguajeKEY, self.azureCognitiveLenguajeRegion);

			// Codificación de lenguaje de la transcripción a través del parámetro 'location' del usuario administrador
			//speechConfig.speechRecognitionLanguage = 'en-US';
			speechConfig.speechRecognitionLanguage = $('#languageOptions').val();

			self.getAudioDataMethod();

			let url = self.path + '/' + self.fileName;
			fetch(url).then(function(response) {
				return response.blob();
			}).catch(function(error) {
				console.log(error.message);
			}).then(function(blob) {
				let reader = new FileReader();
				reader.readAsArrayBuffer(blob);
				reader.addEventListener('loadend', function() {
					let buffer1 = reader.result;

					let tmp = new Uint8Array(buffer1.byteLength);
					tmp.set(new Uint8Array(buffer1), 0);

					//Get buffer1 audio data to create the new combined wav
					let audioData = self.getAudioData.WavHeader.readHeader(new DataView(buffer1));

					//Send combined buffer and send audio data to create the audio data of combined
					let arrBytesFinal = self.getWavBytes(tmp, {
						isFloat: false, // floating point or 16-bit integer
						numChannels: audioData.channels,
						sampleRate: audioData.sampleRate,
					})

					//Create a Blob as Base64 Raw data with audio/wav type
					/* let myBlob = new Blob([arrBytesFinal], {
						type: 'audio/wav; codecs=MS_PCM'
					}); */

					let file = new File([arrBytesFinal], self.fileName, {
						type: 'audio/wav; codecs=MS_PCM'
					});

					let container = new DataTransfer();
					container.items.add(file);

					let filePicker = {};
					filePicker['files'] = container.files;

					let audioFile = filePicker.files[0];

					// Audio a transcribir
					let audioConfig = SpeechSDK.AudioConfig.fromWavFileInput(audioFile);

					// Transcripción
					self.recognizer = new SpeechSDK.SpeechRecognizer(speechConfig, audioConfig);

					// Create the SpeechRecognizer and set up common event handlers and PhraseList data
					self.applyCommonConfigurationTo();

					// Start the continuous recognition. Note that, in this continuous scenario, activity is purely event-
					// driven, as use of continuation (as is in the single-shot sample) isn't applicable when there's not a
					// single result.
					self.recognizer.startContinuousRecognitionAsync();
				});
			});
		});

		// Parar transcripción
		$('#transcriptionStop').off().on('click', function() {
			$('#transcriptionStart').show();
			$('#transcriptionStop').hide();
			self.manualStop = 1;

			self.recognizer.stopContinuousRecognitionAsync(
				function() {
					self.recognizer.close();
					self.recognizer = undefined;
					$('#transcriptionStop').prop('disabled', true);
					$('#transcriptionModalClose').attr('title', '');
					$('#transcriptionModalClose').prop('disabled', false);

					// Texto total de la transcripción para array
					let totalTextHidden = $('#transcriptionTextHidden').text();
					self.totalTextArray = totalTextHidden.split('#');
					self.totalTextArray.pop();
				},
				function(err) {
					self.recognizer.close();
					self.recognizer = undefined;
					$('#transcriptionStop').prop('disabled', true);
					$('#transcriptionModalClose').attr('title', '');
					$('#transcriptionModalClose').prop('disabled', false);

					// Texto total de la transcripción para array
					let totalTextHidden = $('#transcriptionTextHidden').text();
					self.totalTextArray = totalTextHidden.split('#');
					self.totalTextArray.pop();
				}
			);
		});

		// Enviar transcripción
		$('#transcriptionEnd').off().change(function() {
			// Texto total de la transcripción para array
			let totalTextHidden = $('#transcriptionTextHidden').text();
			self.totalTextArray = totalTextHidden.split('#');
			self.totalTextArray.pop();

			if($('#transcriptionEnd').prop('checked')) {
				if(self.manualStop === 0) {
					self.saveTranscriptionInfo();
				} else {
					let objInfo = {
						accept: {
							text: pf.const.language.RSC78
						},
						cancel: {
							text: pf.const.language.RSC57
						}
					};

					let alertTitle = pf.const.language.RSC2001;
					let alertText = pf.const.language.RSC2032;
					pf.utils.showInfoDialogAcceptCancel(alertTitle, alertText, objInfo, function() {
						self.saveTranscriptionInfo();
					}, function() {});
				}
			}
		});
	}

	getAudioDataMethod() {
		let self = this;

		function WavHeader() {
			this.dataOffset = 0;
			this.dataLen = 0;
			this.channels = 0;
			this.sampleRate = 0;
		}

		function fourccToInt(fourcc) {
			return fourcc.charCodeAt(0) << 24 | fourcc.charCodeAt(1) << 16 | fourcc.charCodeAt(2) << 8 | fourcc.charCodeAt(3);
		}

		WavHeader.RIFF = fourccToInt('RIFF');
		WavHeader.WAVE = fourccToInt('WAVE');
		WavHeader.fmt_ = fourccToInt('fmt ');
		WavHeader.data = fourccToInt('data');

		WavHeader.readHeader = function(dataView) {
			let w = new WavHeader();

			let header = dataView.getUint32(0, false);
			if(WavHeader.RIFF != header) {
				return;
			}

			let fileLen = dataView.getUint32(4, true);

			if(WavHeader.WAVE != dataView.getUint32(8, false)) {
				return;
			}

			if(WavHeader.fmt_ != dataView.getUint32(12, false)) {
				return;
			}

			let fmtLen = dataView.getUint32(16, true);

			let pos = 16 + 4;

			switch(fmtLen) {
				case 16:
				case 18:
					w.channels = dataView.getUint16(pos + 2, true);
					w.sampleRate = dataView.getUint32(pos + 4, true);
					break;
				default:
					throw 'extended fmt chunk not implemented';
			}

			pos += fmtLen;

			let data = WavHeader.data;
			let len = 0;
			while(data != header) {
				header = dataView.getUint32(pos, false);
				len = dataView.getUint32(pos + 4, true);
				if(data == header) {
					break;
				}
				pos += (len + 8);
			}
			w.dataLen = len;
			w.dataOffset = pos + 8;
			return w;
		};

		self.getAudioData.WavHeader = WavHeader;
	}

	getWavHeader(options) {
		const numFrames = options.numFrames;
		const numChannels = options.numChannels || 2;
		const sampleRate = options.sampleRate || 44100;
		const bytesPerSample = options.isFloat ? 4 : 2;
		const format = options.isFloat ? 3 : 1;
		const blockAlign = numChannels * bytesPerSample;
		const byteRate = sampleRate * blockAlign;
		const dataSize = numFrames * blockAlign;
		const buffer = new ArrayBuffer(44);
		const dv = new DataView(buffer);

		let p = 0;

		function writeString(s) {
			for(let i = 0; i < s.length; i++) {
				dv.setUint8(p + i, s.charCodeAt(i));
			}
			p += s.length;
		}

		function writeUint32(d) {
			dv.setUint32(p, d, true);
			p += 4;
		}

		function writeUint16(d) {
			dv.setUint16(p, d, true);
			p += 2;
		}

		writeString('RIFF'); // ChunkID
		writeUint32(dataSize + 36); // ChunkSize
		writeString('WAVE'); // Format
		writeString('fmt '); // Subchunk1ID
		writeUint32(16); // Subchunk1Size
		writeUint16(format); // AudioFormat
		writeUint16(numChannels); // NumChannels
		writeUint32(sampleRate); // SampleRate
		writeUint32(byteRate); // ByteRate
		writeUint16(blockAlign); // BlockAlign
		writeUint16(bytesPerSample * 8); // BitsPerSample
		writeString('data'); // Subchunk2ID
		writeUint32(dataSize); // Subchunk2Size

		return new Uint8Array(buffer);
	}

	getWavBytes(buffer, options) {
		let self = this;

		const type = options.isFloat ? Float32Array : Uint16Array;
		const numFrames = buffer.byteLength / type.BYTES_PER_ELEMENT;
		const headerBytes = self.getWavHeader(Object.assign({}, options, {numFrames}));
		const wavBytes = new Uint8Array(headerBytes.length + buffer.byteLength);

		// prepend header, then add pcmBytes
		wavBytes.set(headerBytes, 0);
		wavBytes.set(new Uint8Array(buffer), headerBytes.length);

		return wavBytes;
	}

	applyCommonConfigurationTo() {
		let self = this;

		// The 'recognizing' event signals that an intermediate recognition result is received.
		// Intermediate results arrive while audio is being processed and represent the current "best guess" about
		// what's been spoken so far.
		self.recognizer.recognizing = self.onRecognizing;

		// The 'recognized' event signals that a finalized recognition result has been received. These results are
		// formed across complete utterance audio (with either silence or eof at the end) and will include
		// punctuation, capitalization, and potentially other extra details.
		// 
		// * In the case of continuous scenarios, these final results will be generated after each segment of audio
		//	with sufficient silence at the end.
		// * In the case of intent scenarios, only these final results will contain intent JSON data.
		// * Single-shot scenarios can also use a continuation on recognizeOnceAsync calls to handle this without
		//	event registration.
		self.recognizer.recognized = self.onRecognized;

		// The 'canceled' event signals that the service has stopped processing speech.
		// https://docs.microsoft.com/javascript/api/microsoft-cognitiveservices-speech-sdk/speechrecognitioncanceledeventargs?view=azure-node-latest
		// This can happen for two broad classes of reasons:
		// 1. An error was encountered.
		//	In this case, the .errorDetails property will contain a textual representation of the error.
		// 2. No additional audio is available.
		//	This is caused by the input stream being closed or reaching the end of an audio file.
		self.recognizer.canceled = self.onCanceled;

		// The 'sessionStarted' event signals that audio has begun flowing and an interaction with the service has
		// started.
		/* recognizer.sessionStarted = onSessionStarted; */

		// The 'sessionStopped' event signals that the current interaction with the speech service has ended and
		// audio has stopped flowing.
		self.recognizer.sessionStopped = self.onSessionStopped;

		// PhraseListGrammar allows for the customization of recognizer vocabulary.
		// The semicolon-delimited list of words or phrases will be treated as additional, more likely components
		// of recognition results when applied to the recognizer.
		//
		// See https://docs.microsoft.com/azure/cognitive-services/speech-service/get-started-speech-to-text#improve-recognition-accuracy
		/* if(phrases.value) {
			var phraseListGrammar = SpeechSDK.PhraseListGrammar.fromRecognizer(recognizer);
			phraseListGrammar.addPhrases(phrases.value.split(";"));
		} */
	}

	onCanceled(sender, recognitionEventArgs) {
		console.log(recognitionEventArgs.privErrorDetails);

		$('#transcriptionModalClose').prop('disabled', false);
		$('#transcriptionModalClose').trigger('click');

		let errorTitle = pf.const.language.RSC2001;
		let errorContent = pf.const.language.RSC2102;
		pf.utils.showInfoDialog(errorTitle, errorContent);

		self.recognizer.close();
		self.recognizer = undefined;
	}

	onRecognizing(sender, recognitionEventArgs) {
		let result = recognitionEventArgs.result;

		// Transcripción parcial en contenedor temporal
		let text = $('#transcriptionTextTemp').text();
		$('#transcriptionTextTemp').text(text.replace(/(.*)(^|[\r\n]+).*\[\.\.\.\][\r\n]+/, '$1$2') + `${result.privText} [...]\r\n`);
		document.getElementById('transcriptionModalBody').scrollTop = document.getElementById('transcriptionModalBody').scrollHeight;
	}

	onRecognized(sender, recognitionEventArgs) {
		let result = recognitionEventArgs.result;

		// Limpieza de contenedor temporal
		$('#transcriptionTextTemp').text('');

		// Añadir texto al contenedor principal
		if(result.privText) {
			let text = $('#transcriptionText').html();
			text += result.privText;
			text += '<br><br>';
			$('#transcriptionText').html(text);

			// Añadir texto al contenedor oculto
			let textHidden = $('#transcriptionTextHidden').html();
			textHidden += result.privText;
			textHidden += '#';
			$('#transcriptionTextHidden').html(textHidden);
		}
	}

	onSessionStopped(sender, sessionEventArgs) {
		// Activar el botón para cerrar el modal y el de enviar la transcripción
		$('#transcriptionModalClose').attr('title', '');
		$('#transcriptionModalClose').prop('disabled', false);
		$('#transcriptionSend').prop('disabled', false);

		// Desactivar el botón para parar la transcripción
		$('#transcriptionStop').prop('disabled', true);

		$('#transcriptionEnd').trigger('click');
		$('#languageOptions').prop('disabled', false);

		self.recognizer.close();
		self.recognizer = undefined;
	}

	saveTranscriptionInfo() {
		let self = this;

		let parameters = {
			idProject: self.idProject,
			idCall: self.idCall,
			textTranscription: JSON.stringify(self.totalTextArray),
			reTranscription: self.reTranscription
		};
		ajaxComunCallWithCallback('saveTranscriptionInfo', parameters, function(ajaxReturn) {
			if(ajaxReturn) {
				$('#transcriptionModal').modal('hide');
				let successTitle = pf.const.language.RSC10;
				let successContent = pf.const.language.RSC2028;
				pf.utils.showInfoDialog(successTitle, successContent);

				ProyectosEditController.getFilesRecordedByProject();
			}
		});
	}
}