package de.hft.stuttgart.citygml.yoc.analysis;

import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;

import org.citygml4j.core.model.CityGMLVersion;
import org.citygml4j.core.model.building.Building;
import org.citygml4j.core.model.core.AbstractCityObject;
import org.citygml4j.core.model.core.AbstractCityObjectProperty;
import org.citygml4j.core.model.core.AbstractFeature;
import org.citygml4j.core.model.core.CityModel;
import org.citygml4j.xml.CityGMLContext;
import org.citygml4j.xml.CityGMLContextException;
import org.citygml4j.xml.module.citygml.CoreModule;
import org.citygml4j.xml.reader.CityGMLInputFactory;
import org.citygml4j.xml.reader.CityGMLReadException;
import org.citygml4j.xml.reader.CityGMLReader;
import org.citygml4j.xml.writer.CityGMLOutputFactory;
import org.citygml4j.xml.writer.CityGMLWriteException;
import org.citygml4j.xml.writer.CityGMLWriter;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.xmlobjects.gml.model.geometry.DirectPosition;

public class YearOfConstructionAnalyzer {

	public static void main(String[] args) throws CityGMLContextException, CityGMLReadException, CityGMLWriteException {
		Path inputFile = Paths.get("data", "Grombühl_v3.gml");
		CityModel cityModel = readCityGml(inputFile);
		List<AbstractCityObjectProperty> cityObjectMembers = cityModel.getCityObjectMembers();
		Map<String, AtomicInteger> missingYocUsageCount = new HashMap<>();

		GeometryFactory factory = new GeometryFactory();
		Map<Point, Building> centersMap = new HashMap<>();

		for (AbstractCityObjectProperty acop : cityObjectMembers) {
			AbstractCityObject abstractCityObject = acop.getObject();
			if (abstractCityObject instanceof Building b) {
				DirectPosition center = b.computeEnvelope().getCenter();
				Point point = factory.createPoint(new Coordinate(center.getValue().get(0), center.getValue().get(1)));
				centersMap.put(point, b);
			}
		}
		
		for (AbstractCityObjectProperty acop : cityObjectMembers) {
			AbstractCityObject abstractCityObject = acop.getObject();
			if (abstractCityObject instanceof Building b && b.getDateOfConstruction() == null) {
				String function = b.getFunctions().get(0).getValue();
				if ("31001_1000".equals(function)) {
					DirectPosition center = b.computeEnvelope().getCenter();
					double lowestDistance = Double.MAX_VALUE;
					Point point = factory.createPoint(new Coordinate(center.getValue().get(0), center.getValue().get(1)));
					Building nearestBuilding = null;
					for (Entry<Point, Building> e : centersMap.entrySet()) {
						if (e.getValue() == b) {
							continue;
						}
						if (e.getKey().distance(point) < lowestDistance && e.getValue().getDateOfConstruction() != null) {
							lowestDistance = e.getKey().distance(point);
							nearestBuilding = e.getValue();
						}
					}
					
					if (nearestBuilding == null) {
						throw new IllegalStateException();
					}
					System.out.println("Assigning " + b.getId() + " yoc of: " + nearestBuilding.getDateOfConstruction());
					b.setDateOfConstruction(nearestBuilding.getDateOfConstruction());
				}
				missingYocUsageCount.compute(function, (k, v) -> {
					if (v == null) {
						v = new AtomicInteger(0);
					}
					v.getAndIncrement();
					return v;
				});
			}
		}
		writeCityGML(cityModel, Paths.get("out.gml"));
	}

	private static CityModel readCityGml(Path inFile) throws CityGMLContextException, CityGMLReadException {
		CityGMLContext context = CityGMLContext.newInstance();
		CityGMLInputFactory in = context.createCityGMLInputFactory();
		try (CityGMLReader reader = in.createCityGMLReader(inFile)) {
			AbstractFeature feature = reader.next();
			if (feature instanceof CityModel cm) {
				return cm;
			}
		}
		throw new IllegalStateException("CityGML does not contain a CityModel feature type");
	}
	
	private static void writeCityGML(CityModel cityModel, Path outputPath)
			throws CityGMLWriteException, CityGMLContextException {
		CityGMLContext context = CityGMLContext.newInstance();
		CityGMLVersion version = CityGMLVersion.v2_0;
		CityGMLOutputFactory out = context.createCityGMLOutputFactory(version);

		try (CityGMLWriter writer = out.createCityGMLWriter(outputPath, StandardCharsets.UTF_8.name())) {
			writer.withIndent("  ").withDefaultSchemaLocations().withDefaultPrefixes()
					.withDefaultNamespace(CoreModule.of(version).getNamespaceURI()).write(cityModel);
		}
	}

}
