
using System;
using System.Collections;


public class
CoverageFilter
{
	/* ms: the two-picture information (needed for FMM dimensions).
	 * points: holds FilterPoint elements.
	 * pruneTarget: the number of points we should prune to.
	 */
	public CoverageFilter (MultiMatch.MatchSet ms, ArrayList points, int pruneTarget)
	{
		this.ms = ms;
		this.source = points;
		this.pruneTarget = pruneTarget;
	}

	private MultiMatch.MatchSet ms;
	private ArrayList source;
	private int pruneTarget;

	private const int maximumFMMSize = 50;

	private FMM CreateFMMFromPoints ()
	{
		return (CreateFMMFromPoints (null));
	}

	private FMM CreateFMMFromPoints (bool[] useMask)
	{
		/* Create a list of selected points.
		 */
		ArrayList selected = new ArrayList ();
		for (int n = 0 ; n < source.Count ; ++n)
			if (useMask == null || useMask[n])
				selected.Add (source[n]);

		/* Find the FMM dimensions: largest image dimension reduced to scale
		 * factor.
		 */
		double maxDim = ms.xDim1 > ms.yDim1 ? ms.xDim1 : ms.yDim1;
		double scaleFactor = ((double) maximumFMMSize) / maxDim;
		if (scaleFactor > 1.0)
			scaleFactor = 1.0;

		/* Create the FMM map in the given dimensions.
		 */
		FMM fmm = new FMM ((int) (ms.xDim1 * scaleFactor + 0.5),
			(int) (ms.yDim1 * scaleFactor + 0.5));

		/* Now add the selected points, downscaled.  Exact distance to
		 * initialize surrounding points.
		 */
		foreach (FilterPoint point in selected) {
			double x = point.x * scaleFactor;
			double y = point.y * scaleFactor;

			/* Find the four discrete corner points surrounding the exact
			 * point.
			 */
			int x_low = (int) x;
			int x_high = x_low + 1;
			int y_low = (int) y;
			int y_high = y_low + 1;

			fmm.AddKnown (x_low, y_low, DistanceTo (x_low, y_low, x, y));

			if (x_high < fmm.xdim)
				fmm.AddKnown (x_high, y_low, DistanceTo (x_high, y_low, x, y));
			if (y_high < fmm.ydim)
				fmm.AddKnown (x_low, y_high, DistanceTo (x_low, y_high, x, y));
			if (x_high < fmm.xdim && y_high < fmm.ydim)
				fmm.AddKnown (x_high, y_high, DistanceTo (x_high, y_high, x, y));
		}

		return (fmm);
	}

	/* Compute the score of each distance map when removing one point for each
	 * point.  The resulting array holds the score when exactly the point at
	 * that position is removed from 'source'.
	 */
	public double[] GetScore ()
	{
		double[] results = new double[source.Count];
		bool[] useMask = new bool[source.Count];

		for (int n = 0 ; n < source.Count ; ++n) {
			for (int k = 0 ; k < source.Count ; ++k)
				useMask[k] = (k != n) ? true : false;

			FMM fmm = CreateFMMFromPoints (useMask);
			fmm.InitializeBand ();
			fmm.DoIterations (0, 0.0);
			results[n] = fmm.CalculateTotalSum ();
		}

		for (int n = 0 ; n < source.Count ; ++n) {
			Console.WriteLine ("Score when removing point {0}: {1}", n,
				results[n]);
		}

		return (results);
	}

	public ArrayList PruneOne ()
	{
		double[] scores = GetScore ();
		int minIndex = -1;
		double minScore = Double.MaxValue;

		/* Find maximum efficient prune point (has minimum score)
		 */
		for (int n = 0 ; n < scores.Length ; ++n) {
			if (minIndex < 0 || scores[n] < minScore) {
				minIndex = n;
				minScore = scores[n];
			}
		}

		/* And remove it.
		 */
		ArrayList target = (ArrayList) source.Clone ();
		target.RemoveAt (minIndex);

		return (target);
	}

	private double DistanceTo (double x1, double y1, double x2, double y2)
	{
		return (Math.Sqrt (Math.Pow (x1 - x2, 2.0) + Math.Pow (y1 - y2, 2.0)));
	}
}

