package imseProc.noiseGen;

import java.nio.ByteBuffer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import imseProc.core.ByteBufferImage;
import imseProc.core.ImagePipeController;
import imseProc.core.Img;
import imseProc.core.ImgPipe;
import imseProc.core.ImgSource;

public class NoiseGenSource extends ImgPipe implements ImgSource {
	public static final int bitDepth = 12;
	
	private ByteBufferImage liveImage;
	private NoiseRunner noiseRunner;
	private Thread noiseThread;	
	
	public int width;
	public int height;
	
	private int imageChangePeriod = 1000;
	
	public NoiseGenSource() {
		this.width = 688;
		this.height = 520;
		initMetaData();
	}
	
	public NoiseGenSource(int width, int height) {
		this.width = width;
		this.height = height;
		initMetaData();
	}
		
	public NoiseGenSource(ByteBufferImage liveImage) {
		this.liveImage = liveImage;		
		this.width = liveImage.getWidth();
		this.height = liveImage.getHeight();
		initMetaData();
	}

	private void initMetaData() {
		//Some test meta data
		
		setSeriesMetaData("int[]", new int[]{ 1, 2, 3, 4, 5, 6 });
		setSeriesMetaData("Integer[]", new Integer[]{ 6, 5, 4, 3, 2, 1 });
		
		List<Double> aList = Arrays.asList(new Double[]{ 1.1, 1.2, 1.3, 1.4, 1.5 });		
		setSeriesMetaData("List<Double>", aList );
		
		ArrayList<Object> oList = new ArrayList<Object>();
		oList.add((short) 10);
		oList.add((short) 30);
		oList.add((short) 20);
		oList.add((short) 50);
		oList.add((short) 20);
		setSeriesMetaData("List<Object> (short)", oList );
		
		
		setSeriesMetaData("List<byte[]> (t,z)", Arrays.asList(new byte[][]{ 
				{11,125},{12,115},{13,105},
				{14,95},{15,85},{16,75} } ) );
		
		
		setSeriesMetaData("Object[]{ double[] }", new Object[]{ 
				new double[]{  0.2, 5,  0 }, 
				new double[]{  0.1, 7, -1 }, 
				new double[]{  0.0, 8, -2 }, 
				new double[]{ -0.1, 8, -3 }, 
				new double[]{ -0.2, 7, -4 }, 
				new double[]{ -0.3, 5, -5 }, 
		});
			
		
	}
	
	@Override
	public int getNumImages() { return 1; }

	@Override
	public Img getImage(int imgIdx) {
		if(imgIdx != 0)
			return null;
		
		if(liveImage == null){
			liveImage = new ByteBufferImage(this, 0, width, height, bitDepth);
			double max = liveImage.getMaxPossibleValue();
			for(int y=0; y < height; y++){
				for(int x=0; x < width; x++){
					double c = (((y % 50) / 100.0) * max) + (int)(x * max / width / 2.0);
					if((x%50)==0)
						c=0;
					liveImage.setPixelValue(x, y, c);
				}
			}
			liveImage.setPixelValue(width/2, height/2, 0);
			liveImage.setPixelValue(width/2+1, height/2+1, max);
			
			liveImage.imageChanged(true);
		}
		
		return liveImage;
	}
	
	@Override
	public Img[] getImageSet() {
		return new Img[]{ getImage(0) };
	}
	
	public void startNoise() {
		if(noiseRunner == null)
			noiseRunner = new NoiseRunner();
		if(noiseThread == null)
			noiseThread = new Thread(noiseRunner);
		synchronized (noiseThread) {
			if(!noiseThread.isAlive())
				noiseThread.start();
		}

	}
	
	public void stopNoise(boolean waitForDeath) {
		if(noiseThread != null){
			synchronized (noiseThread) {				
				if(noiseThread.isAlive()){			
					noiseThread.interrupt();
					noiseRunner.death = true;
					if(waitForDeath){
						System.out.print("NoiseGenSource: Waiting for noise thread to die... ");			
						while(noiseThread.isAlive()){
							try {
								Thread.sleep(10);
							} catch (InterruptedException e) { }	
						}
						System.out.println("Done.");
					}
				}
			}
		}
		noiseThread = null;
		noiseRunner = null;
	
	}
	
	private class NoiseRunner implements Runnable{
		boolean death = false;
		
		@Override
		public void run() {
			Random randGen = new Random();
			int rX = width / 10, rY=height / 10;
			double maxVal = liveImage.getMaxPossibleValue();
			updateAllControllers();
			
			while(!death){
				int x0 = randGen.nextInt(width - rX);
				int y0 = randGen.nextInt(height - rY);
				
				for(int y=0; y < rY; y++){
					for(int x=0; x < rX; x++){
						if((x0+x) < width && (y0+y) < height)
						liveImage.setPixelValue(x0+x, y0+y, randGen.nextDouble() * maxVal);
					}
				}
				
				liveImage.imageChanged(true);
				
				try {
					Thread.sleep(imageChangePeriod);
				} catch (InterruptedException e) { }
			}
			
			updateAllControllers();
		}
	}
	
	@Override
	public ImagePipeController createPipeController(Class interfacingClass, Object args[], boolean asSink) {
		ImagePipeController controller = null;
		if(interfacingClass == Composite.class){
			controller = new NoiseGenSWTControl((Composite)args[0], (Integer)args[1], this);
			controllers.add(controller);
		}
		return controller;
	}

	public boolean isNoiseActive() {
		return noiseRunner != null && noiseThread != null && noiseThread.isAlive() && !noiseRunner.death; 
	}

	@Override
	public ImgSource clone() {
		return new NoiseGenSource();
	}
	
	public int getImageChangePeriod() { return imageChangePeriod; }
	public void setImageChangePeriod(int imageChangePeriod) { this.imageChangePeriod = imageChangePeriod; }

	@Override
	public boolean isIdle() { return !isNoiseActive(); }
}
