package kr.util.ft;

import com.bc.utils.RingBuffer;
import kr.util.Util;

import kr.util.audio.AudioClip;
import kr.util.audio.MidiAudioClip;

/**
 * Performs spectrum analysis and creates a histogram with the given frequency range
 * and step
 */
//PERF: not very fast
public class SpectrumAnalyzer2 
{
	private int SAMPLE_SIZE = 3; //must be a power of 2
	private AudioClip ac;
	
	private byte[][] buf;

	public SpectrumAnalyzer2(double freq, int sampleFreq, AudioClip ac)
	{
		freqGenerators = new MidiAudioClip[freqs.length];
		zeros = new long[2][freqs.length];
		for(int i = 0; i < freqs.length; i++)
		{
			freqGenerators[i] = new MidiAudioClip(sampleFreq);
			freqGenerators[i].playFreq(freqs[i]);
		}
		
		this.ac = ac; 
		this.sampleFreq = sampleFreq;
		
		for(int i = 0; i < SAMPLE_SIZE+1; i++)
		{
			rb.setInternal(i, new long[2][freqs.length]);
		}
		
		//initialize the buffer to before the sample begins. 
		//negative sample points are all 0
		rb.setSize(SAMPLE_SIZE+1);
		rb.shiftEndTo(0);
	}

	public double [] getAnalysis(int index)
	{
		return normalize(getRawResults(index));
	}
		
	public double [] normalize(double [] result)
	{
		double max = 0;
		
		for(double d : result)
		{
			max = Math.max(d, max);
		}
		
		if(max != 0)
		{
			for(int i = 0; i < result.length; i++)
			{
				result[i] /= max;
			}
		}
		
		return result;
	}
	
	public double[] getRawResults(int index) {
		int start, end;
				
		int shiftAmount = rb.shiftEndTo(index + SAMPLE_SIZE/2);
		
		//
		//choose the data to fill in. This will fill in the gap that has been created by shifting
		//
		if(shiftAmount > 0)
		{
			start = Math.max(index - SAMPLE_SIZE/2 - 1, rb.getLastIndex() - shiftAmount +1);
			end = index + SAMPLE_SIZE/2+1;
		}
		else
		{
			start = Math.min(index + SAMPLE_SIZE/2, rb.getFirstIndex() - shiftAmount - 1);
			end = index - SAMPLE_SIZE/2 - 1;
		}
		
		long [][] lastV;
		
		// If the gap is not the full size of the buffer, then we need to find the item
		// immediately outside the gap, since each item in the array is the correlation 
		// plus the value of the item previous to it. (This makes it easy to total
		// up the items in the array (by just subtracting the first from the last).
		if(shiftAmount < rb.getSize())
			lastV = rb.get(start+(shiftAmount > 0 ? -1 : 1));
		else //otherwise, use zero for the last amount. This will be fine since we will
			//never use values from beyond the length of the ring buffer
			lastV = zeros;
		
		//if there is any work at all to do
		if(shiftAmount > 0 && start <= end )
		{
			for(int i = start; i != end; i+= shiftAmount > 0 ? 1 : -1)
			{
				long [][] v = rb.get(i);
				byte acVal = index < 0 ? 0 : ac.byteAt(i);
				//byte acVal = i < 0 ? 0 : freqGenerators[30].byteAt(i);
				for(int j = 0; j < v[0].length; j++)
				{
					//PERF: maybe not use byteAt
					v[0][j] = (i < 0 ? 0 : freqGenerators[j].byteAt(i) * acVal) + lastV[0][j] * (shiftAmount > 0 ? 1 : -1);
					v[1][j] = (i < 0 ? 0 : freqGenerators[j].cosByteAt(i) * acVal) + lastV[1][j] * (shiftAmount > 0 ? 1 : -1);
					
				}
				lastV = v;			
			}
		}
		
		//get the total
		long [][] v1 = rb.get(index - SAMPLE_SIZE/2 - 1);
		long [][] v2 = rb.get(index + SAMPLE_SIZE/2 - 1);
		
/*		//HACK
		for(int h = index - SAMPLE_SIZE/2-1; h <= index + SAMPLE_SIZE/2; h++)
		{
		}*/
		
		
		double [] result = new double[v1[0].length];
		double max = 0;
		
		for(int i = 0; i < v1[0].length; i++)
		{
			result[i] = Math.sqrt(Util.square(Math.abs(v2[0][i] - v1[0][i])) + Util.square(Math.abs(v2[1][i] - v1[1][i])));
		}
		
		return result;
	}

	public AudioClip getAudioClip() {
		return ac;
	}

}
