/**
 * Class to calculate Emass and Distribution for the given input
 */
class CalculateEmassAndDistribution{

	constructor(){
		this.PREFIX = "PREFIX";
		this.SUFFIX = "SUFFIX";
		this.minintensity = 0.000001;
		this.protonMass = 1.00727647  ;
		this.intensityTolerance = 1 ;
		this.toleraceMassDiff = 0.02;
	}
	/**
	 * Function to calculate the emass distrubution fo the given sequence
	 * @param {String} seq - Contains the sequence provide by the user
	 * @param {Array} peakDataList - Contains the peak list provided by the user
	 * @param {Float} charge - Contains the chrage of the ion
	 * @param {String} pref_suffInd - Indicator to indiace prefix or suffix
	 */
	emass(seq,peakDataList,charge,pref_suffInd)
	{
		let AcidArray = seq ;
		let  AcidArrayLen= AcidArray.length;
		let totDistributionList = [] ;
		
		for(let i = 0; i < AcidArrayLen ; i++)
		{
			let aminoAcidDist = getAminoAcidDistribution(AcidArray[i]);
			totDistributionList = this.getMassAndIntensity(totDistributionList,aminoAcidDist) ;
		}
		if(pref_suffInd == this.SUFFIX)
		{
			let waterDist = getAminoAcidDistribution("H2O");
			totDistributionList = this.getMassAndIntensity(totDistributionList,waterDist);
		}
		totDistributionList = this.getMZwithHighInte(totDistributionList,charge,peakDataList);
		totDistributionList = this.getNormalizedIntensity(totDistributionList,peakDataList);
		totDistributionList.sort(function(x, y){
		   return d3.ascending(x.mz, y.mz);
		})
		return totDistributionList ;
	}
	/**
	 * Logic to calculate distribution 
	 * @param {Array} totDistributionList - Array with current total distribution
	 * @param {Array} aminoAcidDist - Array with existing calculated distribution of amino acid 
	 */
	getMassAndIntensity(totDistributionList,aminoAcidDist)
	{
		let maxintensity = 0 ;
		if(totDistributionList.length == 0)
		{
			return aminoAcidDist ;
		}
		else
		{
			let len = totDistributionList.length + aminoAcidDist.length - 1;
	  		let completeDistributionList = new Array(len).fill(0) ;
			for(let i=0;i<totDistributionList.length;i++)
			{
				for(let j=0;j<aminoAcidDist.length;j++)
				{
					let intensity = 0;
					let index = i+j ;
					let mass = totDistributionList[i].mass+aminoAcidDist[j].mass ;
					intensity = totDistributionList[i].intensity * aminoAcidDist[j].intensity ;
					if(completeDistributionList[index] != 0) intensity = intensity + completeDistributionList[index].intensity ;
					completeDistributionList[index] = {mass:mass,intensity:intensity};
					if(intensity > maxintensity) maxintensity = intensity ;
				}
			}
			let completeDistributionList_temp = [];
			for(let i = 0; i<completeDistributionList.length; i++)
			{
				let tempintensityVal = (completeDistributionList[i].intensity/maxintensity)*100;
				//Taking float values till 6 decimals
				if( tempintensityVal > this.minintensity)
				{
					completeDistributionList[i].intensity = Math.round(tempintensityVal * 1000000) / 1000000;//tempintensityVal//Math.round(tempintensityVal * 1000000) / 1000000; //parseFloat(((tempDistributionList[i].intensity/maxintensity)*100).toFixed(6)) ;
					completeDistributionList_temp.push(completeDistributionList[i]);
				}
			}
			completeDistributionList = completeDistributionList_temp ;
			return completeDistributionList ;
		}
	}
	/**
	 * Code to remove the calculate MZ(mass/charge) value and remove low intensities
	 * @param {Array} totDistributionList - Total distribution calculated
	 * @param {Integer} charge - charge for the mass list by user
	 * @param {Array} peakDataList - peaklist entered by the user
	 */
	getMZwithHighInte(totDistributionList,charge,peakDataList)
	{
		let len = totDistributionList.length;
		let overaAllMaxIntensity = 0 ;
		let onePerctInte = 0;
		let peakListLen = peakDataList.length;
		for(let k=0;k<peakListLen ; k++)
		{
			if(peakDataList[k].intensity > overaAllMaxIntensity) overaAllMaxIntensity = peakDataList[k].intensity ;
		}
		onePerctInte = overaAllMaxIntensity/100 ;
		let newtotDistributionList = [];
		for(let i=0;i<len;i++)
		{
			let intensity = totDistributionList[i].intensity ;
			intensity = overaAllMaxIntensity * intensity/100 ;
			if(intensity > onePerctInte)
			{
				let mz = totDistributionList[i].mass/charge + this.protonMass;
				let intensity = totDistributionList[i].intensity ;
				let tempdistObj = {mz:mz,intensity:intensity};
				newtotDistributionList.push(tempdistObj);
			}
		}
		return newtotDistributionList ;
	}
	/**
	 * Code to normalize the Intensity. 
	 * Take the average of intensity from the peaks entered by the user.
	 * Take the average of the calculated distribution for each Array element in the Array. 
	 * Make both of them equal and calculating the rest of the 
	 * distribution intensity based on the avg value from the peak list.
	 * @param {Array} totDistributionList - Total distribution calculated
	 * @param {Array} peakDataList - Peak Data entered by the user
	 */
	getNormalizedIntensity(totDistributionList,peakDataList)
	{
		let len = totDistributionList.length;
		let peakListLen = peakDataList.length;
		let intensity = 0;
		let count = 0 ;
		let distributionInte = 0;
		for(let i=0;i<len;i++)
		{
			for(let j=0;j<peakListLen;j++)
			{
				if(Math.abs(totDistributionList[i].mz - peakDataList[j].mz) <= this.toleraceMassDiff )
				{
					intensity = intensity + peakDataList[j].intensity ;
					distributionInte = distributionInte + totDistributionList[i].intensity;
					count = count + 1;
				}
			}
		}
		let avg = intensity/count ;
		let distributionAvgInte = distributionInte/count;
		for(let i=0;i<len;i++)
		{
			totDistributionList[i].intensity = (avg * totDistributionList[i].intensity)/distributionAvgInte ;
		}
		return totDistributionList ;
	}
}