package net.heartshield.filter;

import net.heartshield.signal.DVec;

/**
 * Window-based almost-realtime processing of a signal.
 *
 * @author David Madl (git@abanbytes.eu)
 * @date 2017-05-16
 */
public class WindowedBatchFilter extends RFilterBlock {
    protected int mDelay;
    protected double[] mBuf;

    protected int N; // left samples
    protected int M; // modulus
    protected double mPrevVal = 0.0;

    /**
     * @param win window size in samples
     * @param initial initialization value, pretended to be to the left of the first input batch
     */
    public WindowedBatchFilter(int win, double initial) {
        if(win <= 0)
            throw new IllegalArgumentException("win must be >= 1");

        mDelay = win;
        mBuf = DVec.mul(DVec.ones(win), initial);

        M = win;
        N = M;
    }

    protected double processWindow(double[] buf, int start) {
        // simple downsampler
        return buf[start + mDelay];
    }

    @Override
    protected double[] batch(double[] x) {
        /*
         * N  left samples  1 <= N <= M
         * L  input length  0 < L
         * M  modulus       1 <= M
         *
         *      N      ||        L
         * +---+---+---++---+---+---+---+---+
         * |   |   |   ||   |   |   |   |   |
         * +---+---+---++---+---+---+---+---+
         */

        int L = x.length;

        double[] buf = DVec.stack(mBuf, x);

        // too short to process even a single window
        if(N + L <= M) {
            N += L;
            mBuf = DVec.get(buf, buf.length - mDelay, buf.length);
            return DVec.mul(DVec.ones(L), mPrevVal);
        }

        int start = M - N;
        //int outLen = (N + L - 1) / M;

        //double[] ret = new double[outLen];
        double[] ret = new double[x.length];

        //double val = processWindow(buf, start);
        for(int j = 0; j < start; j++)
            ret[j] = mPrevVal;

        // processWindow() result of the whole window BEFORE is used for the next window.
        // i.e. [x_0 x_1 x_2 x_3]  with M=2 becomes
        //      [0.0 0.0 y01 y01]
        //
        // add [x_4] ->         [y23]
        // add [x_5] ->             [y23]
        //
        // where yij = processWindow([x_i x_j])

        double val = mPrevVal;
        for(int i = start; i < L; i += M) {
            val = processWindow(buf, i);

            int end = Math.min(i + M, L);
            for(int k = i; k < end; k++)
                ret[k] = val;
        }
        mPrevVal = val;

        N = (N + L - 1) % M + 1;

        mBuf = DVec.get(buf, buf.length - mDelay, buf.length);
        return ret;
    }
}
