/*-
 *  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.checks;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.checks.geometry.RingNotClosedCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.MultipleConnectedComponentCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.DuplicatePointsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.PolygonWrongOrientationCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.FaceOutCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.HoleOutsideCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.InteriorDisconnectedCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.ManifoldVertexCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.NestedRingsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.NumPointsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.NonManifoldEdgeCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.PlanarCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.PolygonSameOrientationCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.PolygonIntersectingRingsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.RingSelfIntCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.SolidNotClosedCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.SolidSelfIntCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.TooFewPolygonsCheck;
import de.hft.stuttgart.citydoctor2.checks.semantics.IsCeilingCheck;
import de.hft.stuttgart.citydoctor2.checks.semantics.IsFloorCheck;
import de.hft.stuttgart.citydoctor2.checks.semantics.IsGroundCheck;
import de.hft.stuttgart.citydoctor2.checks.semantics.IsWallCheck;
import de.hft.stuttgart.citydoctor2.checks.semantics.RoofSurfaceUnfragmentedCheck;
import de.hft.stuttgart.citydoctor2.utils.Localization;

/**
 * Container class for all checks. If new checks are added, they need to be
 * published here to be accessed by the checker instance.
 * 
 * @author Matthias Betz
 *
 */
public class Checks {

	private static final Logger logger = LogManager.getLogger(Checks.class);

	private static List<CheckPrototype> checkPrototypes;
	private static Map<CheckId, CheckPrototype> prototypeMap;

	private Map<CheckId, Check> checkMap;

	static {
		checkPrototypes = new ArrayList<>();
		prototypeMap = new EnumMap<>(CheckId.class);

		// add new checks here
		publish(new NumPointsCheck());
		publish(new RingNotClosedCheck());
		publish(new DuplicatePointsCheck());
		publish(new RingSelfIntCheck());
		publish(new PlanarCheck());
		publish(new PolygonSameOrientationCheck());
		publish(new HoleOutsideCheck());
		publish(new NestedRingsCheck());
		publish(new PolygonIntersectingRingsCheck());
		publish(new InteriorDisconnectedCheck());
		publish(new MultipleConnectedComponentCheck());
		publish(new SolidNotClosedCheck());
		publish(new NonManifoldEdgeCheck());
		publish(new PolygonWrongOrientationCheck());
		publish(new FaceOutCheck());
		publish(new TooFewPolygonsCheck());
		publish(new ManifoldVertexCheck());
		publish(new SolidSelfIntCheck());

		// semantic checks
		publish(new IsWallCheck());
		publish(new IsFloorCheck());
		publish(new IsCeilingCheck());
		publish(new IsGroundCheck());
		publish(new RoofSurfaceUnfragmentedCheck());
	}

	/**
	 * Creates new checks for every available check prototype and stores them in
	 * this container. Access the checks with {@link Checks#getCheckForId(CheckId)}.
	 */
	public Checks() {
		checkMap = new EnumMap<>(CheckId.class);

		for (CheckPrototype proto : checkPrototypes) {
			Check check = proto.createCheck();
			checkMap.put(check.getCheckId(), check);
		}
	}

	private static void publish(Check check) {
		CheckPrototype prototype = new CheckPrototype(check);
		checkPrototypes.add(prototype);
		prototypeMap.put(prototype.getCheckId(), prototype);
	}

	/**
	 * 
	 * @return All available checks as prototypes, where they can be instantiated.
	 */
	public static List<CheckPrototype> getCheckPrototypes() {
		return checkPrototypes;
	}

	/**
	 * 
	 * @param key The check ID
	 * @return The prototype for the given ID or null if there is no such prototype.
	 */
	public static CheckPrototype getCheckPrototypeForId(CheckId key) {
		return prototypeMap.get(key);
	}

	/**
	 * 
	 * @param id The check ID
	 * @return The check for the check ID or null if it does not exist
	 */
	public Check getCheckForId(CheckId id) {
		Check c = checkMap.get(id);
		if (c == null && logger.isWarnEnabled()) {
			logger.warn(Localization.getText("Checks.missingCheck"), id);
		}
		return c;
	}
}
