package mil.nga.geopackage.test.tiles.features;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.sql.SQLException;
import java.util.Date;

import mil.nga.geopackage.BoundingBox;
import mil.nga.geopackage.GeoPackage;
import mil.nga.geopackage.core.contents.Contents;
import mil.nga.geopackage.core.contents.ContentsDao;
import mil.nga.geopackage.features.columns.GeometryColumns;
import mil.nga.geopackage.features.user.FeatureDao;
import mil.nga.geopackage.features.user.FeatureRow;
import mil.nga.geopackage.geom.GeoPackageGeometryData;
import mil.nga.geopackage.schema.TableColumnKey;
import mil.nga.geopackage.tiles.features.DefaultFeatureTiles;
import mil.nga.geopackage.tiles.features.FeatureTilePointIcon;
import mil.nga.geopackage.tiles.features.FeatureTiles;
import mil.nga.sf.GeometryType;
import mil.nga.sf.LineString;
import mil.nga.sf.Point;
import mil.nga.sf.Polygon;
import mil.nga.sf.proj.ProjectionConstants;

/**
 * Feature Tile Utils
 */
public class FeatureTileUtils {

	public static final String TABLE_NAME = "feature_tiles";

	/**
	 * Create feature dao
	 *
	 * @return feature dao
	 */
	public static FeatureDao createFeatureDao(GeoPackage geoPackage) {

		BoundingBox boundingBox = new BoundingBox();

		GeometryColumns geometryColumns = new GeometryColumns();
		geometryColumns.setId(new TableColumnKey(TABLE_NAME, "geom"));
		geometryColumns.setGeometryType(GeometryType.GEOMETRY);
		geometryColumns.setZ((byte) 0);
		geometryColumns.setM((byte) 0);

		geoPackage.createFeatureTableWithMetadata(geometryColumns, boundingBox,
				ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM);

		FeatureDao featureDao = geoPackage.getFeatureDao(geometryColumns);

		return featureDao;
	}

	/**
	 * Insert features
	 *
	 * @param featureDao
	 * @return number of features
	 */
	public static int insertFeatures(GeoPackage geoPackage,
			FeatureDao featureDao) throws SQLException {

		int count = 0;

		count += 5;
		insertPoint(featureDao, 0, 0);
		insertPoint(featureDao, 0,
				ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE - 1);
		insertPoint(featureDao, 0,
				ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE + 1);
		insertPoint(featureDao, -179, 0);
		insertPoint(featureDao, 179, 0);

		count += 4;
		insertFourPoints(featureDao, 179,
				ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE - 1);
		count += 4;
		insertFourPoints(featureDao, 90, 45);

		count += 4;
		insertFourLines(featureDao, new double[][] { { 135.0, 67.5 },
				{ 90.0, 45.0 }, { 135.0, 45.0 } });

		count += 4;
		insertFourPolygons(featureDao, new double[][] { { 60.0, 35.0 },
				{ 65.0, 15.0 }, { 15.0, 20.0 }, { 20.0, 40.0 } },
				new double[][] { { 50.0, 30.0 }, { 48.0, 22.0 },
						{ 30.0, 23.0 }, { 25.0, 34.0 } });

		updateLastChange(geoPackage, featureDao);

		return count;
	}

	/**
	 * Create a new feature tiles
	 *
	 * @param geoPackage
	 * @param featureDao
	 * @param useIcon
	 *            true to use an icon instead of the default point
	 * @return feature tiles
	 */
	public static FeatureTiles createFeatureTiles(GeoPackage geoPackage,
			FeatureDao featureDao, boolean useIcon) {

		FeatureTiles featureTiles = new DefaultFeatureTiles(featureDao);

		if (useIcon) {
			BufferedImage icon = new BufferedImage(5, 5,
					BufferedImage.TYPE_INT_ARGB);
			Graphics2D graphics = icon.createGraphics();
			graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
			graphics.setColor(Color.MAGENTA);
			graphics.fillRect(0, 0, 5, 5);
			FeatureTilePointIcon pointIcon = new FeatureTilePointIcon(icon);
			pointIcon.centerIcon();
			featureTiles.setPointIcon(pointIcon);
		} else {
			featureTiles.setPointColor(Color.BLUE);
		}

		featureTiles.setLineColor(Color.GREEN);

		featureTiles.setPolygonColor(Color.RED);

		featureTiles.setFillPolygon(true);
		featureTiles.setPolygonFillColor(new Color(255, 0, 0, 50));

		featureTiles.calculateDrawOverlap();

		return featureTiles;
	}

