import java.util.Arrays;
import java.util.Random;

import ij.ImagePlus;
import ij.WindowManager;
import ij.gui.GenericDialog;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;

/*
 **
 ** WavePatternGenerator.java
 **
 ** Copyright (C) 2014 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **  
 **  WavePatternGenerator.java is free software: you can redistribute it and/or modify
 **  it under the terms of the GNU General Public License as published by
 **  the Free Software Foundation, either version 3 of the License, or
 **  (at your option) any later version.
 **
 **  This program is distributed in the hope that it will be useful,
 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 **  GNU General Public License for more details.
 **
 **  You should have received a copy of the GNU General Public License
 **  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ** -----------------------------------------------------------------------------**
 **
 */

public class WavePatternGenerator {
	String DEFAULT_DIRECTORY=null;
//	/    		GenericDialog gd=new GenericDialog("Select images "+startIndex+"..."+(endIndex-1));
	public boolean selectAndGenerate(){
   		GenericDialog gd=new GenericDialog("Select wave pattern parameters");
   		boolean generateCirc=false;
   		boolean generateFFT=false;
   		int width=4096;
   		int height=4096;
   		double period=10.0;
   		double x0=width/2;
   		double y0=height/2;
   		boolean binary=false;
   		
		int fft_size= 4096;
		boolean useSelectedImage=false;
		double fxc=0.15; // center frequency X (0..0.5)
		double fyc=0.05; // center frequency Y (0..0.5)
		double sigma_long=0.001; // coeff for x^2, relative to full size 
		double sigma_lat= 0.001; // 
   		
   		
   		gd.addCheckbox("Halftone",!binary);
   		gd.addMessage("=== Circular waves generation ===");
   		gd.addCheckbox("Generate circular wave pattern",generateCirc);
		gd.addNumericField("Image width", width, 0, 4, "pixels");
		gd.addNumericField("Image height", height, 0, 4, "pixels");
		gd.addNumericField("Wave period", period, 2, 6, "pixels");
		gd.addNumericField("Wave center X", x0, 2, 10, "pixels");
		gd.addNumericField("Wave center Y", y0, 2, 10, "pixels");
   		gd.addMessage("=== FFT filter ===");
   		gd.addCheckbox("Generate FFT-fileterd wave pattern",generateFFT);
   		gd.addCheckbox("Use selected image (otherwise use random data)",useSelectedImage);
		gd.addNumericField("FFT size", fft_size, 0, 4, "pixels");
		gd.addNumericField("Filter center X (frequency domain)", fxc, 5, 7, "fraction");
		gd.addNumericField("Filter center Y (frequency domain)", fyc, 5, 7, "fraction");
		gd.addNumericField("Filter half-width longitudinal", sigma_long, 5, 7, "fraction");
		gd.addNumericField("Filter half-width lateral", sigma_lat, 5, 7, "fraction");
//		WindowTools.addScrollBars(gd);
		gd.showDialog();
		if (gd.wasCanceled()) return false;
		binary=        !gd.getNextBoolean();
		generateCirc=   gd.getNextBoolean();
		width=    (int) gd.getNextNumber();
		height=   (int) gd.getNextNumber();
		period=         gd.getNextNumber();
		x0=             gd.getNextNumber();
		y0=             gd.getNextNumber();
		generateFFT=    gd.getNextBoolean();
		fft_size= (int) gd.getNextNumber();
		fxc=            gd.getNextNumber();
		fyc=            gd.getNextNumber();
		sigma_long=       gd.getNextNumber();
		sigma_lat=       gd.getNextNumber();
		
		if (generateCirc) {
			circularPatternGenerator(
					width,
					height,
					period,
					x0,
					y0,
					binary);
		}
		if (generateFFT) {
		FHTFilter (
				fft_size,
				useSelectedImage?WindowManager.getCurrentImage():null, // input image or null (will use random)
				fxc, // center frequency X (0..0.5)
				fyc, // center frequency Y (0..0.5)
				sigma_long, // coeff for x^2, relative to full size 
				sigma_lat, // 
				binary);
		}
		return true;
	}
	public ImagePlus FHTFilter (
			int size,
			ImagePlus imp_in, // input image or null (will use random)
			double fxc, // center frequency X (0..0.5)
			double fyc, // center frequency Y (0..0.5)
			double sigma_long, // coeff for x^2, relative to full size 
			double sigma_lat, // 
			boolean binary
			){
// Random random=new Random();
		// double rnd=random.nextDouble();
		double kSigma=5.0;
		double vLen=Math.sqrt(fxc*fxc+fyc*fyc);
		double [] vLong={fxc/vLen, fyc/vLen};
		double [] vLat= {-fyc/vLen, fxc/vLen};
		double [] data=new double[size*size];
		if (imp_in!=null){
			int iWidth=imp_in.getWidth();
			int iHeight=imp_in.getHeight();
			int dy=iHeight/2-size/2;
			int dx=iWidth/2-size/2;
			float [] fpixels= (float[]) imp_in.getProcessor().getPixels();
			for (int y=0;y<size;y++){
				int y_in=y+dy;
				if ((y_in<0) || (y_in>=iHeight)) y_in=-1;
				for (int x=0;x<size;x++){
					if (y_in<0) data[y*size+x]=0.0;
					else {
						int x_in=x+dx;
						if ((x_in<0) || (x_in>=iWidth)) data[y*size+x]=0.0;
						else {
							
							data[y*size+x]=fpixels[y_in*iWidth+x_in];
						}
					}
				}
			}
		} else {
			Random random=new Random();
			for (int i=0;i<data.length;i++) data[i]=random.nextDouble();
		}
		// generate frequency mask
		double [] mask=new double[size*size];
		Arrays.fill(mask,0.0);
		int ifxc=(int) Math.round(fxc*size);
		int ifyc=(int) Math.round(fyc*size);
		int iRangeX= (int) Math.round((sigma_long*Math.abs(vLong[0]) + sigma_lat*Math.abs(vLat[0]))* size*kSigma);
		int iRangeY= (int) Math.round((sigma_long*Math.abs(vLong[1]) + sigma_lat*Math.abs(vLat[1]))* size*kSigma);
		double kLong= -0.5/(sigma_long*sigma_long*size*size);
		double kLat=  -0.5/(sigma_lat* sigma_lat* size*size);
		for (int y=ifyc-iRangeY;y<ifyc+iRangeY;y++){
			int iy=(y+size)%size;
			int base=size*iy;
			for (int x=ifxc-iRangeX;x<ifxc+iRangeX;x++){
				int ix=(x+size)%size;
				double cLong=(x-ifxc)*vLong[0]+(y-ifyc)*vLong[1];
				double cLat= (x-ifxc)*vLat[0]+ (y-ifyc)*vLat[1];
				mask[base+ix]=Math.exp(kLong*cLong*cLong+kLat*cLat*cLat);
			}
		}
		(new showDoubleFloatArrays()).showArrays(data, "input data");
		(new showDoubleFloatArrays()).showArrays(mask, "mask");
		DoubleFHT fht =new DoubleFHT();
		fht.swapQuadrants(data);
		fht.transform(    data);
		(new showDoubleFloatArrays()).showArrays(data, "FHT data");
		for (int i=0;i<data.length;i++) data[i]*=mask[i];
		(new showDoubleFloatArrays()).showArrays(data, "masked FHT data");
		fht.inverseTransform(data);
		fht.swapQuadrants   (data);
//		(new showDoubleFloatArrays()).showArrays(data, "restored data");
		float [] pixels = new float [data.length];
		if (binary) for (int i=0;i<data.length;i++) pixels[i] = (data[i]>0)?1.0f:0.0f;
		else        for (int i=0;i<data.length;i++) pixels[i] = (float) data[i];
		ImageProcessor ip_wave = new FloatProcessor(size,size);
		ip_wave.setPixels(pixels);
		ip_wave.resetMinAndMax();
		ImagePlus imp_circle= new ImagePlus("wave_fxc"+fxc+"_fyc"+fyc+"_sigma_long"+sigma_long+"_sigma_lat"+sigma_lat,ip_wave);
		imp_circle.show();
		return imp_circle;
	}
	
	public ImagePlus circularPatternGenerator(
			int width,
			int height,
			double period,
			double centerX,
			double centerY,
			boolean binary
			){
		ImageProcessor ip_cirWave = new FloatProcessor(width,height);
		float [] pixels = new float [width*height];
		double l=period/Math.PI;
		for (int y=0;y<height;y++){
			int base=y*width;
			for (int x=0;x<width;x++){
		
			double dx=x-centerX;
			double dy=y-centerY;
			
			double r=Math.sqrt(dx*dx+dy*dy);
			float d=(float) Math.sin(r/l);
			pixels[base+x]=binary?((d>0)?1.0f:0.0f):d;
			}
		}
		ip_cirWave.setPixels(pixels);
		ip_cirWave.resetMinAndMax();
		ImagePlus imp_circle= new ImagePlus("circularWaves_period"+period+"_xc"+centerX+"_yc"+centerY,ip_cirWave);
		imp_circle.show();
		return imp_circle;
	}

}
