package net.heartshield.sensors;

import net.heartshield.data.Measurement;
import net.heartshield.signal.DVec;

import java.util.List;
import java.util.Map;

/**
 * @author David Madl (git@abanbytes.eu)
 * @date 2017-04-12
 */
public class DummyIntensityDetector implements IntensityDetector {
    Measurement mMeasurement;
    IntensityListener mListener;
    State mState = State.STOPPED;
    int mI;
    long mTimeNanos;
    long mStartTimeNanos;
    long mDt;
    private static final double FPS = 30.0;

    public DummyIntensityDetector(Measurement measurement) {
        mMeasurement = measurement;
        mDt = (long) (1e9 / FPS);
    }

    class DummyIntensityThread extends Thread {
        @Override
        public void run() {
            // ...
        }
    }

    @Override
    public void start() throws IllegalStateException {
        // TODO: mock state machine logic
        // mock state machine logic
        // we speed up testing by avoiding Thread.sleep() - passed timestamps still increase in the intended 33 ms spacing

        if(mState != State.STOPPED)
            throw new IllegalStateException("start() called in wrong state " + mState);

        // STOPPED -> WAITING -> LOCKED -> STOPPED

        setState(State.WAITING);
        mTimeNanos = System.nanoTime();
        mStartTimeNanos = mTimeNanos;

        for(mI = 0; mState == State.WAITING && mI < mMeasurement.series.ppgData.size(); mI++) {
            double[] trgb = mMeasurement.series.ppgData.get(mI);
            double[] rgb = DVec.get(trgb, 1, 4);
            mListener.onFrame(mTimeNanos, rgb, dummyImageSummary(rgb));
            mTimeNanos += mDt;
        }

        if(mI == mMeasurement.series.ppgData.size())
            throw new IllegalStateException("MeasurementController never called lock() before all samples were exhausted in start()");

        if(mState != State.LOCKED)
            throw new IllegalStateException("should be in LOCKED state here");
    }

    private static ImageSummary dummyImageSummary(double[] rgb) {
        ImageSummary summary = new ImageSummary();
        final int fakeRes = 1;

        summary.meanCol = new double[3][fakeRes];
        summary.meanRow = new double[3][fakeRes];
        summary.stdCol  = new double[3][fakeRes];
        summary.stdRow  = new double[3][fakeRes];

        for(int ch = 0; ch < 3; ch++) {
            summary.meanCol[ch][0] = rgb[ch];
            summary.meanRow[ch][0] = rgb[ch];
            summary.stdCol[ch][0] = 0.0;
            summary.stdRow[ch][0] = 0.0;
        }

        return summary;
    }

    @Override
    public void stop() {
        // check if FSM has gone through all transitions
        // (there is no other way to have arrived at LOCKED)

        // assuming measurement finishes in LOCKED state, should be true for almost all replays
        if(mState != State.LOCKED)
            throw new IllegalStateException("stop() called in wrong state " + mState);
        setState(State.STOPPED);
    }

    @Override
    public void lock() throws IllegalStateException {
        if(mState != State.WAITING)
            throw new IllegalStateException("lock() called in wrong state " + mState);

        setState(State.LOCKED);

        for(mI = 0; (mState == State.LOCKED || mState == State.WAITING) && mI < mMeasurement.series.ppgData.size(); mI++) {
            double[] rgb = mMeasurement.series.ppgData.get(mI);
            mListener.onFrame(mTimeNanos, rgb, dummyImageSummary(rgb));
            mTimeNanos += mDt;
        }

        // true unless in very rare cases where user took their finger off right at the last moment
        if(mState != State.LOCKED)
            throw new IllegalStateException("should be in LOCKED state here after finishing replay of PPG measurement");
    }

    @Override
    public void unlock() throws IllegalStateException {
        if(mState != State.LOCKED)
            throw new IllegalStateException("unlock() called in wrong state " + mState);
        setState(State.WAITING);
    }

    @Override
    public State getState() {
        return mState;
    }

    @Override
    public int getDroppedCount() {
        return 0;
    }

    @Override
    public CameraFeatures getFeatures() {
        return null;
    }

    @Override
    public int getFps() {
        return 30;
    }

    @Override
    public List<double[]> getSamples() {
        // subtract static timestamps, and add current start (will be subtracted by MeasurementController)
        double dt = mMeasurement.series.ppgData.get(0)[0] - mStartTimeNanos / 1e9;
        return SensorData.transposeSamples(SensorData.transposeSamples(SensorData.transposeSamples(mMeasurement.series.ppgData), dt));
    }

    @Override
    public void setIntensityListener(IntensityListener listener) {
        mListener = listener;
    }

    @Override
    public void setDebugMode(boolean debugMode) {

    }

    private void setState(State newState) {
        //Log.i(TAG, "setState(" + newState.toString() + ")");
        if(mListener == null)
            throw new IllegalStateException("must use a listener for mock testing");

        State prevState = mState;
        mState = newState;
        if(prevState != newState)
            mListener.onState(newState);
    }
}
