package com.esri.hadoop.hive;

import com.esri.core.geometry.Envelope;

public class BinUtils {
	final long numCols;
	final double extentMin;
	final double extentMax;
	final double binSize;
	
	public BinUtils(double binSize) {
		this.binSize = binSize;
		
		// absolute max number of rows/columns we can have
		long maxBinsPerAxis = (long) Math.sqrt(Long.MAX_VALUE);
		
		// a smaller binSize gives us a smaller extent width and height that
		// can be addressed by a single 64 bit long
		double size = (binSize < 1) ? maxBinsPerAxis * binSize : maxBinsPerAxis;
		
		extentMax = size/2;
		extentMin = extentMax - size;
		numCols = (long)(Math.ceil(size / binSize));
	}
	
	/**
	 * Gets bin ID from a point.
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public long getId(double x, double y) {
		double down = (extentMax - y) / binSize;
		double over = (x - extentMin) / binSize;
		
		return ((long)down * numCols) + (long)over;
	}
	
	/**
	 * Gets the envelope for the bin ID.
	 * 
	 * @param binId
	 * @param envelope
	 */
	public void queryEnvelope(long binId, Envelope envelope) {
		long down = binId / numCols;
		long over = binId % numCols;
		
		double xmin = extentMin + (over * binSize);
		double xmax = xmin + binSize;
		double ymax = extentMax - (down * binSize);
		double ymin = ymax - binSize;
		
		envelope.setCoords(xmin, ymin, xmax, ymax);
	}
	
	/**
	 * Gets the envelope for the bin that contains the x,y coords.
	 * 
	 * @param x
	 * @param y
	 * @param envelope
	 */
	public void queryEnvelope(double x, double y, Envelope envelope) {
		double down = (extentMax - y) / binSize;
		double over = (x - extentMin) / binSize;
		
		double xmin = extentMin + (over * binSize);
		double xmax = xmin + binSize;
		double ymax = extentMax - (down * binSize);
		double ymin = ymax - binSize;
		
		envelope.setCoords(xmin, ymin, xmax, ymax);
	}
}