package com.elphel.imagej.orthomosaic;

import java.awt.Rectangle;

import com.elphel.imagej.gpu.GPUTileProcessor;

public class FineXYCorr {
	public double []   top_left_metric; // relative to the reference image vertical point, normally negative
	public int         width_tiles;
	public int         height_tiles;
	public double      warp_pix_size;
	public double      warp_tile_size;
	public double      render_div_tile;
	public double      pix_div_render;
	public double []   render_origin = new double[2]; // vertical point of the first image in render pixels
	public double [][] warp_xy; // [width_tiles*height_tiles][2], values in pixel shift, data on tiles grid
	public Rectangle   render_woi;
	public double      render_pix_size; // in meters
	public double []   render_tl; // top left corner in render pixels for the  top-left corner of the warp array 
	public FineXYCorr (
			int zoom_lev,
			int width,
			double [] tl_metric,// relative to the reference image vertical point, normally negative
			double [][] vf) { // [tiles][2], may have nulls
		warp_pix_size =   OrthoMap.getPixelSizeMeters (zoom_lev); // meters
		warp_tile_size =  warp_pix_size * GPUTileProcessor.DTT_SIZE;
		width_tiles =     width;
		height_tiles =    vf.length/width_tiles;
		top_left_metric = tl_metric;
		warp_xy =         vf;
	}
	
	public void setRender(int zoom_lev,
			double px0, // in render pixels
			double py0) {
		render_pix_size = OrthoMap.getPixelSizeMeters (zoom_lev); // meters
		render_origin = new double [] {px0,py0};
		render_tl = new double[]{
				render_origin[0] + top_left_metric[0] / render_pix_size,
				render_origin[1] + top_left_metric[1] / render_pix_size};
		render_div_tile = render_pix_size/warp_tile_size;
		pix_div_render = warp_pix_size/render_pix_size;
	}
	
	/**
	 * Return rectangular area where this instance's warp changes
	 * rendering of the (second image). Use to optimize accesses to getCorrectedXY
	 * @return Rectangle that contains all the render pixels that may be warped
	 */
	public Rectangle getRenderWOI (){
		int x0 = (int) Math.floor(render_tl[0]);
		int y0 = (int) Math.floor(render_tl[1]);
		int x1 = (int) Math.ceil(render_tl[0] + warp_tile_size*(width_tiles-1)/render_pix_size);
		int y1 = (int) Math.ceil(render_tl[1] + warp_tile_size*((warp_xy.length/width_tiles)-1)/render_pix_size);
		return new Rectangle(x0,y0,x1-x0+1, y1-y0+1);
	}
	
	
	/**
	 * Apply warping to the x,y pair
	 * @param xy {X,Y} before and after warping
	 */
	public void warpXY(// in render pixels
			double [] xy) { // no interpolation - using nearest
		int ix = (int) Math.round((xy[0] - render_tl[0]) * render_div_tile);
		int iy = (int) Math.round((xy[1] - render_tl[1]) * render_div_tile);
		if ((ix >= 0) && (ix < width_tiles) && (iy >= 0) && (iy < height_tiles)){
			double [] dxdy = warp_xy[ix + width_tiles * iy];
			if (dxdy != null) {
				xy[0] -= dxdy[0]*pix_div_render;
				xy[1] -= dxdy[1]*pix_div_render;
			}
		}
	}
	
	public double [] getWarp(
			double x,   
			double y) {
		int ix = (int) Math.round((x - render_tl[0]) * render_div_tile);
		int iy = (int) Math.round((y- render_tl[1]) * render_div_tile);
		if ((ix >= 0) && (ix < width_tiles) && (iy >= 0) && (iy < height_tiles)){
			double [] dxdy = warp_xy[ix + width_tiles * iy];
			if (dxdy != null) {
				return new double [] {
						dxdy[0]*pix_div_render,
						dxdy[1]*pix_div_render};
			}
		}
		return new double [2];
	}
	
	
}
