/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0; you may not use this file except in compliance with the Elastic License
 * 2.0.
 */

package org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid;

import org.elasticsearch.common.geo.GeoBoundingBox;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.geometry.utils.Geohash;

/**
 * Bounded geotile aggregation. It accepts hashes that intersects the provided bounds.
 */
public class BoundedGeoHashGridTiler extends AbstractGeoHashGridTiler {
    private final GeoBoundingBox bbox;
    private final boolean crossesDateline;
    private final long maxHashes;

    public BoundedGeoHashGridTiler(int precision, GeoBoundingBox bbox) {
        super(precision);
        this.bbox = bbox;
        this.crossesDateline = bbox.right() < bbox.left();
        final long hashesY = (long)((bbox.top() - bbox.bottom()) / Geohash.latHeightInDegrees(precision)) + 1;
        final long hashesX;
        if (crossesDateline) {
            hashesX = (long)((360 - bbox.left() + bbox.right()) / Geohash.lonWidthInDegrees(precision)) + 1;
        } else {
            hashesX = (long)((bbox.right() - bbox.left()) / Geohash.lonWidthInDegrees(precision)) + 1;
        }
        this.maxHashes = hashesX * hashesY;
    }

    @Override
    protected long getMaxCells() {
        return maxHashes;
    }

    @Override
    protected boolean validHash(String hash) {
        final Rectangle rectangle = Geohash.toBoundingBox(hash);
        // touching hashes are excluded
        if (bbox.top() > rectangle.getMinY() && bbox.bottom() < rectangle.getMaxY()) {
            if (crossesDateline) {
                return bbox.left() < rectangle.getMaxX() || bbox.right() > rectangle.getMinX();
            } else {
                return bbox.left() < rectangle.getMaxX() && bbox.right() > rectangle.getMinX();
            }
        }
        return false;
    }
}

@@ -970,24 +970,35 @@
 esY = (long)
+ Math.ceil(
 ((bbox.top()
@@ -1051,32 +1051,33 @@
 (precision)) + 1
+)
 ;%0A        final 
@@ -1125,39 +1125,53 @@
 ) %7B%0A            
+final long 
 hashes
-X
+Left
  = (long)((360 -
@@ -1167,30 +1167,142 @@
 ong)
-((360 - bbox.left() + 
+ Math.ceil(((180 - bbox.left()) / Geohash.lonWidthInDegrees(precision)) + 1);%0A            final long hashesRight = (long) Math.ceil(((
 bbox
@@ -1309,16 +1309,22 @@
 .right()
+ + 180
 ) / Geoh
@@ -1352,32 +1352,81 @@
 (precision)) + 1
+);%0A            hashesX = hashesLeft + hashesRight
 ;%0A        %7D else
@@ -1456,16 +1456,27 @@
 = (long)
+ Math.ceil(
 ((bbox.r
@@ -1540,16 +1540,17 @@
 on)) + 1
+)
 ;%0A      

Fix computation of max cells for bounded geohash grid aggregation (#74542)