package imseProc.dshGen;

import jafama.FastMath;

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.DoubleFlatArrayImage;
import imseProc.core.IMSEProc;
import imseProc.core.ImagePipeController;
import imseProc.core.Img;
import imseProc.core.ImgPipe;
import imseProc.core.ImgSource;

public class DSHGenSource extends ImgPipe implements ImgSource {
	private DoubleFlatArrayImage images[];
	
	public int width;
	public int height;
	public int nImages;
		
	public static final int T0_TL = 0;
	public static final int T0_TR = 1;
	public static final int T0_BL = 2;
	public static final int T0_BR = 3;
	public static final int T1_TL = 4;
	public static final int T1_TR = 5;
	public static final int T1_BL = 6;
	public static final int T1_BR = 7;
	
	public double amp[], f1[], f2[], polAng[], ellip[];	
	private boolean idle = true;
		
	public DSHGenSource() {
		this.width = -1;
		this.amp = null;
		this.images = null;
	}
	
	private void calc(){
		idle = false;
		IMSEProc.ensureFinalUpdate(this, new Runnable() { @Override public void run() { doCalc(); } });
	}
	
	private void doCalc() {
		images = new DoubleFlatArrayImage[nImages];
				
		for(int i=0; i < nImages; i++){
			double imageData[] = new double[width*height];
			
			double uT = i / (nImages-1.0);
			for(int iY=0; iY < height; iY++){
				double uY = iY / (height-1.0);
				for(int iX=0; iX < width; iX++){
					double uX = iX / (width-1.0);
					
					double amp = interp(this.amp, uT, uX, uY);
					double wt1 = 2*Math.PI*interp(this.f1, uT, uX, uY);
					double wt2 = 2*Math.PI*interp(this.f2, uT, uX, uY);
					double polAng = interp(this.polAng, uT, uX, uY);
					double ellip = interp(this.ellip, uT, uX, uY);
							
					//imageData[iY*width+iX] = amp * FastMath.sin(wt1) * FastMath.sin(wt2);
					
					double s0 = (1.0-ellip) * FastMath.cos(polAng);
					double s1 = (1.0-ellip) * FastMath.sin(polAng);
					double s2 = ellip;
					
					double phaseOffset1 = 0*Math.PI/180, phaseOffset2 = 0*Math.PI/180;
					
					//double env = FastMath.exp(-0.5*omegaVar*tau2*tau2);
					//double osc0 = - FastMath.sin(wt1 + phaseOffset1) * FastMath.sin(wt2 + phaseOffset2);
					{	
						double osc0 = - FastMath.cos((wt1 + phaseOffset1) - (wt2 + phaseOffset2) ) / 2 
									  +   FastMath.cos((wt1 + phaseOffset1) + (wt2 + phaseOffset2) ) / 2;
						
						// cos(wt1 - wt2)/2 - cos(wt1 + wt2)/2
						double osc1 = - 1.0*FastMath.cos(wt2 + phaseOffset2);
						double osc2 = + FastMath.cos(wt1 + phaseOffset1) * FastMath.sin(wt2 + phaseOffset2);
	
						imageData[iY*width+iX] = 1 * 0.5 * (1 + amp * (s0*osc0 + s1*osc1 + s2*osc2));
					
					}{
						wt1 *= 1.05;
						wt2 *= 1.05;
						
						double osc0 = - FastMath.cos((wt1 + phaseOffset1) - (wt2 + phaseOffset2) ) / 2 
									  +   FastMath.cos((wt1 + phaseOffset1) + (wt2 + phaseOffset2) ) / 2;
						
						// cos(wt1 - wt2)/2 - cos(wt1 + wt2)/2
						double osc1 = - 1.0*FastMath.cos(wt2 + phaseOffset2);
						double osc2 = + FastMath.cos(wt1 + phaseOffset1) * FastMath.sin(wt2 + phaseOffset2);
	
						imageData[iY*width+iX] += 1 * 0.5 * (1 + amp * (s0*osc0 + s1*osc1 + s2*osc2));
					}
					
				}
			}
			
			images[i] = new DoubleFlatArrayImage(this, i, width, height, imageData);
		}
		
		notifyImageSetChanged();
		idle = true;
	}
	
	private final static double interp(double param[], double uT, double uX, double uY){
		return	(1.0-uT)*(1.0-uY)*(1.0-uX)*param[T0_TL] +
				(1.0-uT)*(1.0-uY)*(    uX)*param[T0_TR] +
				(1.0-uT)*(    uY)*(1.0-uX)*param[T0_BL] +
				(1.0-uT)*(    uY)*(    uX)*param[T0_BR] +
				(    uT)*(1.0-uY)*(1.0-uX)*param[T1_TL] +
				(    uT)*(1.0-uY)*(    uX)*param[T1_TR] +
				(    uT)*(    uY)*(1.0-uX)*param[T1_BL] +
				(    uT)*(    uY)*(    uX)*param[T1_BR];
	}
		
	@Override
	public int getNumImages() { return (images != null) ? images.length : 0; }

	@Override
	public Img getImage(int imgIdx) {
		return (images != null && imgIdx >= 0 && imgIdx < images.length)
					? images[imgIdx] : null;
	}
	
	@Override
	public Img[] getImageSet() { return images; }
			
	@Override
	public ImagePipeController createPipeController(Class interfacingClass, Object args[], boolean asSink) {
		ImagePipeController controller = null;
		if(interfacingClass == Composite.class){
			controller = new DSHGenSWTControl((Composite)args[0], (Integer)args[1], this);
			controllers.add(controller);
		}
		return controller;
	}

	@Override
	public ImgSource clone() {
		return new DSHGenSource();
	}
	
	public void setAll(int width, int height, int nImages, double amp[], double f1[], double f2[], double polAng[], double ellip[]){
		this.width = width;
		this.height = height;
		this.nImages = nImages;
		this.amp = amp;
		this.f1 = f1;
		this.f2 = f2;
		this.polAng = polAng;
		this.ellip = ellip;
		calc();
	}

	public boolean isIdle(){ return idle; }
}
