# Source: invokespectrum.js

    /**
     * @function addSpectrum
     * @description
     * Starting point of drawing spectrum graph
     * @param {String} id - gets the svg id of the graph
     * @param {Array} peakList - contains the list of data with mz and intensity used to draw lines on the graph
     * @param {Array} envelopeList - contains the list of data with actual mass, mz and intensity used to draw circles on graph
     * @param {float} monoMZ - Value to which tha graph as to point on click of mz value used to zoom the grpah to that location
     * @param {Array} ionData - contains the list of data with mass and acid name
     * @param {object} graphFeatures - contains all the features needed for drawing the graphs
     */
    addSpectrum = function(id,peakList,envelopeList,monoMZ, ionData, graphFeatures){
    	let specParameters = compSpectrumParameters(peakList, envelopeList, monoMZ);
    	specParameters.graphFeatures = graphFeatures;
    	let peakData = {};
    	peakData.peak_list = peakList ;
    	if(envelopeList != null)
    	{
    		peakData.envelope_list = sortEnvelopes(envelopeList) ;
    	}
    	id = "#"+id;
    	if(ionData == null)
    	{
    		specParameters.graphFeatures.showIons = false ;
    	}
    	else{
    		ionData = groupBy(ionData,'ion');
    	}
    	specParameters.padding = graphFeatures.padding;
    	specParameters.specWidth = graphFeatures.specWidth;
    	specParameters.specHeight = graphFeatures.specHeight;
    	specParameters.svgHeight = graphFeatures.svgHeight;
    	specParameters.padding.head = graphFeatures.padding.head;
    	let spectrumGraph = new SpectrumGraph(id,specParameters,peakData,ionData);
    	return spectrumGraph;
    }
     /**
      * Function to set spectrum perameters based on the data
      * @param {Array} peakList - contains the list of data with mz and intensity used to draw lines on the graph
      * @param {Array} envelopeList - contains the list of data with actual mass, mz and intensity used to draw circles on graph
      * @param {float} monoMZ - Value to which tha graph as to point on click of mz value used to zoom the grpah to that location
      */
     function compSpectrumParameters(peakList, envelopeList, monoMZ){
    	 console.log("peakList : ",peakList);
    	let ratio = 1;
    	let specParameters = new SpectrumParameters();
    	// Sort by mz
    	peakList.sort(function(x,y){
    		return x.mz - y.mz;
    	});
    	let listSize = peakList.length;
    	let minMzData = peakList[0].mz;
    	let maxMzData = peakList[listSize-1].mz;
    	// Sort by intensity
    	peakList.sort(function(x,y){
    		return x.intensity - y.intensity;
    	});

    	let maxEnvelope = -1;
    	let minEnvelope = 1000000000; // A random onstant high value
    	if(envelopeList != null)
    	{
    		ratio = 1.000684;
    		envelopeList.forEach(function(element){
    			element.env_peaks.forEach(function(e){
    				if (e.intensity > maxEnvelope){
    					maxEnvelope = e.intensity;
    				}
    				else if (e.intensity < minEnvelope){
    					minEnvelope = e.intensity;
    				}
    			})
    		})
    	}
    	let maxIntensity = peakList[listSize-1].intensity;
    	let minIntensity = peakList[0].intensity;
    	let currminMz = minMzData ;
    	let currmaxMz = maxMzData ;
    	let currentMaxIntensity;
    	if (maxIntensity > maxEnvelope) {
    		currentMaxIntensity = maxIntensity;
    	}
    	else {
    		currentMaxIntensity = maxEnvelope;
    	}

    	if(monoMZ != null)
    	{
    		//  Initializing with 1% of total intensity. If there exists no peaks in the
    		//  current range the intensity can't be 0 or nothing which will produce an
    		//  error in arithmatic calculations
    		currentMaxIntensity = 0.01 * currentMaxIntensity ;
    		monoMZ = monoMZ * ratio;
    		currminMz = monoMZ - specParameters.onClickMassAdjacentRange ;
    		currmaxMz = monoMZ + specParameters.onClickMassAdjacentRange ;
    		for(let i = 0; i<listSize ; i++)
    		{
    			if(peakList[i].mz > currminMz && peakList[i].mz < currmaxMz)
    			{
    				if(peakList[i].intensity > currentMaxIntensity)
    				{
    					currentMaxIntensity = peakList[i].intensity;
    				}
    			}
    		}
    	}

    	//  for specParameters, going to pass whichever value between peak max and envelope max that is bigger
    	if (maxIntensity < maxEnvelope){
    		maxIntensity = maxEnvelope;
    	}
    	if (minIntensity > minEnvelope){
    		minIntensity = minEnvelope;
    	}
    	specParameters.initScale(currminMz,currmaxMz,maxIntensity,minIntensity,minMzData,maxMzData,currentMaxIntensity);

    	// Code must be included to get top 200 intensities at any given time
    	peakList.sort(function(x,y){
    		return d3.descending(x.intensity, y.intensity);
    	});
    	return specParameters;
     }

    /**
     * Sorting envelopes based on intensity to show top 200 envelops with high intensitites
     * @param {Array} envelopeList - contains the list of data with actual mass, mz and intensity used to draw circles on graph
     */
    function sortEnvelopes(envelopeList)
    {
    	envelopeList.forEach(function(envelope){
    		// ...env_peak converts arguments into list as sort function is not allowed on arguments
    		envelope.env_peaks.forEach(function(...env_peak){
    			env_peak.sort(function(x,y){
    				return d3.descending(x.intensity, y.intensity);
    			})
    		})
    	})
    	envelopeList.sort(function(x,y){
    	 	return d3.descending(x.env_peaks[0].intensity, y.env_peaks[0].intensity);
    	})
    	return envelopeList ;
    }

    /**
     * Function returns a map list with key and value
     * @param {list} listData - contains list of data
     * @param {string} keyValue - contains keyword based on which new group is created
     */
    function groupBy(listData,keyValue){
    	const map = new Map();
        listData.forEach((element)=>{
            const key = element[keyValue];
            const collection = map.get(key);
            if(!collection) map.set(key,[element]);
            else collection.push(element);
        });
        return map;
    }