/*-
 *  Copyright 2023 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
 * 
 *  This file is part of CityDoctor2.
 *
 *  CityDoctor2 is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  CityDoctor2 is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with CityDoctor2.  If not, see <https://www.gnu.org/licenses/>.
 */
package de.hft.stuttgart.citydoctor2.check;

import java.util.List;

import org.xmlobjects.gml.model.measures.Angle;
import org.xmlobjects.gml.model.measures.Length;

import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError;
import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError;
import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError;
import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError;
import de.hft.stuttgart.citydoctor2.check.error.MultipleConnectedComponentsError;
import de.hft.stuttgart.citydoctor2.check.error.NestedRingError;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldVertexError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonNormalsDeviation;
import de.hft.stuttgart.citydoctor2.check.error.NotCeilingError;
import de.hft.stuttgart.citydoctor2.check.error.NotFloorError;
import de.hft.stuttgart.citydoctor2.check.error.NotGroundError;
import de.hft.stuttgart.citydoctor2.check.error.NotWallError;
import de.hft.stuttgart.citydoctor2.check.error.NullAreaError;
import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonHoleOutsideError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonIntersectingRingsError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonSameOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWithoutSurfaceError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError;
import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError;
import de.hft.stuttgart.citydoctor2.check.error.RingNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
import de.hft.stuttgart.citydoctor2.utils.QualityADEUtils;
import de.hft.stuttgart.quality.model.enums.ResultType;
import de.hft.stuttgart.quality.model.enums.RingSelfIntType;
import de.hft.stuttgart.quality.model.properties.AbstractErrorProperty;
import de.hft.stuttgart.quality.model.properties.EdgeListProperty;
import de.hft.stuttgart.quality.model.properties.EdgeProperty;
import de.hft.stuttgart.quality.model.properties.PolygonIdListProperty;
import de.hft.stuttgart.quality.model.types.AllPolygonsOrientedWrongError;
import de.hft.stuttgart.quality.model.types.ConsecutivePointsSameError;
import de.hft.stuttgart.quality.model.types.EdgeList;
import de.hft.stuttgart.quality.model.types.HoleOutsideError;
import de.hft.stuttgart.quality.model.types.InnerRingsNestedError;
import de.hft.stuttgart.quality.model.types.InteriorDisconnectedError;
import de.hft.stuttgart.quality.model.types.IntersectingRingsError;
import de.hft.stuttgart.quality.model.types.MultipleComponentsError;
import de.hft.stuttgart.quality.model.types.OrientationRingsSameError;
import de.hft.stuttgart.quality.model.types.PlanarDistancePlaneError;
import de.hft.stuttgart.quality.model.types.PlanarNormalsDeviationError;
import de.hft.stuttgart.quality.model.types.PolygonIdList;
import de.hft.stuttgart.quality.model.types.RingSelfIntersectionError;
import de.hft.stuttgart.quality.model.types.SemanticAttributeInvalidError;
import de.hft.stuttgart.quality.model.types.SemanticAttributeMissingError;
import de.hft.stuttgart.quality.model.types.SemanticAttributeWrongValueError;
import de.hft.stuttgart.quality.model.types.SolidSelfIntersectionError;
import de.hft.stuttgart.quality.model.types.SolidTooFewPolygonsError;
import de.hft.stuttgart.quality.model.types.ValidationResult;

public class QualityAdeErrorVisitor implements ErrorVisitor {
	
	private ValidationResult res;
	
	public QualityAdeErrorVisitor(ValidationResult res) {
		this.res = res;
	}

	@Override
	public void visit(PolygonHoleOutsideError cdErr) {
		for (LinearRing ringsOutside : cdErr.getHolesOutside()) {
			HoleOutsideError err = new HoleOutsideError();
			err.setPolygonId(cdErr.getPolygon().getGmlId().getGmlString());
			err.setLinearRingId(ringsOutside.getGmlId().getGmlString());
			res.getErrors().add(new AbstractErrorProperty(err));
		}
	}


