/*-
 *  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.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import org.citygml4j.core.model.bridge.AbstractBridge;
import org.citygml4j.core.model.bridge.Bridge;
import org.citygml4j.core.model.deprecated.bridge.DeprecatedPropertiesOfAbstractBridge;
import org.citygml4j.core.util.geometry.GeometryFactory;
import org.junit.Test;

import de.hft.stuttgart.citydoctor2.check.AbstractCheck;
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.BridgeObject.BridgeType;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;

public class BridgeObjectTest {

	@Test
	public void testAccept() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		bo.addBoundarySurface(new BoundarySurface(null));
		AtomicInteger boCounter = new AtomicInteger(0);
		AtomicInteger bsCounter = new AtomicInteger(0);
		AbstractCheck c = new AbstractCheck() {

			@Override
			public void check(BridgeObject bo) {
				boCounter.incrementAndGet();
			}

			@Override
			public void check(BoundarySurface bs) {
				bsCounter.incrementAndGet();
			}
		};
		bo.accept(c);
		assertEquals(1, boCounter.intValue());
		assertEquals(1, bsCounter.intValue());

		AtomicInteger boCounter2 = new AtomicInteger(0);
		c = new AbstractCheck();
		bo.accept(c);
		assertEquals(0, boCounter2.intValue());
	}

	@Test
	public void testPrepareForChecking() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		BoundarySurface bsMock = mock(BoundarySurface.class);
		bo.addBoundarySurface(bsMock);
		bo.prepareForChecking();
		verify(bsMock).prepareForChecking();
	}

	@Test
	public void testClearMetaInformation() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		BoundarySurface bsMock = mock(BoundarySurface.class);
		bo.addBoundarySurface(bsMock);
		bo.clearMetaInformation();
		verify(bsMock).clearMetaInformation();
	}

	@Test
	public void testContainsError() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		assertFalse(bo.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
		bo.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
		assertTrue(bo.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
	}

	@Test
	public void testContainsError2() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		BoundarySurface bsMock = mock(BoundarySurface.class);
		bo.addBoundarySurface(bsMock);
		assertFalse(bo.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
		when(bsMock.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)).thenReturn(true);
		assertTrue(bo.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
	}

	@Test
	public void testClearAllContainedCheckResults() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		BoundarySurface bsMock = mock(BoundarySurface.class);
		bo.addBoundarySurface(bsMock);
		bo.clearAllContainedCheckResults();
		verify(bsMock).clearAllContainedCheckResults();
	}

	@Test
	public void testContainsAnyError() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		BoundarySurface bsMock = mock(BoundarySurface.class);
		bo.addBoundarySurface(bsMock);
		bo.containsAnyError();
		verify(bsMock).containsAnyError();
		
		when(bsMock.containsAnyError()).thenReturn(true);
		assertTrue(bo.containsAnyError());
		
		BridgeObject bo2 = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		bo2.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
		assertTrue(bo2.containsAnyError());
	}

	@Test
	public void testCollectContainedErrors() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		BoundarySurface bsMock = mock(BoundarySurface.class);
		bo.addBoundarySurface(bsMock);
		ArrayList<CheckError> errors = new ArrayList<>();
		bo.collectContainedErrors(errors);
		verify(bsMock).collectContainedErrors(errors);
	}

	@Test
	public void testReCreateGeometries() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		BoundarySurface bsMock = mock(BoundarySurface.class);
		bo.addBoundarySurface(bsMock);
		GeometryFactory factoryMock = mock(GeometryFactory.class);
		ParserConfiguration configMock = mock(ParserConfiguration.class);
		bo.reCreateGeometries(factoryMock, configMock);
		verify(bsMock).reCreateGeometries(factoryMock, configMock);
	}
	
	@Test
	public void testReCreateGeometriesMultiSurfaceLod1() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		Geometry geom = GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD1);
		AbstractBridge ab = new Bridge();
		bo.setGmlObject(ab);
		bo.addGeometry(geom);
		GeometryFactory factory = GeometryFactory.newInstance();
		ParserConfiguration config = new ParserConfiguration(8, false);
		bo.reCreateGeometries(factory, config);
		assertNotNull(ab.getDeprecatedProperties().getLod1MultiSurface());
		assertNotNull(ab.getDeprecatedProperties().getLod1MultiSurface().getObject());

	}
	
	@Test
	public void testReCreateGeometriesMultiSurfaceLod2() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		Geometry geom = GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD2);
		AbstractBridge ab = new Bridge();
		bo.setGmlObject(ab);
		bo.addGeometry(geom);
		GeometryFactory factory = GeometryFactory.newInstance();
		ParserConfiguration config = new ParserConfiguration(8, false);
		bo.reCreateGeometries(factory, config);
		assertNotNull(ab.getLod2MultiSurface());
		assertNotNull(ab.getLod2MultiSurface().getObject());
	}
	
	@Test
	public void testReCreateGeometriesMultiSurfaceLod3() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		Geometry geom = GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD3);
		AbstractBridge ab = new Bridge();
		bo.setGmlObject(ab);
		bo.addGeometry(geom);
		GeometryFactory factory = GeometryFactory.newInstance();
		ParserConfiguration config = new ParserConfiguration(8, false);
		bo.reCreateGeometries(factory, config);
		assertNotNull(ab.getLod3MultiSurface());
		assertNotNull(ab.getLod3MultiSurface().getObject());
	}
	
	@Test
	public void testReCreateGeometriesMultiSurfaceLod4() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		Geometry geom = GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD4);
		AbstractBridge ab = new Bridge();
		bo.setGmlObject(ab);
		bo.addGeometry(geom);
		GeometryFactory factory = GeometryFactory.newInstance();
		ParserConfiguration config = new ParserConfiguration(8, false);
		bo.reCreateGeometries(factory, config);
		assertNotNull(ab.getDeprecatedProperties().getLod4MultiSurface());
		assertNotNull(ab.getDeprecatedProperties().getLod4MultiSurface().getObject());
	}
	
	@Test
	public void testReCreateGeometriesSolidLod1() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		Geometry geom = GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD1);
		AbstractBridge ab = new Bridge();
		bo.setGmlObject(ab);
		bo.addGeometry(geom);
		GeometryFactory factory = GeometryFactory.newInstance();
		ParserConfiguration config = new ParserConfiguration(8, false);
		bo.reCreateGeometries(factory, config);
		assertNotNull(ab.getLod1Solid());
		assertNotNull(ab.getLod1Solid().getObject());
	}
	
	@Test
	public void testReCreateGeometriesSolidLod2() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		Geometry geom = GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD2);
		AbstractBridge ab = new Bridge();
		bo.setGmlObject(ab);
		bo.addGeometry(geom);
		GeometryFactory factory = GeometryFactory.newInstance();
		ParserConfiguration config = new ParserConfiguration(8, false);
		bo.reCreateGeometries(factory, config);
		assertNotNull(ab.getLod2Solid());
		assertNotNull(ab.getLod2Solid().getObject());
	}
	
	@Test
	public void testReCreateGeometriesSolidLod3() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		Geometry geom = GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD3);
		AbstractBridge ab = new Bridge();
		bo.setGmlObject(ab);
		bo.addGeometry(geom);
		GeometryFactory factory = GeometryFactory.newInstance();
		ParserConfiguration config = new ParserConfiguration(8, false);
		bo.reCreateGeometries(factory, config);
		assertNotNull(ab.getLod3Solid());
		assertNotNull(ab.getLod3Solid().getObject());
	}
	
	@Test
	public void testReCreateGeometriesSolidLod4() {
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, mock(AbstractBridge.class));
		Geometry geom = GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD4);
		AbstractBridge ab = new Bridge();
		bo.setGmlObject(ab);
		bo.addGeometry(geom);
		GeometryFactory factory = GeometryFactory.newInstance();
		ParserConfiguration config = new ParserConfiguration(8, false);
		bo.reCreateGeometries(factory, config);
		assertNotNull(ab.getDeprecatedProperties().getLod4Solid());
		assertNotNull(ab.getDeprecatedProperties().getLod4Solid().getObject());
	}

	@Test
	public void testUnsetGmlGeometries() {
		AbstractBridge abMock = mock(AbstractBridge.class);
		DeprecatedPropertiesOfAbstractBridge propsMock = mock(DeprecatedPropertiesOfAbstractBridge.class);
		when(abMock.getDeprecatedProperties()).thenReturn(propsMock);
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, abMock);
		BoundarySurface bsMock = mock(BoundarySurface.class);
		bo.addBoundarySurface(bsMock);
		
		bo.unsetGmlGeometries();
		verify(bsMock).unsetGmlGeometries();
		verify(propsMock).setLod1MultiSurface(null);
		verify(abMock).setLod2MultiSurface(null);
		verify(abMock).setLod3MultiSurface(null);
		verify(propsMock).setLod4MultiSurface(null);
		
		verify(abMock).setLod1Solid(null);
		verify(abMock).setLod2Solid(null);
		verify(abMock).setLod3Solid(null);
		verify(propsMock).setLod4Solid(null);
	}

	@Test
	public void testGetFeatureType() {
		AbstractBridge abMock = mock(AbstractBridge.class);
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, abMock);
		assertEquals(FeatureType.BRIDGE, bo.getFeatureType());
	}

	@Test
	public void testGetType() {
		AbstractBridge abMock = mock(AbstractBridge.class);
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, abMock);
		assertEquals(BridgeType.BRIDGE, bo.getType());
		bo.setType(BridgeType.BRIDGE_PART);
		assertEquals(BridgeType.BRIDGE_PART, bo.getType());
	}

	@Test
	public void testGetGmlObject() {
		AbstractBridge abMock = mock(AbstractBridge.class);
		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, abMock);
		assertEquals(abMock, bo.getGmlObject());
		AbstractBridge abMock2 = mock(AbstractBridge.class);
		bo.setGmlObject(abMock2);
		assertEquals(abMock2, bo.getGmlObject());
	}
}
