package com.elphel.imagej.gpu;

import java.util.Arrays;

import com.elphel.imagej.common.ShowDoubleFloatArrays;

public class TpTask {
	public int        task;
	// task bits 0..7 - texture neighbors (0 - N, 1 - NE, ..., 7 - NW)
	// bit  8 (GPUTileProcessor.TASK_TEXT_EN) - enable texture generation
	// bit  9 (GPUTileProcessor.TASK_CORR_EN) - enable intrascene correlations
	// bit 10 (GPUTileProcessor.TASK_INTER_EN) - enable interscene correlations
	// Old (still not updated for CPU): [0](+1) - generate 4 images, [4..9]+16..+512 - correlation pairs, 2 - generate texture tiles
	public float      target_disparity;
    public int        num_sensors = 4;
	public int        ty;
	public int        tx;
	public float []   centerXY = {Float.NaN,Float.NaN};
	public float[][]  xy = null;
	public float[][]  xy_aux = null;
	public float [][] disp_dist = null;
//	public float      weight;
	public float      scale = 0.0f; // for motion blur correction
	// 0.0 - set (as it was). >0 multiply and set. <0 multiply and accumulate
	public static int getSize(int num_sensors) {
//		return 5 + 2* num_sensors + 4 * num_sensors;
		return 6 + 2* num_sensors + 4 * num_sensors; // added scale
	}
	public int getSize() {
//		return 5 + 2* num_sensors + 4 * num_sensors;
		return getSize(num_sensors);
	}
	
	public void setScale(float scale) {
		this.scale = scale;
	}
	
	public void setScale(double scale) {
		this.scale = (float) scale;
	}
	
	public float getScale() {
		return scale;
	}
	
	public TpTask(
			int num_sensors,
			int tileX,
			int tileY) {
		this.num_sensors = num_sensors;
		this.ty = tileY;
		this.tx = tileX;
	}

	public TpTask(int num_sensors, int tx, int ty, float target_disparity, int task ) {
		this.tx = tx;
		this.ty = ty;
		this.target_disparity = target_disparity;
		this.task = task;
		this.num_sensors = num_sensors; // will not be encoded
		this.disp_dist = new float [num_sensors][4];
	}
	/**
	 * Initialize from the float array (read from the GPU)
	 * @param num_sensors number of sensors in an array
	 * @param flt float array containing tasks data
	 * @param indx task number to use
	 * @param use_aux (always false now)
	 */
	public TpTask(int num_sensors, float [] flt, int task_indx, boolean use_aux)
	{
		this.num_sensors = num_sensors; // will not be encoded
		int indx = task_indx * getSize(num_sensors);
		task =    Float.floatToIntBits(flt[indx++]); // 0
		int txy = Float.floatToIntBits(flt[indx++]); // 1
		ty = txy >> 16;
		tx = txy & 0xffff;
		target_disparity = flt[indx++];              // 2
		centerXY[0] = flt[indx++];                   // 3
		centerXY[1] = flt[indx++];                   // 4
		scale = flt[indx++];                         // 5
		if (use_aux) {
			xy_aux = new float[num_sensors][2];
    		for (int i = 0; i < num_sensors; i++) {
    			xy_aux[i][0] = flt[indx++];
    			xy_aux[i][1] = flt[indx++];
    		}
		} else {
			xy = new float[num_sensors][2];
    		for (int i = 0; i < num_sensors; i++) {
    			xy[i][0] = flt[indx++];
    			xy[i][1] = flt[indx++];
    		}
		}
		disp_dist = new float [num_sensors][4];
		for (int i = 0; i < num_sensors; i++) {
			for (int j = 0; j < 4; j++) {
				disp_dist[i][j] = flt[indx++];
			}
		}
	}
	
	public void setTextureEnable(boolean en) {
		if (en) task |=  (1 << GPUTileProcessor.TASK_TEXT_EN);
		else 	task &= ~(1 << GPUTileProcessor.TASK_TEXT_EN);
	}
	
	public boolean getTextureEnable() {
		return (task & (1 << GPUTileProcessor.TASK_TEXT_EN)) != 0;
	}

	public void setIntraCorrelationEnable(boolean en) {
		if (en) task |=  (1 << GPUTileProcessor.TASK_CORR_EN);
		else 	task &= ~(1 << GPUTileProcessor.TASK_CORR_EN);
	}
	
	public boolean getIntraCorrelationEnable() {
		return (task & (1 << GPUTileProcessor.TASK_CORR_EN)) != 0;
	}
	
	public void setInterCorrelationEnable(boolean en) {
		if (en) task |=  (1 << GPUTileProcessor.TASK_INTER_EN);
		else 	task &= ~(1 << GPUTileProcessor.TASK_INTER_EN);
	}
	
	public boolean getInterCorrelationEnable() {
		return (task & (1 << GPUTileProcessor.TASK_INTER_EN)) != 0;
	}
	
	
	