	@Override
	public void visit(NonManifoldEdgeError cdErr) {
		de.hft.stuttgart.quality.model.types.NonManifoldEdgeError err = new de.hft.stuttgart.quality.model.types.NonManifoldEdgeError();
		err.setGeometryId(cdErr.getGeometry().getGmlId().getGmlString());
		EdgeList edgeList = new EdgeList();
		err.setEdges(new EdgeListProperty(edgeList));
		List<EdgeProperty> edges = edgeList.getEdges();
		for (Edge e : cdErr.getEdges()) {
			edges.add(new EdgeProperty(QualityADEUtils.convertEdge(e)));
		}		
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(MultipleConnectedComponentsError cdErr) {
		MultipleComponentsError err = new MultipleComponentsError();
		for (List<Polygon> component : cdErr.getComponents()) {
			PolygonIdList idList = new PolygonIdList();
			err.getComponents().add(new PolygonIdListProperty(idList));
			for (Polygon p : component) {
				idList.getPolygonIds().add(p.getGmlId().getGmlString());
			}
		}
		err.setGeometryId(cdErr.getGeometry().getGmlId().getGmlString());		
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(NestedRingError cdErr) {
		InnerRingsNestedError err = new InnerRingsNestedError();
		err.setPolygonId(cdErr.getPolygon().getGmlId().getGmlString());
		err.setLinearRingId1(cdErr.getInnerRing().getGmlId().getGmlString());
		err.setLinearRingId2(cdErr.getWithinRing().getGmlId().getGmlString());		
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(NonManifoldVertexError cdErr) {
		var err = new de.hft.stuttgart.quality.model.types.NonManifoldVertexError();
		err.setGeometryId(cdErr.getGeometry().getGmlId().getGmlString());
		err.setVertex(QualityADEUtils.convertVertex(cdErr.getVertex()));
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(PolygonWrongOrientationError cdErr) {
		var err = new de.hft.stuttgart.quality.model.types.PolygonWrongOrientationError();
		err.setGeometryId(cdErr.getGeometry().getGmlId().getGmlString());
		EdgeList edgeList = new EdgeList();
		err.setEdges(new EdgeListProperty(edgeList));
		List<EdgeProperty> edges = edgeList.getEdges();
		for (Edge e : cdErr.getEdges()) {
			var adeEdge = new de.hft.stuttgart.quality.model.types.Edge();
			adeEdge.setFrom(QualityADEUtils.convertVertex(e.getFrom()));
			adeEdge.setTo(QualityADEUtils.convertVertex(e.getTo()));
			edges.add(new EdgeProperty(adeEdge));
		}
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(PolygonSameOrientationError cdErr) {
		var err = new OrientationRingsSameError();
		err.setLinearRing(cdErr.getInnerRing().getGmlId().getGmlString());
		err.setPolygonId(cdErr.getPolygon().getGmlId().getGmlString());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(SolidNotClosedError cdErr) {
		var err = new de.hft.stuttgart.quality.model.types.SolidNotClosedError();
		err.setGeometryId(cdErr.getGeometry().getGmlId().getGmlString());
		EdgeList edgeList = new EdgeList();
		err.setEdges(new EdgeListProperty(edgeList));
		List<EdgeProperty> edges = edgeList.getEdges();
		for (Edge e : cdErr.getErrorEdges()) {
			var adeEdge = new de.hft.stuttgart.quality.model.types.Edge();
			adeEdge.setFrom(QualityADEUtils.convertVertex(e.getFrom()));
			adeEdge.setTo(QualityADEUtils.convertVertex(e.getTo()));
			edges.add(new EdgeProperty(adeEdge));
		}
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(DependenciesNotMetError cdErr) {
		// this error cannot be translated
		res.setResultType(ResultType.ERROR);
	}

	@Override
	public void visit(UnknownCheckError cdErr) {
		// this error cannot be translated
		res.setResultType(ResultType.ERROR);
	}

	@Override
	public void visit(RingNotClosedError cdErr) {
		var err = new de.hft.stuttgart.quality.model.types.RingNotClosedError();
		err.setLinearRingId(cdErr.getRing().getGmlId().getGmlString());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(ConsecutivePointSameError cdErr) {
		var  err = new ConsecutivePointsSameError();
		err.setVertex1(QualityADEUtils.convertVertex(cdErr.getVertex1()));
		err.setVertex2(QualityADEUtils.convertVertex(cdErr.getVertex2()));
		err.setLinearRingId(cdErr.getRing().getGmlId().getGmlString());		
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(AllPolygonsWrongOrientationError cdErr) {
		var err = new AllPolygonsOrientedWrongError();
		err.setGeometryId(cdErr.getGeometry().getGmlId().getGmlString());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(PolygonInteriorDisconnectedError cdErr) {
		var err = new InteriorDisconnectedError();
		err.setPolygonId(cdErr.getPolygon().getGmlId().getGmlString());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(NullAreaError cdErr) {
		// this error cannot be translated but should also be a self intersection
	}

	@Override
	public void visit(RingTooFewPointsError cdErr) {
		var err = new de.hft.stuttgart.quality.model.types.RingTooFewPointsError();
		err.setLinearRingId(cdErr.getRing().getGmlId().getGmlString());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(NonPlanarPolygonNormalsDeviation cdErr) {
		var err = new PlanarNormalsDeviationError();
		err.setDeviation(new Angle(cdErr.getDeviation(), "rad"));
		err.setPolygonId(cdErr.getPolygon().getGmlId().getGmlString());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(NonPlanarPolygonDistancePlaneError cdErr) {
		var err = new PlanarDistancePlaneError();
		err.setDistance(new Length(cdErr.getDistance(), "m"));
		err.setPolygonId(cdErr.getPolygon().getGmlId().getGmlString());
		err.setVertex(QualityADEUtils.convertVertex(cdErr.getVertex()));
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(PolygonIntersectingRingsError cdErr) {
		var err = new IntersectingRingsError();
		err.setLinearRingId1(cdErr.getIntersectingRings().getValue0().getGmlId().getGmlString());
		err.setLinearRingId2(cdErr.getIntersectingRings().getValue1().getGmlId().getGmlString());
		err.setPolygonId(cdErr.getPolygon().getGmlId().getGmlString());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(SolidSelfIntError cdErr) {
		for (PolygonIntersection intersection : cdErr.getIntersections()) {
			var err = new SolidSelfIntersectionError();
			err.setGeometryId(cdErr.getGeometry().getGmlId().getGmlString());
			err.setPolygonId1(intersection.getP1().getGmlId().getGmlString());
			err.setPolygonId2(intersection.getP2().getGmlId().getGmlString());
			res.getErrors().add(new AbstractErrorProperty(err));
		}
	}

	@Override
	public void visit(TooFewPolygonsError cdErr) {
		var err = new SolidTooFewPolygonsError();
		err.setGeometryId(cdErr.getGeometry().getGmlId().getGmlString());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(RingDuplicatePointError cdErr) {
		var err = new RingSelfIntersectionError();
		err.setType(RingSelfIntType.DUPLICATE_POINT);
		err.setLinearRingId(cdErr.getRing().getGmlId().getGmlString());
		err.setVertex1(QualityADEUtils.convertVertex(cdErr.getVertex1()));
		err.setVertex2(QualityADEUtils.convertVertex(cdErr.getVertex2()));
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(RingEdgeIntersectionError cdErr) {
		var err = new RingSelfIntersectionError();
		err.setType(RingSelfIntType.EDGE_INTERSECTION);
		err.setLinearRingId(cdErr.getRing().getGmlId().getGmlString());
		var e1 = new de.hft.stuttgart.quality.model.types.Edge();
		e1.setFrom(QualityADEUtils.convertVertex(cdErr.getEdge1().getFrom()));
		e1.setTo(QualityADEUtils.convertVertex(cdErr.getEdge1().getTo()));
		err.setEdge1(new EdgeProperty(e1));
		
		var e2 = new de.hft.stuttgart.quality.model.types.Edge();
		e2.setFrom(QualityADEUtils.convertVertex(cdErr.getEdge2().getFrom()));
		e2.setTo(QualityADEUtils.convertVertex(cdErr.getEdge2().getTo()));
		err.setEdge2(new EdgeProperty(e2));
		
		err.setVertex1(QualityADEUtils.convertVertex(cdErr.getIntersection()));
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(PointTouchesEdgeError cdErr) {
		var err = new RingSelfIntersectionError();
		err.setType(RingSelfIntType.VERTEX_TOUCHES_EDGE);
		err.setLinearRingId(cdErr.getRing().getGmlId().getGmlString());
		
		var e1 = new de.hft.stuttgart.quality.model.types.Edge();
		e1.setFrom(QualityADEUtils.convertVertex(cdErr.getEdge().getFrom()));
		e1.setTo(QualityADEUtils.convertVertex(cdErr.getEdge().getTo()));
		err.setEdge1(new EdgeProperty(e1));
		
		err.setVertex1(QualityADEUtils.convertVertex(cdErr.getVertex()));
		
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(NotCeilingError cdErr) {
		// not translated
	}

	@Override
	public void visit(NotFloorError cdErr) {
		// not translated
	}

	@Override
	public void visit(NotWallError cdErr) {
		// not translated
	}

	@Override
	public void visit(NotGroundError cdErr) {
		// not translated
	}

	@Override
	public void visit(CheckError err) {
		// not translated
	}

	@Override
	public void visit(SchematronError err) {
		// not translated
	}

	@Override
	public void visit(SurfaceUnfragmentedError err) {
		// not translated
	}

	@Override
	public void visit(DegeneratedRingError cdErr) {
		// not translated
	}

	@Override
	public void visit(AttributeMissingError cdErr) {
		var err = new SemanticAttributeMissingError();
		err.setChildId(cdErr.getChildId());
		err.setAttributeName(cdErr.getNameOfAttribute());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(AttributeValueWrongError cdErr) {
		SemanticAttributeWrongValueError err = new SemanticAttributeWrongValueError();
		err.setChildId(cdErr.getChildId());
		err.setAttributeName(cdErr.getNameOfAttribute());
		res.getErrors().add(new AbstractErrorProperty(err));
	}
	
	@Override
	public void visit(AttributeInvalidError cdErr) {
		SemanticAttributeInvalidError err = new SemanticAttributeInvalidError();
		err.setChildId(cdErr.getChildId());
		err.setAttributeName(cdErr.getNameOfAttribute());
		res.getErrors().add(new AbstractErrorProperty(err));
	}

	@Override
	public void visit(PolygonWithoutSurfaceError err) {
		// not translated
	}

}
