// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.opendata.core.layers;

import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Action;
import javax.swing.Icon;

import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.plugins.opendata.core.OdConstants;
import org.openstreetmap.josm.tools.ImageProvider;
import org.openstreetmap.josm.tools.Pair;

public class OdDiffLayer extends Layer implements OdLayer {

	private final OdDataLayer dataLayer;
	
	public final List<Pair<OsmPrimitive,OsmPrimitive>> differentPrimitives;
	public final List<OsmPrimitive> onlyInTlsPrimitives;
	public final List<OsmPrimitive> onlyInOsmPrimitives;
	
	public OdDiffLayer(OdDataLayer dataLayer, String name) {
		super(name);
		this.dataLayer = dataLayer;
		this.differentPrimitives = new ArrayList<>();
		this.onlyInTlsPrimitives = new ArrayList<>();
		this.onlyInOsmPrimitives = new ArrayList<>();
		initDiff(dataLayer.data, dataLayer.osmLayer.data);
	}
	
	private void initDiff(DataSet tlsData, DataSet osmData) {
		for (OsmPrimitive p1 : tlsData.allPrimitives()) {
			if (dataLayer.handler.isRelevant(p1)) {
				OsmPrimitive p2 = findPrimitiveAt(osmData, p1);
				if (p2 == null) {
					onlyInTlsPrimitives.add(p1);
				} else if (!dataLayer.handler.equals(p1, p2)) {
					differentPrimitives.add(new Pair<>(p1, p2));
				}
			}
		}
		for (OsmPrimitive p1 : osmData.allPrimitives()) {
			if (dataLayer.handler.isRelevant(p1)) {
				if (findPrimitiveAt(tlsData, p1) == null) {
					onlyInOsmPrimitives.add(p1);
				}
			}
		}
	}
	
	private double distance(OsmPrimitive p1, OsmPrimitive p2) {
		return p1.getBBox().getCenter().greatCircleDistance(p2.getBBox().getCenter());
	}
	
	private OsmPrimitive findPrimitiveAt(DataSet dataSet, OsmPrimitive source) {
		double maxDistance = Main.pref.getDouble(OdConstants.PREF_MAXDISTANCE, OdConstants.DEFAULT_MAXDISTANCE);
		//List<OsmPrimitive> samePrimitives = new ArrayList<OsmPrimitive>();
		OsmPrimitive nearestSamePrimitive = null;
		//List<OsmPrimitive> potentialPrimitives = new ArrayList<OsmPrimitive>();
		OsmPrimitive nearestPotentialPrimitive = null;
		for (OsmPrimitive p : dataSet.allPrimitives()) {
			if (dataLayer.handler.isRelevant(p)) {
				double dist = distance(source, p); 
				if (dist <= maxDistance) {
					if (dataLayer.handler.equals(p, source)) {
						//samePrimitives.add(p);
						if (nearestSamePrimitive == null || distance(p, nearestSamePrimitive) > dist) {
							nearestSamePrimitive = p;
						}
					} else {
						//potentialPrimitives.add(p);
						if (nearestPotentialPrimitive == null || distance(p, nearestPotentialPrimitive) > dist) {
							nearestPotentialPrimitive = p;
						}
					}
				}
			}
		}
		return nearestSamePrimitive != null ? nearestSamePrimitive : nearestPotentialPrimitive;
	}

	@Override
	public void paint(Graphics2D g, MapView mv, Bounds box) {
		// TODO Auto-generated method stub
	}

	@Override
	public Icon getIcon() {
		return ImageProvider.get("layer", "diff");
	}

	@Override
	public String getToolTipText() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void mergeFrom(Layer from) {
	}

	@Override
	public boolean isMergable(Layer other) {
		return false;
	}

	@Override
	public void visitBoundingBox(BoundingXYVisitor v) {
		// TODO Auto-generated method stub
	}

	@Override
	public Object getInfoComponent() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Action[] getMenuEntries() {
		return null;
	}

	@Override
	public OdDataLayer getDataLayer() {
		return dataLayer;
	}
}
