package eu.simstadt.geo.fast_xml_parser;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import com.ximpleware.XPathParseException;
import eu.simstadt.geo.RegionChooserUtils;


public class ConvexHullCalculatorTests
{
	private static final GeometryFactory gf = new GeometryFactory();
	private static final Path repository = Paths.get("src/test/resources/testdata");

	@Test
	public void testExtractConvexHullFromOneBuilding() throws IOException, XPathParseException {
		Path citygmlPath = repository.resolve("Gruenbuehl.proj/20140218_Gruenbuehl_LOD2_1building.gml");
		Geometry hull = ConvexHullCalculator.calculateFromCityGML(citygmlPath);
		assertEquals(hull.getCoordinates().length, 4 + 1); // Convex hull of a building should be a closed rectangle
		Point someBuildingPoint = gf.createPoint(new Coordinate(9.216845, 48.878196)); // WGS84
		assertTrue("Hull should contain every building point", hull.contains(someBuildingPoint));
	}

	@Test
	public void testExtractConvexHullFromOneSmallRegion() throws IOException, XPathParseException {
		Path citygmlPath = repository.resolve("Gruenbuehl.proj/Gruenbuehl_LOD2_ALKIS_1010.gml");
		Geometry hull = ConvexHullCalculator.calculateFromCityGML(citygmlPath);
		assertTrue(hull.getCoordinates().length > 4); // Convex hull should have at least 4 corners
		//		Point somewhereBetweenBuildings = gf.createPoint(new Coordinate(3515883.6668538367, 5415843.300640578)); // Original coordinates, GSK3
		Point somewhereBetweenBuildings = gf.createPoint(new Coordinate(9.21552249084, 48.87980446)); // WGS84
		assertTrue("Hull should contain region between buildings", hull.contains(somewhereBetweenBuildings));
	}

	@Test
	public void testExtractConvexHullFromStoeckachNoBuildingPart() throws IOException, XPathParseException {
		Path citygmlPath = repository.resolve("Stuttgart.proj/Stöckach_überarbeitete GML-NoBuildingPart.gml");
		Geometry hull = ConvexHullCalculator.calculateFromCityGML(citygmlPath);
		assertTrue(hull.getCoordinates().length > 4); // Convex hull should have at least 4 corners
		Point somewhereBetweenBuildings = gf.createPoint(new Coordinate(9.195212, 48.789062)); // WGS84
		assertTrue("Hull should contain region between buildings", hull.contains(somewhereBetweenBuildings));
	}

	@Test
	public void testExtractConvexHullFromEveryCitygmlInRepository() throws IOException {
		int minHullCount = 6;
		Path cachePath = repository.resolve(".cache/hulls");
		if (Files.exists(cachePath)) {
			// In order to make sure that hulls are calculated during the test, not just read from Cache
			Files.walk(cachePath).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
		}

		long gmlCount = RegionChooserUtils.everyCityGML(repository).count();
		AtomicInteger hullCount = new AtomicInteger(0);
		ConvexHullCalculator.extractHullsForEveryCityGML(repository, kmlHull -> {
			assertTrue("KML hull should contain project name", kmlHull.contains("Data name=\"project\""));
			assertTrue("KML hull should contain srs name", kmlHull.contains("Data name=\"srsName\""));
			assertTrue("KML hull should contain epsg id", kmlHull.contains("<value>EPSG:"));
			assertTrue("KML hull should contain coordinates", kmlHull.contains("<coordinates>"));
			hullCount.getAndIncrement();
		});
		assertTrue("At least " + minHullCount + " citygmls should be present in repository", gmlCount >= minHullCount);
		assertTrue("At least " + minHullCount + " hulls should have been calculated", hullCount.get() >= minHullCount);
	}
}
