package mil.nga.geopackage.extension.index;

import java.sql.SQLException;
import java.util.concurrent.Callable;

import mil.nga.geopackage.BoundingBox;
import mil.nga.geopackage.GeoPackage;
import mil.nga.geopackage.GeoPackageException;
import mil.nga.geopackage.features.user.FeatureDao;
import mil.nga.geopackage.features.user.FeatureResultSet;
import mil.nga.geopackage.features.user.FeatureRow;
import mil.nga.geopackage.projection.Projection;
import mil.nga.geopackage.projection.ProjectionTransform;

import com.j256.ormlite.dao.CloseableIterator;
import com.j256.ormlite.misc.TransactionManager;
import com.j256.ormlite.support.ConnectionSource;

/**
 * Feature Table Index NGA Extension implementation. This extension is used to
 * index Geometries within a feature table by their minimum bounding box for
 * bounding box queries. This extension is required to provide an index
 * implementation when a SQLite version is used before SpatialLite support
 * (Android).
 * 
 * @author osbornb
 * @since 1.1.0
 */
public class FeatureTableIndex extends FeatureTableCoreIndex {

	/**
	 * Feature DAO
	 */
	private final FeatureDao featureDao;

	/**
	 * Constructor
	 * 
	 * @param geoPackage
	 * @param featureDao
	 */
	public FeatureTableIndex(GeoPackage geoPackage, FeatureDao featureDao) {
		super(geoPackage, featureDao.getTableName(), featureDao
				.getGeometryColumnName());
		this.featureDao = featureDao;
	}

	/**
	 * Index the feature row. This method assumes that indexing has been
	 * completed and maintained as the last indexed time is updated.
	 *
	 * @param row
	 */
	public void index(FeatureRow row) {
		TableIndex tableIndex = getTableIndex();
		if (tableIndex == null) {
			throw new GeoPackageException(
					"GeoPackage table is not indexed. GeoPackage: "
							+ getGeoPackage().getName() + ", Table: "
							+ getTableName());
		}
		index(tableIndex, row.getId(), row.getGeometry());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected int indexTable(final TableIndex tableIndex) {

		int count = 0;

		try {
			// Iterate through each row and index as a single transaction
			ConnectionSource connectionSource = getGeoPackage().getDatabase()
					.getConnectionSource();
			count = TransactionManager.callInTransaction(connectionSource,
					new Callable<Integer>() {
						public Integer call() throws Exception {
							int count = 0;
							FeatureResultSet resultSet = featureDao
									.queryForAll();
							try {
								while ((progress == null || progress.isActive())
										&& resultSet.moveToNext()) {
									FeatureRow row = resultSet.getRow();
									boolean indexed = index(tableIndex,
											row.getId(), row.getGeometry());
									if (indexed) {
										count++;
									}
									if (progress != null) {
										progress.addProgress(1);
									}
								}
							} finally {
								resultSet.close();
							}

							// Update the last indexed time
							if (progress == null || progress.isActive()) {
								updateLastIndexed();
							}
							return count;
						}
					});
		} catch (SQLException e) {
			throw new GeoPackageException("Failed to Index Table. GeoPackage: "
					+ getGeoPackage().getName() + ", Table: " + getTableName(),
					e);
		}

		return count;
	}

	/**
	 * Query for Geometry Index objects within the bounding box, projected
	 * correctly
	 * 
	 * @param boundingBox
	 * @param projection
	 *            projection of the provided bounding box
	 * @return geometry indices iterator
	 */
	public CloseableIterator<GeometryIndex> query(BoundingBox boundingBox,
			Projection projection) {

		BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox,
				projection);

		CloseableIterator<GeometryIndex> geometryIndices = query(featureBoundingBox);

		return geometryIndices;
	}

	/**
	 * Query for Geometry Index count within the bounding box, projected
	 * correctly
	 * 
	 * @param boundingBox
	 * @param projection
	 *            projection of the provided bounding box
	 * @return count
	 */
	public long count(BoundingBox boundingBox, Projection projection) {

		BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox,
				projection);

		long count = count(featureBoundingBox);

		return count;
	}

	/**
	 * Get the bounding box in the feature projection from the bounding box in
	 * the provided projection
	 * 
	 * @param boundingBox
	 * @param projection
	 * @return feature projected bounding box
	 */
	private BoundingBox getFeatureBoundingBox(BoundingBox boundingBox,
			Projection projection) {
		ProjectionTransform projectionTransform = projection
				.getTransformation(featureDao.getProjection());
		BoundingBox featureBoundingBox = projectionTransform
				.transform(boundingBox);
		return featureBoundingBox;
	}

	/**
	 * Get the feature row for the Geometry Index
	 * 
	 * @param geometryIndex
	 * @return feature row
	 */
	public FeatureRow getFeatureRow(GeometryIndex geometryIndex) {
		return featureDao.queryForIdRow(geometryIndex.getGeomId());
	}

}

@@ -1411,24 +1411,151 @@
 ureDao;%0A%09%7D%0A%0A
+%09/**%0A%09 * Close the table index%0A%09 */%0A%09public void close() %7B%0A%09%09// Don't close anything, leave the GeoPackage connection open%0A%09%7D%0A%0A
 %09/**%0A%09 * Ind
@@ -3365,32 +3365,226 @@
 turn count;%0A%09%7D%0A%0A
+%09/**%0A%09 * Delete the index for the feature row%0A%09 *%0A%09 * @param row%0A%09 * @return deleted rows, should be 0 or 1%0A%09 */%0A%09public int deleteIndex(FeatureRow row) %7B%0A%09%09return deleteIndex(row.getId());%0A%09%7D%0A%0A
 %09/**%0A%09 * Query f

Delete index method