package imseProc.core;

import jafama.FastMath;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.LinkedList;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;


/** Image storing complex values (the magnitude is returned by the default interface) 
 */ 
public class ComplexImage extends ByteBufferImage {
	
	public static ComplexImage[] checkBulkAllocation(ImgSource source, ComplexImage imgs[], int width, int height, int nImgs){
		if(imgs != null){ 
			if(imgs.length == nImgs){
			
				boolean allMatched = true;			
				for(int i=0; i < nImgs; i++){
					if(imgs[i] == null || imgs[i].destroyed || !(imgs[i] instanceof ComplexImage) 
							|| imgs[i].getWidth() != width || imgs[i].getHeight() != height 
							|| imgs[i].bitDepth != -64 || imgs[i].imageBuffer.order() != ByteOrder.LITTLE_ENDIAN){
						allMatched = false;
						break;
					}
				}
				if(allMatched){
					return imgs;
				}	
			}
			
			//match failed, need to reallocate
			for(int i=0; i < imgs.length; i++){
				if(imgs[i] != null && !imgs[i].isDestroyed())
					imgs[i].destroy();
			}
		}
		
		System.gc(); //good time for this, since the last destory() should have freed lots of stuff
		return allocateBulk(source, width, height, nImgs, false);		
	}
		
	public static ComplexImage[] allocateBulk(ImgSource source, int width, int height, int nImgs, boolean init){
		//use ByteBuffer's implementation for double, with twice the width.
		ByteBufferImage bbImgs[] = ByteBufferImage.allocateBulk(source, width*2, height, -64, nImgs, init, ByteOrder.LITTLE_ENDIAN);
		ComplexImage imgs[] = new ComplexImage[nImgs];
		
		for(int i=0; i < nImgs; i++){
			imgs[i] = new ComplexImage(bbImgs[i]); //effectively cast up to ComplexImage
			bbImgs[i].destroy(); //release it's hold on the buffer allocation
		}
		
		return imgs;
	}
	
	protected ComplexImage(ImgSource source, int sourceIndex, int width, int height, ByteBuffer imageBuffer, ByteBuffer allocBuffer, LinkedList<ByteBufferImage> allImagesInAlloc, boolean init) {
		super(source, sourceIndex, width, height, -64, imageBuffer, allocBuffer, allImagesInAlloc, init);
	}
	
	/** Convert ByteBufferImage to ComplexImage */
	private ComplexImage(ByteBufferImage img) {
		super(img);
		this.width = super.width / 2;
	}
	
	public ComplexImage(ImgSource source, int sourceIndex, int width, int height, boolean init){
		super(source, sourceIndex, width*2, height, -64);
		this.width = width;
	}
		
	@Override
	public boolean isMemoryCompatible(Img img) {
			return img != null && (img instanceof ComplexImage) 
					&& img.getWidth() == width && img.getHeight() == height;
	}
	
	@Override
	public int getWidth() {
		return width;
	}
	
	@Override
	public double getMaxPossibleValue() {
		return Double.POSITIVE_INFINITY;
	}

	@Override
	public final double getPixelValue(int x, int y) {
		if(destroyed) return Double.NaN;
		double r = imageBuffer.getDouble((y*width+x)*16);
		double i = imageBuffer.getDouble((y*width+x)*16+8);
		return FastMath.sqrt(r*r + i*i);		
	}
	
	public final double getReal(int x, int y) { return destroyed ? Double.NaN : imageBuffer.getDouble((y*width+x)*16); }
	public final double getImag(int x, int y) { return destroyed ? Double.NaN : imageBuffer.getDouble((y*width+x)*16+8); }

	
	public void setPixelValue(int x, int y, double real, double imag) {
		if(destroyed) return;
		imageBuffer.putDouble((y*width+x)*16, real);
		imageBuffer.putDouble((y*width+x)*16+8, imag);
	}
	
	public void setPixelValue(int x, int y, double val) {
		throw new NotImplementedException(); //because it doesn't make sense
	}
	
	public void fillFromJTransformsPackedData(double[] data, boolean freqSpace) {
		fillFromJTransformsPackedData(data, freqSpace, width, height);
	}

	/**
	 * @param data
	 * @param freqSpace If true, the (0,0) frequency is put at the image centre, otherwise the image has (0,0) at (0,0)
	 * @param fftW, fftH 	Width and height of the FFT data from JTransforms 
	 * @throws InterruptedException
	 */
	public void fillFromJTransformsPackedData(double[] data, boolean freqSpace, int fftW, int fftH) {
		if(destroyed) return;
		
		//JTansforms packet FFT complex data looks like this:
		//pr00,pi00,pr12,pi12...
		//and then rows go [+ve cols, -ve cols, +ve cols ....]
		//and then the block of rows pairs are [ +++++, ------] in y
		
		
		//sorry for the mess...
		if(freqSpace){
			for(int y=0; y < (Math.min(fftH, height)/2); y++){
				for(int x=0; x <(Math.min(fftW, width)/2); x++){
					//+ve x, +ve y
					int i = (y+(height/2))*width+(width/2)+x;
					imageBuffer.putDouble(i*16+0, data[2*y*fftW + x*2]);
					imageBuffer.putDouble(i*16+8, data[2*y*fftW + x*2+1]);
					
					//+ve x, -ve y
					i = (height/2-y-1)*width+(width/2)+x;
					imageBuffer.putDouble(i*16+0, data[2*((fftH/2-y-1)+(fftH/2))*fftW + x*2]);
					imageBuffer.putDouble(i*16+8, data[2*((fftH/2-y-1)+(fftH/2))*fftW + x*2+1]);
					
					//-ve x, +ve y
					i = (y+(height/2))*width+(width/2-x-1);
					imageBuffer.putDouble(i*16+0, data[(2*y+1)*fftW + (fftW/2-x-1)*2]);
					imageBuffer.putDouble(i*16+8, data[(2*y+1)*fftW + (fftW/2-x-1)*2+1]);
					//-ve x, -ve y
					i = (height/2-y-1)*width+(width/2-x-1);
					imageBuffer.putDouble(i*16+0, data[(2*((fftH/2-y-1)+(fftH/2))+1)*fftW + (fftW/2-x-1)*2]);
					imageBuffer.putDouble(i*16+8, data[(2*((fftH/2-y-1)+(fftH/2))+1)*fftW + (fftW/2-x-1)*2+1]);
				}	
			}
		}else{
			for(int y=0; y < Math.min(fftH, height); y++){
				for(int x=0; x <  Math.min(fftW, width); x++){
					//-ve x, -ve y
					imageBuffer.putDouble((y*width+x)*16+0, data[y*fftW*2 + x*2]); //real
					imageBuffer.putDouble((y*width+x)*16+8, data[y*fftW*2 + x*2+1]); //imag
				}
			}
		}
	}

}
