/**
 **
 ** FineXYCorr.java - Fine sub-pixel XY correction parameters for orthomosaic alignment
 **
 ** Copyright (C) 2024 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  FineXYCorr.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/>.
 ** -----------------------------------------------------------------------------**
 **
 */
package com.elphel.imagej.orthomosaic;

import java.awt.Rectangle;

import com.elphel.imagej.gpu.GPUTileProcessor;

public class FineXYCorr {
	public double      scale_warp = 1.0;
	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 NOT_USED
			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*scale_warp,
						dxdy[1]*pix_div_render*scale_warp};
			}
		}
		return new double [2];
	}
	
	
}
