# Source: matchedpeaks.js

    /**
     * Class to calculate Matched Peaks and distribution of the Amoino Acids
     */
    class MatchedPeaks {
    	PREFIX = "PREFIX";
    	SUFFIX = "SUFFIX";
    	matchedUnMatchedList = [];
    	matchedList = [];
    	water = "H2O";

    	CONST_A = "a";
    	CONST_B = "b";
    	CONST_C = "c";
    	CONST_Y = "Y";
    	colors = ["red","orange","blue","grey"];
    	massShiftList = [] ;
    	constructor(){
    		this.matchedList = [];
    		this.matchedUnMatchedList = [];
    		this.million = 1000000;
    	}
    	/**
    	 * Function to form object with necessary attributes at each amino acid position
    	 * @param {Object} monoMassList_temp - Single element from mono Mass List
    	 * @param {String} id - Unique Id for each peak
    	 * @param {String} ion - Contains matched type(b/y) appended with charge
    	 * @param {Integer} position - Position of the amino acid
    	 * @param {Float} massDiff - Mass difference at particular position
    	 * @param {Float} mass - Mass at the position of the acid
    	 * @param {Char} matchedInd - Indicator indicating matched/not matched. If matched containd "Y"
    	 */
    	matchedPeakAttributes(monoMassList_temp,id,ion,position,massDiff,mass,matchedInd)
    	{
    		let peak = monoMassList_temp;
    		let thMass = "";
    		let PPMerror = "";
    		// Check if Matched Indicator is Yes "Y" else keep empty spaces in the new attributes
    		if(matchedInd == this.CONST_Y)
    		{
    			thMass = Math.round(mass * 10000)/10000 ;
    			massDiff = Math.round(massDiff * 10000)/10000;
    			PPMerror = massDiff/thMass * this.million ;
    			PPMerror = Math.round(PPMerror * 10000)/10000;
    		}
    		peak.peakId = id ;
    		peak.ion = ion;
    		peak.position = position;
    		peak.massError = massDiff;
    		peak.thMass = thMass ;
    		peak.PPMerror = PPMerror;
    		peak.matchedInd = matchedInd;
    		console.log("peak : ", peak);
    		return peak;
    	}
    	/**
    	 * Returns Complete list with matched Indication
    	 * @param {Array} prefixOrSuffixMassList - List with either prefix or suffix mass list
    	 * @param {Array} monoMassList - Contains Mono Mass list from html page
    	 * @param {String} sequence - Protein sequence
    	 * @param {Float} massErrorthVal - Threshhold value entered by user to determine the mass error and matched peaks
    	 * @param {Float} ppmErrorthVal - Threshhold value entered by user to determine the mass error and matched peaks in ppm units
    	 * @param {String} ionType - Contains either y/b
    	 * @param {String} preOrSufInd - Contains information of either PREFIX/SUFFIX
    	 */
    	getMatchedPeakList(prefixOrSuffixMassList,monoMassList,sequence,massErrorthVal,ppmErrorthVal,ionType,preOrSufInd)
    	{
    		let matchedList = [];
    		let monoMassList_temp = monoMassList.slice();
    		let len = monoMassList_temp.length;
    		let preOrSufListln = prefixOrSuffixMassList.length;
    		let seqln = sequence.length;
    		for(let i=0; i < len; i++)
    		{
    			let peakId = i+1;
    			let peak = {};
    			for(let j = 0; j<preOrSufListln; j++)
    			{
    				let massDiff = monoMassList_temp[i].mass - prefixOrSuffixMassList[j].mass ;
    				if(massErrorthVal != null || isNaN(massErrorthVal))
    				{
    					if(Math.abs(massDiff) <= massErrorthVal)
    					{
    						//increment the position as the seq starts from 0 but the data in table starts from 1
    						let ion = ionType + prefixOrSuffixMassList[j].position ;
    						let position = prefixOrSuffixMassList[j].position ;
    						if(preOrSufInd == "suffix")
    						{
    							position = seqln - position;
    						}
    						let mass = prefixOrSuffixMassList[j].mass;
    						let matchedInd = "Y";
    						peak = this.matchedPeakAttributes(monoMassList_temp[i],peakId,ion,position,
    																massDiff,mass,matchedInd);
    						matchedList.push(peak);
    					}
    				}
    				else{
    					let prefDiff = Math.round(massDiff * 10000)/10000;
    					let prefMassRounded = Math.round(prefixOrSuffixMassList[j].mass * 10000)/10000;
    					let prePPMerror = prefDiff/prefMassRounded * this.million ;
    					if(Math.abs(prePPMerror) <= ppmErrorthVal)
    					{
    						//increment the position as the seq starts from 0 but the data in table starts from 1
    						let ion = ionType + prefixOrSuffixMassList[j].position ;
    						let position = prefixOrSuffixMassList[j].position ;
    						if(preOrSufInd == "suffix")
    						{
    							position = seqln - position;
    						}
    						let mass = prefixOrSuffixMassList[j].mass;
    						let matchedInd = "Y";
    						peak = this.matchedPeakAttributes(monoMassList_temp[i],peakId,ion,position,
    																massDiff,mass,matchedInd);
    						matchedList.push(peak);
    					}
    				}
    			}
    		}
    		return matchedList;
    	}
    	/**
    	 * Set matched indicator to complete data and Seperate matched and unmatched list of data with matchedInd attribute
    	 * @param {Array} monoMassList - Contains mono Mass list data
    	 * @param {Array} matchedList - Contains all the data of both matched and unmatched data
    	 */
    	getMatchedAndUnMatchedList(monoMassList,matchedList)
    	{
    		let matchedAndUnmatchedList = matchedList.map(x => ({...x}));
    		//matchedAndUnmatchedList.concat(matchedList);
    		let len = matchedList.length;
    		let MatchedPeaksObj = new MatchedPeaks();
    		monoMassList.forEach(function(eachElement,i){
    			let matched = false;
    			let peakId = i+1;
    			for(let j=0; j < len; j++)
    			{
    				if(peakId == parseInt(matchedList[j].peakId))
    				{
    					matched = true;
    					break;
    				}
    			}
    			if(!matched)
    			{
    				let matchedInd = "N";
    				let peak = MatchedPeaksObj.matchedPeakAttributes(monoMassList[i],peakId,"","","","",matchedInd);
    			  matchedAndUnmatchedList.push(peak);
    			}
    		})
    		completeCalData.matchedandunmatcheddata = matchedAndUnmatchedList;
    		return matchedAndUnmatchedList ;
    	}
    	/**
    	 * Get the envelopes/distribution for the sequence
    	 * @param {Array} peakDataList - Contains peak list information
    	 * @param {String} sequence - Contains protein sequence
    	 * @param {Array} matchedUnMatchedList - Contains all matched and unmatched list
    	 */
    	getDistribution(peakDataList,sequence,matchedUnMatchedList)
    	{
    		let len = matchedUnMatchedList.length;
    		let totalDistribution = [] ;
    		let calEmassAndDistObj = new CalculateEmassAndDistribution();
    		let molecularFormObj = new MolecularFormulae();
    		let seqln = sequence.length;
    		for(let i = 0; i < len; i++)
    		{
    			let distributionList = {};
          /*
    			if(matchedUnMatchedList[i].matchedInd == "Y")
    			{
    				if(this.CONST_A == matchedUnMatchedList[i].ion[0] || this.CONST_B == matchedUnMatchedList[i].ion[0]
    																		|| this.CONST_C == matchedUnMatchedList[i].ion[0])
    				{
    					let matchedPos = matchedUnMatchedList[i].position ;
    					let seq = sequence.slice(0,matchedPos) ;
    					distributionList.mono_mass = matchedUnMatchedList[i].mass;
    					distributionList.charge = matchedUnMatchedList[i].charge;
    					distributionList.env_peaks = calEmassAndDistObj.emass(seq,peakDataList,matchedUnMatchedList[i].charge,this.PREFIX);
    					totalDistribution.push(distributionList);
    				}
    				else
    				{
    					let matchedPos = matchedUnMatchedList[i].position ;
    					let seq = sequence.slice(matchedPos,seqln) ;
    					distributionList.mono_mass = matchedUnMatchedList[i].mass;
    					distributionList.charge = matchedUnMatchedList[i].charge;
    					distributionList.env_peaks = calEmassAndDistObj.emass(seq,peakDataList,matchedUnMatchedList[i].charge,this.SUFFIX);
    					totalDistribution.push(distributionList);
    				}
    			}
    			else
    			{
          */
    				distributionList.mono_mass = matchedUnMatchedList[i].mass;
    				distributionList.charge = matchedUnMatchedList[i].charge;
    				distributionList.env_peaks = molecularFormObj.emass(distributionList.mono_mass,distributionList.charge,peakDataList);
    				totalDistribution.push(distributionList);
    			//}
    		}
    		if(totalDistribution.length != 0)
    		{
    			totalDistribution.sort(function(x,y){
    				return d3.ascending(x.env_peaks[0].mz, y.env_peaks[0].mz);
    			})
    		}

    		let envlength = totalDistribution.length;
    		let colorListsize = this.colors.length;
    		while(envlength--){
    			let index = envlength%colorListsize ;
    			totalDistribution[envlength].color = this.colors[index] ;
    		}
    		return totalDistribution ;
    	}
    	/**
    	 * Get All the matched positions and mass list seperated with a matched indicator
    	 * @param {Array} prefixOrSuffixMassList - List of prefix and suffix masses
    	 * @param {Array} monoMassList - Contains Mono Mass list
    	 * @param {Float} massErrorthVal - Mass Error Threshold value entered by user
    	 * @param {Float} ppmErrorthVal - PPM Error Threshold value entered by user
    	 * @param {String} prefixInd - contains if the complete data is of prefix/suffix mass list
    	 */
    	getMatchedAndUnmatchedPrefixAndSuffixMassList(prefixOrSuffixMassList, monoMassList,
    																massErrorthVal,ppmErrorthVal,prefixInd)
    	{
    		//console.log("monoMassList : ", monoMassList);
    		let MatchedAndUnMatchedList = [];
    		let monoMassList_temp = monoMassList.slice();
    		let len = monoMassList_temp.length;
    		let preOrSufListln = prefixOrSuffixMassList.length;
    		let MatchedAndUnMatchedListObj = {};

    		for(let j = 0; j<preOrSufListln; j++)
    		{
    			let position = prefixOrSuffixMassList[j].position;
    			if(prefixInd != "prefix")
    			{
    				position = preOrSufListln-position+1;
    			}
    			let mass = prefixOrSuffixMassList[j].mass;
    			let matchedInd = "N";
    			let charge = 1;// Setting Default Value
    			for(let i=0; i < len; i++)
    			{
    				let massDiff = monoMassList_temp[i].mass - prefixOrSuffixMassList[j].mass ;
    				let prefDiff = Math.round(massDiff * 10000)/10000;
    				let prefMassRounded = Math.round(prefixOrSuffixMassList[j].mass * 10000)/10000;
    				let prePPMerror = prefDiff/prefMassRounded * this.million ;
    				if((massErrorthVal != null || isNaN(massErrorthVal) )||
    								(ppmErrorthVal != null || isNaN(ppmErrorthVal) ))
    				{
    					if((Math.abs(massDiff) <= massErrorthVal) || (Math.abs(prePPMerror) <= ppmErrorthVal))
    					{
    						//increment the position as the seq starts from 0 but the data in table starts from 1
    						position = prefixOrSuffixMassList[j].position ;
    						if(prefixInd != "prefix")
    						{
    							position = preOrSufListln-prefixOrSuffixMassList[j].position +1;
    						}
    						mass = prefixOrSuffixMassList[j].mass;
    						matchedInd = "Y";
    						charge = monoMassList_temp[i].charge;
    						break;
    					}
    				}
    			}
    			MatchedAndUnMatchedListObj = {position:position,mass:mass,charge:charge,matchedInd:matchedInd};
    			MatchedAndUnMatchedList.push(MatchedAndUnMatchedListObj);
    		}
    		return MatchedAndUnMatchedList;
    	}
      }