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

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

public class PolyLine2d extends BaseEntity {

	private PolyLineSegment2d mpFirst;
	private PolyLineSegment2d mpLast;

	public PolyLine2d(PolyLineSegment2d pStart) {
		mpFirst = mpLast = pStart;
		addChild(mpFirst);

		while (mpLast.getNext() != null) {
			addChild(mpLast.getNext());
			mpLast = mpLast.getNext();
		}
	}

	public PolyLine2d(Coordinate2d pVertex1, Coordinate2d pVertex2) {
		PolyLineSegment2d pSegment = new PolyLineSegment2d(pVertex1, pVertex2);
		mpFirst = mpLast = pSegment;
		addChild(pSegment);
	}

	public List<Coordinate2d> getCoordinates() {
		List<Coordinate2d> coordinates = new ArrayList<>();
		if (mpFirst != null) {
			PolyLineSegment2d pSegment = mpFirst;
			coordinates.add(pSegment.getStart());
			while (pSegment != null) {
				coordinates.add(pSegment.getEnd());

				pSegment = pSegment.getNext();
			}
		}
		return coordinates;
	}

	public PolyLineSegment2d getFirstSegment() {
		return mpFirst;
	}

	public PolyLineSegment2d getLastSegment() {
		return mpLast;
	}

	public Coordinate2d getStart() {
		return mpFirst.getStart();
	}

	public Coordinate2d getEnd() {
		return mpLast.getEnd();
	}

	public List<PolyLineSegment2d> getSegments() {
		List<PolyLineSegment2d> segments = new ArrayList<>();

		PolyLineSegment2d pSegment = mpFirst;
		while (pSegment != null) {
			segments.add(pSegment);
			pSegment = pSegment.getNext();
		}

		return segments;
	}

	public PolyLine2d append(PolyLine2d pSecondLine) {
		// append the end-vertices to the current line
		PolyLineSegment2d pSecondStart = pSecondLine.getFirstSegment();
		while (pSecondStart != null) {
			append(pSecondStart.getEnd());
			pSecondStart = pSecondStart.getNext();
		}
		return pSecondLine;
	}

	private Coordinate2d append(Coordinate2d pVertex) {
		// check if the polyline already contains segments
		// -----------------------------------------------

		PolyLineSegment2d pNewSegment = new PolyLineSegment2d(getEnd(), pVertex);
		addChild(pNewSegment);

		mpLast.setNext(pNewSegment);
		mpLast = pNewSegment;

		return pVertex;
	}

	/**
	 * Split a polyline at a vertex that is already part of the polyline. The
	 * original PolyLine will be split, and the right fragment will be returned. The
	 * original PolyLine will hold the left fragment
	 * 
	 * @param pVertex
	 * @return
	 */
	public PolyLine2d split(Coordinate2d pVertex) {
		PolyLineSegment2d seg = mpFirst;
		PolyLineSegment2d leftLast = null;
		PolyLineSegment2d rightFirst = null;

		for (; seg != null; seg = seg.getNext()) {
			if (seg.getEnd() == pVertex) {
				leftLast = seg;
				rightFirst = seg.getNext();
				break;
			}
		}

		if (leftLast == null) {
			throw new IllegalArgumentException("vertex is not part of polyline");
		}

		List<PolyLineSegment2d> segmentsToRemove = new ArrayList<>();

		PolyLineSegment2d pToRemove = rightFirst;
		while (pToRemove != null) {
			segmentsToRemove.add(pToRemove);
			pToRemove = pToRemove.getNext();
		}

		// terminate this polyline
		// -----------------------

		leftLast.setNext(null);

		mpLast = leftLast;

		PolyLine2d pRightLine = new PolyLine2d(rightFirst);

		// De-refernence all unused segments from this line
		// ------------------------------------------------

		for (PolyLineSegment2d itTR : segmentsToRemove) {
			removeChild(itTR);
		}

		return pRightLine;
	}

}
