package net.heartshield.filter;

import net.heartshield.signal.DVec;

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

/**
 * Evenly resamples signal values (t,x) that may be unevenly sampled.
 * Linearly interpolates intermediate values.
 *
 * @author David Madl (git@abanbytes.eu)
 * @date 2017-03-19
 */
public class EveningResampler {
    public EveningResampler(double fps) {
        mFps = fps;
    }

    /**
     * Add uneven signal sample value `frame` at `time`.
     * `time` must be strictly increasing and positive.
     */
    public synchronized void addUneven(double time, double frame) {
        if(time <= mPrevTime)
            throw new IllegalArgumentException("time must be strictly increasing");

        final int curFrame = time2frame(time), prevFrame = time2frame(mPrevTime);
        final double curK = (frame - mPrevFrame) / (time - mPrevTime);
        double k = mPrevK; // first entry must be added with previous slope

        //System.out.println("addUneven(t=" + time + ", f=" + frame + ") prevFrame=" + prevFrame + " curFrame=" + curFrame);
        //System.out.println("dt=" + (time - mPrevTime) + " dy=" + (frame - mPrevFrame));
        for(int i = prevFrame; i < curFrame; i++) {
            double t = (double) i / mFps;
            // floor rounding to curFrame -> t < time
            if(t >= time)
                throw new IllegalStateException("t >= time. this should never happen");

            double f = mPrevFrame + (t - mPrevTime) * k;

            //System.out.println("  i=" + i + " " + "t=" + t + " delt=" + (t - mPrevTime) + " f=" + f);
            mEvenTimes.add(t);
            mEvenFrames.add(f);
            add(t, f);

            k = curK;
        }

        mPrevTime = time;
        mPrevFrame = frame;
        mPrevK = curK;
    }

    private int time2frame(double t) {
        return (int) (mFps * t);
    }

    // note: these would make more sense in a combined call... since they may need to be consistent.
    public synchronized double[] getEvenTimes() {
        return DVec.toArray(mEvenTimes);
    }
    public synchronized double[] getEvenFrames() {
        return DVec.toArray(mEvenFrames);
    }

    /** override this to receive even updates */
    void add(double time, double frame) {}

    public synchronized void clear() {
        mPrevTime = -1e-3;
        mPrevFrame = 0.0;
        mPrevK = 0.0;
    }

    protected double mFps;
    private double mPrevTime = -1e-3;
    private double mPrevFrame = 0.0;
    private double mPrevK = 0.0;

    private List<Double> mEvenTimes = new ArrayList<>();
    private List<Double> mEvenFrames = new ArrayList<>();
}