	public float [][] getDispDist(){
		return disp_dist;
	}
	public void setDispDist(float [][] disp_dist){
		this.disp_dist = disp_dist;
	}
	public void setDispDist(double [][] disp_dist){
		this.disp_dist = new float [disp_dist.length][disp_dist[0].length];
		for (int i = 0; i < disp_dist.length; i++) {
			for (int j = 0; j < disp_dist[0].length; j++) {
				this.disp_dist[i][j] = (float) disp_dist[i][j];		
			}
		}
	}

	public double [][] getDoubleDispDist(){
		if (disp_dist == null) { // can it happen?
			return null;
		}
		double [][] ddisp_dist = new double [disp_dist.length][disp_dist[0].length];
		for (int nsens = 0; nsens < disp_dist.length; nsens++) {
			for (int i = 0; i < disp_dist[nsens].length; i++) {
				ddisp_dist[nsens][i] = disp_dist[nsens][i]; 
			}
		}
		return ddisp_dist;
	}

	public float [][] getXY(){
		return  xy;
	}
	public double [][] getDoubleXY(){
		float [][] fXY = getXY();
		if (fXY == null) {
			return null;
		}
		double [][] dXY = new double [fXY.length][fXY[0].length];
		for (int nsens = 0; nsens < fXY.length; nsens++) {
			for (int i = 0; i < fXY[nsens].length; i++) {
				dXY[nsens][i] = fXY[nsens][i]; 
			}
		}
		return dXY;
	}
	
	public int getTileY(){
		return ty;
	}
	public int getTileX(){
		return tx;
	}
	public int [] getTileXY(){
		return new int [] {tx,ty};
	}
	
	public int getTask() {
		return task;
	}
	public double getTargetDisparity() {
		return target_disparity;
	}

	public void updateTargetDisparity(float td) {
		target_disparity = td;
	}
	
	
	public float [] getCenterXY() {
		return centerXY;
	}

	public double [] getDoubleCenterXY() {
		return new double [] {centerXY[0],centerXY[1]};
	}
	
	public void setCenterXY(double [] centerXY) {
		this.centerXY = new float [] {(float) centerXY[0],(float) centerXY[1]};
	}


	// convert this class instance to float array to match layout of the C struct
	public float [] asFloatArray(boolean use_aux) {
		
		float [] flt = new float [getSize()];
		return asFloatArray(flt, 0, use_aux);
	}
	// convert this class instance to float array to match layout of the C struct,
	// fill existing float array from the specified index
	public float [] asFloatArray(float [] flt, int 	task_indx, boolean use_aux) {
		int indx = task_indx * getSize(num_sensors);
		flt[indx++] = Float.intBitsToFloat(task);            // 0
		flt[indx++] = Float.intBitsToFloat(tx + (ty << 16)); // 1
		flt[indx++] = this.target_disparity;                 // 2
		flt[indx++] = centerXY[0];                           // 3
		flt[indx++] = centerXY[1];                           // 4
		flt[indx++] = scale;                                 // 5
		float [][] offsets = use_aux? this.xy_aux: this.xy;
		for (int i = 0; i < num_sensors; i++) {
			if (offsets != null) {
				flt[indx++] = offsets[i][0];
				flt[indx++] = offsets[i][1];
			} else {
				indx+= 2;
			}
		}
		return flt;
	}
	
	public static void showTpTask(
			TpTask[] tp_tasks,
			int      tilesX,
			int      tilesY,
			String   title) {
		int numSensors = tp_tasks[0].num_sensors;
		String [] titles0 = {"X-cent", "Y-cent", "Disp","Scale"};
		String [] titles = new String[titles0.length+2*numSensors];
		for (int i = 0; i < titles0.length; i++) {
			titles[i] = titles0[i];
		}
		for (int i = 0; i < numSensors; i++) {
			titles[titles0.length + 2*i + 0] = "X-"+i;
			titles[titles0.length + 2*i + 1] = "Y-"+i;
		}
		double [][] data = new double [titles.length][tilesX*tilesY];
		for (int i = 0; i < data.length; i++) {
			Arrays.fill(data[i], Double.NaN);
		}
		for (int nTask = 0; nTask < tp_tasks.length; nTask++) {
			TpTask task = tp_tasks[nTask];
			int nTile = task.ty*tilesX+task.tx;
			data [0][nTile] = task.centerXY[0];
			data [1][nTile] = task.centerXY[1];
			data [2][nTile] = task.target_disparity;
			data [3][nTile] = task.scale;
			for (int i = 0; i < numSensors; i++) {
				data [titles0.length + 2*i + 0][nTile] = task.xy[i][0];
				data [titles0.length + 2*i + 1][nTile] = task.xy[i][1];
			}
		}
		ShowDoubleFloatArrays.showArrays(
				data,
				tilesX,
				tilesY,
				true,
				title,
				titles);
	}
	
	
	
}