import java.awt.BorderLayout;
import java.io.IOException;
import java.util.Arrays;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import net.beadsproject.beads.core.AudioContext;
import net.beadsproject.beads.core.Bead;
import net.beadsproject.beads.core.UGen;
import net.beadsproject.beads.data.Sample;
import net.beadsproject.beads.ugens.Clock;

import org.LiveGraph.LiveGraph;
import org.LiveGraph.dataFile.write.DataStreamWriter;
import org.LiveGraph.dataFile.write.DataStreamWriterFactory;

public class BufferedSampleTestWithSlider {

	static class BufferedSamplePlayer extends UGen implements ChangeListener
	{
		Sample bs;
		int frame = 0;
		JSlider slider;
		
		// read one frame at a time from sample? or a whole chunk?
		static final boolean PERFRAME = true; 
		
		public BufferedSamplePlayer(AudioContext ac, Sample bs)
		{
			super(ac,bs.getNumChannels());
			this.bs = bs;	
			slider = null;
		}
		
		@Override
		public void calculateBuffer()
		{
			long nFrames = bs.getNumFrames();
			if (nFrames==AudioSystem.NOT_SPECIFIED)
			{
				System.out.println("dude it's an error");
				return;				
			}
			
			if (frame < nFrames)
			{
				if (PERFRAME)
				{
					int from = frame;
					int i = 0;
					int to = (int) Math.min(nFrames,frame+bufferSize);
					float[] data = new float[outs];
					for(;from<to;++from,++i)
					{
						bs.getFrame(from, data);
						for(int ch=0;ch<outs;ch++)
						{
							bufOut[ch][i] = data[ch];
						}
					}
				}
				else
				{				
					bs.getFrames(frame, bufOut);
				}
			}
			
			if ((nFrames - frame) < this.bufferSize)
			{
				for(int ch = 0;ch<this.outs;ch++)
					Arrays.fill(bufOut[ch], (int)(nFrames - frame), this.bufferSize, 0);					
				pause(true);
			}
			else
			{			
				frame += bufOut[0].length;
				slider.setValue(frame);
			}			
		}

		public void stateChanged(ChangeEvent e) {
			JSlider source = (JSlider)e.getSource();
		    //if (!source.getValueIsAdjusting()) {
		        int frame = (int)source.getValue();
		        this.frame = frame;
		        pause(false);
		    //}
		}
		
		public void setSlider(JSlider s)
		{
			this.slider = s;
		}
	}
	
	static Runtime rt;
	// returns the number of bytes used in the heap
	public static long heapSize()
	{
		return rt.totalMemory()-rt.freeMemory();
	}
	
	public static void printHeapSize(String s)
	{
		System.out.println("Memory used (" + s + ") = " + heapSize() + " bytes");
	}
	
	static DataStreamWriter dataout;
	static Sample bs;
	
	/**
	 * @param args
	 * @throws UnsupportedAudioFileException 
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException, UnsupportedAudioFileException {
		
		System.out.println("*****************************************");
		System.out.println("This test can  be used to profile the memory usage and speed of the buffered sample. e.g., compare different regimes.");
		System.out.println("It uses livegraph for live plotting.");
		System.out.println("When it boots, load the latest data file from output/bufferedsampletest, and set the update time to fast.");
		System.out.println("*****************************************");
		
		final String DEMO_DIR = "output/bufferedsampletest";
		
		// Setup a data writer object:
		dataout = DataStreamWriterFactory.createDataWriter(DEMO_DIR,
		                                "BufferedSampleTest");
		// Set a values separator:
		dataout.setSeparator(";");
		 
		  // Add a file description line:
		dataout.writeFileInfo("Buffered Sample Test.");
		 
		  // Set-up the data series:
		dataout.addDataSeries("Time");
		dataout.addDataSeries("Heap Size");
		dataout.addDataSeries("Regions Loaded");
		
		rt = Runtime.getRuntime();
		
		AudioContext ac = new AudioContext();
		printHeapSize("After new AC");

		// 
		String[] audiofiles = {"1234.aif","tada.wav","01 Leek.aif","gammaBrosTheme.mp3"};
		String file = audiofiles[3];
		
		bs = new Sample("audio/"+file, new Sample.TimedRegime(100,2000,2000,5000,Sample.TimedRegime.Order.ORDERED));
		
		printHeapSize("After Buffered Sample Loaded");
				
		BufferedSamplePlayer bsp = new BufferedSamplePlayer(ac,bs);
		ac.out.addInput(bsp);
		
		//BufferedSamplePlayer bsp2 = new BufferedSamplePlayer(ac,bs);
		//ac.out.addInput(bsp2);
				
		// gui
		
		//Create and set up the window.
        JFrame frame = new JFrame("BufferedSampleTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        assert bs.getNumFrames()>0;
        JSlider slider = new JSlider(0,(int) bs.getNumFrames());
        slider.addChangeListener(bsp);
        bsp.setSlider(slider);
        frame.add(slider, BorderLayout.CENTER);
        
        /*
        JSlider slider2 = new JSlider(0,(int) bs.nFrames);
        slider2.addChangeListener(bsp2);
        bsp2.setSlider(slider2);
        frame.add(slider2, BorderLayout.SOUTH);
         */
        
        //Display the window.
        frame.pack();
        frame.setVisible(true);

        // start
        
        printHeapSize("Just before ac.start()");
        Clock cl = new Clock(ac,10.0f);
        
        cl.addMessageListener(new Bead(){
        	protected void messageReceived(Bead b)
        	{
        		if (((Clock)b).isBeat())
        		{
        			//printHeapSize("tick");
        			dataout.setDataValue(((Clock)b).getBeatCount()*10.f);
        			dataout.setDataValue(heapSize());
        			dataout.setDataValue(bs.getNumberOfRegionsLoaded());
        			dataout.writeDataSet();
        		}
        	}
        });
        
        ac.out.addDependent(cl);
        
        LiveGraph app = LiveGraph.application();
        app.execStandalone();
        
		ac.start();		
		//dataout.close();
	}

}
