/*-
 *  Copyright 2020 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.datastructure;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import java.util.ArrayList;
import java.util.List;

import org.citygml4j.factory.GMLGeometryFactory;
import org.citygml4j.model.citygml.transportation.AuxiliaryTrafficArea;
import org.citygml4j.model.citygml.transportation.AuxiliaryTrafficAreaProperty;
import org.citygml4j.model.citygml.transportation.Road;
import org.citygml4j.model.citygml.transportation.TrafficArea;
import org.citygml4j.model.citygml.transportation.TransportationComplex;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurfaceProperty;
import org.junit.Test;

import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject.TransportationType;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;

public class TransportationObjectTest {

	@Test
	public void testGetFeatureType() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		assertEquals(FeatureType.TRANSPORTATION, to.getFeatureType());
	}
	
	@Test(expected = IllegalStateException.class)
	public void testReCreateGeometriesSolid() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD1));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
	}
	
	@Test
	public void testReCreateGeometriesWithComposedOf() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		TransportationComplex tcMock = mock(TransportationComplex.class);
		to.setGmlObject(tcMock);
		TransportationObject ataTo = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_AREA);
		AuxiliaryTrafficArea ataMock = mock(AuxiliaryTrafficArea.class);
		ataTo.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD2));
		ataTo.setGmlObject(ataMock);
		tcMock.addAuxiliaryTrafficArea(new AuxiliaryTrafficAreaProperty(ataMock));

		to.addChild(ataTo);
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
		verify(tcMock, never()).setLod1MultiSurface(any());
		verify(ataMock).setLod2MultiSurface(any());
	}

	
	@Test
	public void testReCreateGeometriesMultiSurfaceLod1() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		Road roadMock = mock(Road.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD1));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
		verify(roadMock).setLod1MultiSurface(any());
	}
	
	@Test
	public void testReCreateGeometriesMultiSurfaceLod2() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		Road roadMock = mock(Road.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD2));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
		verify(roadMock).setLod2MultiSurface(any());
	}
	
	@Test
	public void testReCreateGeometriesMultiSurfaceLod3() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		Road roadMock = mock(Road.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD3));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
		verify(roadMock).setLod3MultiSurface(any());
	}
	
	@Test
	public void testReCreateGeometriesMultiSurfaceLod4() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		Road roadMock = mock(Road.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD4));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
		verify(roadMock).setLod4MultiSurface(any());
	}
	
	@Test(expected = IllegalStateException.class)
	public void testReCreateGeometriesMultiSurfaceLod0() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		Road roadMock = mock(Road.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD0));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
	}
	
	@Test(expected = IllegalStateException.class)
	public void testReCreateGeometriesTrafficAreaMultiSurfaceLod1() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		TrafficArea roadMock = mock(TrafficArea.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD1));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
	}
	
	@Test
	public void testReCreateGeometriesTrafficAreaMultiSurfaceLod2() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		TrafficArea roadMock = mock(TrafficArea.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD2));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
		verify(roadMock).setLod2MultiSurface(any());
	}
	
	@Test
	public void testReCreateGeometriesTrafficAreaMultiSurfaceLod3() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		TrafficArea roadMock = mock(TrafficArea.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD3));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
		verify(roadMock).setLod3MultiSurface(any());
	}
	
	@Test
	public void testReCreateGeometriesTrafficAreaMultiSurfaceLod4() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		TrafficArea roadMock = mock(TrafficArea.class);
		to.setGmlObject(roadMock);
		to.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD4));
		GMLGeometryFactory factory = new GMLGeometryFactory();
		to.reCreateGeometries(factory, mock(ParserConfiguration.class));
		verify(roadMock).setLod4MultiSurface(any());
	}
	
	@Test
	public void testContainsError() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		to.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
		assertTrue(to.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
		assertFalse(to.containsError(CheckId.C_GE_P_INNER_RINGS_NESTED));
	}
	
	@Test
	public void testContainsErrorInComposesOf() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		TransportationObject to2 = new TransportationObject(TransportationType.TRACK);
		to.addChild(to2);
		to2.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
		assertTrue(to.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
		assertFalse(to.containsError(CheckId.C_GE_P_INNER_RINGS_NESTED));
	}
	
	@Test
	public void testClearAllContainedCheckResults() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		TransportationObject to2 = mock(TransportationObject.class);
		to.addChild(to2);
		assertFalse(to.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
		to.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
		assertTrue(to.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
		to.clearAllContainedCheckResults();
		assertFalse(to.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
		verify(to2).clearAllContainedCheckResults();
	}
	
	@Test
	public void testCollectContainedErrors() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		TransportationObject to2 = mock(TransportationObject.class);
		to.addChild(to2);
		to.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
		List<CheckError> errors = new ArrayList<>();
		to.collectContainedErrors(errors);
		assertEquals(1, errors.size());
		verify(to2).collectContainedErrors(errors);
	}
	
	@Test
	public void testUnsetGmlGeometriesRoad() {
		TransportationObject to = new TransportationObject(TransportationType.ROAD);
		Road r = mock(Road.class);
		to.setGmlObject(r);
		r.setLod1MultiSurface(mock(MultiSurfaceProperty.class));
		r.setLod2MultiSurface(mock(MultiSurfaceProperty.class));
		r.setLod3MultiSurface(mock(MultiSurfaceProperty.class));
		r.setLod4MultiSurface(mock(MultiSurfaceProperty.class));
		to.unsetGmlGeometries();
		assertNull(r.getLod1MultiSurface());
		assertNull(r.getLod2MultiSurface());
		assertNull(r.getLod3MultiSurface());
		assertNull(r.getLod4MultiSurface());
	}
	
	@Test
	public void testUnsetGmlGeometriesTrafficArea() {
		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
		TrafficArea r = mock(TrafficArea.class);
		to.setGmlObject(r);
		r.setLod2MultiSurface(mock(MultiSurfaceProperty.class));
		r.setLod3MultiSurface(mock(MultiSurfaceProperty.class));
		r.setLod4MultiSurface(mock(MultiSurfaceProperty.class));
		to.unsetGmlGeometries();
		assertNull(r.getLod2MultiSurface());
		assertNull(r.getLod3MultiSurface());
		assertNull(r.getLod4MultiSurface());
	}
	
	@Test
	public void testUnsetGmlGeometriesAuxilliaryTrafficArea() {
		TransportationObject to = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_AREA);
		AuxiliaryTrafficArea r = mock(AuxiliaryTrafficArea.class);
		to.setGmlObject(r);
		r.setLod2MultiSurface(mock(MultiSurfaceProperty.class));
		r.setLod3MultiSurface(mock(MultiSurfaceProperty.class));
		r.setLod4MultiSurface(mock(MultiSurfaceProperty.class));
		to.unsetGmlGeometries();
		assertNull(r.getLod2MultiSurface());
		assertNull(r.getLod3MultiSurface());
		assertNull(r.getLod4MultiSurface());
	}

}