	public static void insertFourPoints(FeatureDao featureDao, double x,
			double y) {
		insertPoint(featureDao, x, y);
		insertPoint(featureDao, x, -1 * y);
		insertPoint(featureDao, -1 * x, y);
		insertPoint(featureDao, -1 * x, -1 * y);
	}

	public static void insertFourLines(FeatureDao featureDao, double[][] points) {
		insertLine(featureDao, convertPoints(points, false, false));
		insertLine(featureDao, convertPoints(points, true, false));
		insertLine(featureDao, convertPoints(points, false, true));
		insertLine(featureDao, convertPoints(points, true, true));
	}

	public static void insertFourPolygons(FeatureDao featureDao,
			double[][]... points) {
		insertPolygon(featureDao, convertPoints(false, false, points));
		insertPolygon(featureDao, convertPoints(true, false, points));
		insertPolygon(featureDao, convertPoints(false, true, points));
		insertPolygon(featureDao, convertPoints(true, true, points));
	}

	private static double[][] convertPoints(double[][] points,
			boolean negativeX, boolean negativeY) {

		double[][] newPoints = new double[points.length][2];
		for (int i = 0; i < points.length; i++) {
			newPoints[i][0] = negativeX ? points[i][0] * -1 : points[i][0];
			newPoints[i][1] = negativeY ? points[i][1] * -1 : points[i][1];
		}

		return newPoints;
	}

	private static double[][][] convertPoints(boolean negativeX,
			boolean negativeY, double[][]... points) {

		double[][][] newPoints = new double[points.length][][];
		for (int i = 0; i < points.length; i++) {
			newPoints[i] = convertPoints(points[i], negativeX, negativeY);
		}

		return newPoints;
	}

	public static long insertPoint(FeatureDao featureDao, double x, double y) {
		FeatureRow featureRow = featureDao.newRow();
		setPoint(featureRow, x, y);
		return featureDao.insert(featureRow);
	}

	public static void setPoint(FeatureRow featureRow, double x, double y) {
		GeoPackageGeometryData geomData = new GeoPackageGeometryData(
				ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM);
		Point point = new Point(false, false, x, y);
		geomData.setGeometry(point);
		featureRow.setGeometry(geomData);
	}

	public static long insertLine(FeatureDao featureDao, double[][] points) {
		FeatureRow featureRow = featureDao.newRow();
		GeoPackageGeometryData geomData = new GeoPackageGeometryData(
				ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM);
		LineString lineString = getLineString(points);
		geomData.setGeometry(lineString);
		featureRow.setGeometry(geomData);
		return featureDao.insert(featureRow);
	}

	private static LineString getLineString(double[][] points) {
		LineString lineString = new LineString(false, false);
		for (int i = 0; i < points.length; i++) {
			Point point = new Point(false, false, points[i][0], points[i][1]);
			lineString.addPoint(point);
		}
		return lineString;
	}

	public static long insertPolygon(FeatureDao featureDao,
			double[][]... points) {
		FeatureRow featureRow = featureDao.newRow();
		GeoPackageGeometryData geomData = new GeoPackageGeometryData(
				ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM);
		Polygon polygon = new Polygon(false, false);
		for (double[][] ring : points) {
			LineString lineString = getLineString(ring);
			polygon.addRing(lineString);
		}
		geomData.setGeometry(polygon);
		featureRow.setGeometry(geomData);
		return featureDao.insert(featureRow);
	}

	public static void updateLastChange(GeoPackage geoPackage,
			FeatureDao featureDao) throws SQLException {
		Contents contents = featureDao.getGeometryColumns().getContents();
		contents.setLastChange(new Date());
		ContentsDao contentsDao = geoPackage.getContentsDao();
		contentsDao.update(contents);
	}

}