package org.spview.peakfinding;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;

public class DigitalFilter {
	public static boolean debug = false;

	public static ArrayList<Point2D> findPeaks(double[] x, double[] y, double pt, double cutoff, int filterWidth) {
		int nPeaks = 0;

		pt = (pt >= 0 ? 1 : -1);

		/* set a single-cycle square of width filterWidth */
		double[] filter = new double[filterWidth];

		Arrays.fill(filter, 1.0);
		Arrays.fill(filter, (int) Math.ceil(filterWidth / 2.0), filterWidth, -1.0);

		if (Math.ceil(filterWidth / 2.0) > (filterWidth / 2.0))
			filter[(int) Math.floor(filterWidth / 2.0)] = 0.0;

		int offset = (int) Math.ceil((filterWidth - 1) / 2.0);

		double[] yConv = convolve(y, filter);

		for (int index = offset; index < yConv.length - 1 && index < y.length + offset; index += 1) {
			if (yConv[index] * yConv[index + 1] < 0) {
				if ((pt * y[index - offset] >= pt * cutoff) && ((yConv[index + 1] - yConv[index]) * pt < 0)) {
					nPeaks++;
				}
			}
		}

		if (nPeaks == 0) {
			System.out.println("Screwup - no peaks were located");
			return null;
		}

		ArrayList<Point2D> peakList = new ArrayList<>();
		nPeaks = 0;

		for (int index = offset; index < yConv.length - 1 && index < y.length + offset; index += 1) {
			if (yConv[index] * yConv[index + 1] < 0) {
				if ((pt * y[index - offset] >= pt * cutoff) && ((yConv[index + 1] - yConv[index]) * pt < 0)) {
					// Interpolate the peak position
					int peak = (int) Math.abs(yConv[index] / (yConv[index] - yConv[index + 1])) + index - offset;

					Point2D XY = new Point2D.Double(x[peak], y[peak]);
					if (debug)
						System.out.println(XY);
					peakList.add(XY);
					nPeaks += 1;
				}
			}
		}
		return peakList;
	}

	private static double[] convolve(double[] x, double[] h) {
		final int xLen = x.length;
		final int hLen = h.length;

		// initialize the output array
		final int totalLength = xLen + hLen - 1;
		final double[] y = new double[totalLength];

		// straightforward implementation of the convolution sum
		for (int n = 0; n < totalLength; n++) {
			double yn = 0;
			int k = Math.max(0, n + 1 - xLen);
			int j = n - k;
			while (k < hLen && j >= 0) {
				yn += x[j--] * h[k++];
			}
			y[n] = yn;
		}

		return y;
	}
}
