/*
 * Copyright (C) 2006 Steve Ratcliffe
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 * 
 *  This program 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 General Public License for more details.
 * 
 * 
 * Author: Steve Ratcliffe
 * Create date: 18-Dec-2006
 */
package uk.me.parabola.mkgmap.general;

import java.awt.Rectangle;
import java.util.List;

import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.log.Logger;

/**
 * Represent a line on a Garmin map.  Lines are a list of points.  They have
 * a type (major highway, stream etc) and a name.  And that is just about it.
 *
 * @author Steve Ratcliffe
 */
public class MapLine extends MapElement {
	private static final Logger log = Logger.getLogger(MapLine.class);

	private List<Coord> points;
	private boolean direction; // set if direction is important.
	private boolean skipSizeFilter;
	private boolean wasClipped;
	private int minLat = Integer.MAX_VALUE;
	private int minLong = Integer.MAX_VALUE;
	private int maxLat = Integer.MIN_VALUE;
	private int maxLong = Integer.MIN_VALUE;

	public MapLine() {
	}

	public MapLine(MapLine orig) {
		super(orig);
		direction = orig.direction;
		skipSizeFilter = orig.skipSizeFilter;
		//roadDef = orig.roadDef;
	}

	public MapLine copy() {
		return new MapLine(this);
	}

	public List<Coord> getPoints() {
		return points;
	}

	public void setPoints(List<Coord> points) {
		if (this.points != null)
			log.warn("overwriting points");
		assert points != null : "trying to set null points";
		assert !points.isEmpty() : "trying to set points with zero length";

		this.points = points;
		// preserve first and last point, so that points which are shared by
		// different ways are kept
		if (points.size() > 0 && this instanceof MapShape == false){
			points.get(0).preserved(true);
			points.get(points.size()-1).preserved(true);
		}
		testForConsecutivePoints(points);
	}
	
	public void testForConsecutivePoints(List<Coord> points) {
		Coord last = null;
		for (Coord co : points) {
			if (last != null && last.equals(co)) {
				if (log.isInfoEnabled())
					log.info("Line", getName() , "has consecutive equal points at" , co.toDegreeString());
			} else {
				addToBounds(co);
				last = co;
			}
		}
	}

	public void insertPointsAtStart(List<Coord> additionalPoints) {
		assert points.get(0).equals(additionalPoints.get(additionalPoints.size()-1));
		testForConsecutivePoints(additionalPoints);
		points.addAll(0, additionalPoints.subList(0, additionalPoints.size()-1));
	}

	public void insertPointsAtEnd(List<Coord> additionalPoints) {
		testForConsecutivePoints(additionalPoints);
		points.remove(points.size()-1); 
		points.addAll(additionalPoints);
	}

	public boolean isDirection() {
		return direction;
	}

	public void setDirection(boolean direction) {
		this.direction = direction;
	}

	public boolean isRoad() {
		return false;
	}

	public boolean isSkipSizeFilter() {
		return skipSizeFilter;
	}

	public void setSkipSizeFilter(boolean skipSizeFilter) {
		this.skipSizeFilter = skipSizeFilter;
	}


	public boolean wasClipped() {
		return wasClipped;
	}

	public void setClipped(boolean wasClipped) {
		this.wasClipped = wasClipped;
	}

	/**
	 * Get the mid-point of the bounding box for this element.  This is as good
	 * an indication of 'where the element is' as any.  Previously we just
	 * used the location of the first point which would lead to biases in
	 * allocating elements to subdivisions.
	 *
	 * @return The mid-point of the bounding box.
	 */
	public Coord getLocation() {
		return new Coord((minLat + maxLat) / 2, (minLong + maxLong) / 2);// high prec not needed
	}

	/**
	 * We build up the bounding box of this element by calling this routine.
	 *
	 * @param co The coordinate to add.
	 */
	private void addToBounds(Coord co) {
		int lat = co.getLatitude();
		if (lat < minLat)
			minLat = lat;
		if (lat > maxLat)
			maxLat = lat;

		int lon = co.getLongitude();
		if (lon < minLong)
			minLong = lon;
		if (lon > maxLong)
			maxLong = lon;
	}

	/**
	 * Get the region that this element covers.
	 *
	 * @return The area that bounds this element.
	 */
	public Area getBounds() {
		return new Area(minLat, minLong, maxLat, maxLong);
	}

	/**
	 * @return bounding box that can have 0 height or width
	 */
	public Rectangle getRect(){
		return new Rectangle(minLong, minLat, maxLong-minLong, maxLat-minLat);
	}
}
