package eu.simstadt.regionchooser.fast_xml_parser;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.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.jupiter.api.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.regionchooser.RegionChooserUtils;
import eu.simstadt.regionchooser.fast_xml_parser.ConvexHullCalculator;


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(4 + 1, hull.getCoordinates().length); // Convex hull of a building should be a closed rectangle
		Point someBuildingPoint = gf.createPoint(new Coordinate(9.216845, 48.878196)); // WGS84
		assertTrue(hull.contains(someBuildingPoint), "Hull should contain every building point");
	}

	@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.contains(somewhereBetweenBuildings), "Hull should contain region between buildings");
	}

	@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.contains(somewhereBetweenBuildings), "Hull should contain region between buildings");
	}

	@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(kmlHull.contains("Data name=\"project\""), "KML hull should contain project name");
			assertTrue(kmlHull.contains("Data name=\"srsName\""), "KML hull should contain srs name");
			assertTrue(kmlHull.contains("<value>EPSG:"), "KML hull should contain epsg id");
			assertTrue(kmlHull.contains("<coordinates>"), "KML hull should contain coordinates");
			hullCount.getAndIncrement();
		});
		assertTrue(gmlCount >= minHullCount, "At least " + minHullCount + " citygmls should be present in repository");
		assertTrue(hullCount.get() >= minHullCount, "At least " + minHullCount + " hulls should have been calculated");
	}
}
