package net.heartshield.sensors;

import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.view.TextureView;

import net.heartshield.signal.DVec;

import java.util.List;

/**
 * Camera brightness time series signal capture.
 *
 * Events are delivered to the main thread, but pixel handling occurs in a separate thread.
 *
 * @author David Madl (git@abanbytes.eu)
 * @date 2016-03-21
 */
public class IntensitySensor implements IntensityDetector.IntensityListener, ISensor {
    private IntensityDetector mDetector;
    private Handler mHandler;
    private Double mFps;
    private SensorData mData;
    private ISensor.Listener mListener = null;

    private static final int NDIM = 3; // r,g,b channels

    public IntensitySensor(Activity activity, TextureView cameraPreview, int width, int height) {
        mDetector = new Camera2Detector(activity, cameraPreview, width, height, /* stickyUiThread */ false);
        mDetector.setIntensityListener(this);
        mHandler = new Handler(Looper.getMainLooper());
        mData = new SensorData(NDIM);
    }

    private void emitOnState(final ISensor.State newState) {
        if(mListener == null)
            return;
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mListener.onState(newState);
            }
        });
    }

    @Override
    public void onState(IntensityDetector.State newState) {
        switch(newState) {
            case STOPPED:
                emitOnState(State.STOPPED);
                break;
            case WAITING:
                mFps = (double) mDetector.getFps();
                emitOnState(State.RUNNING);
                break;
            case LOCKED:
                break;
            case FAILED:
                emitOnState(State.FAILED);
                break;
            default:
                throw new IllegalStateException();
        }
    }

    @Override
    public void onFrame(long timestamp, long cpuTimeNanos, double[] rgb, IntensityDetector.ImageSummary summary) {

        final int offset = mData.length();
        mData.addSample(DVec.stack(new double[]{ ((double) timestamp) / 1e9 }, rgb));

        if(mListener != null) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mListener.onData(offset, 1);
                }
            });
        }
    }

    /////

    /**
     * Nominal number of sample values per second.
     * Only valid after the first call to {@link ISensor#start}
     */
    @Override
    public double getFps() { return mFps; }

    /** start measurement, resetting sensor data */
    @Override
    public void start() {
        mData.clear();
        mDetector.start();
    }

    /** stop measurement, keeping sensor data */
    @Override
    public void stop() {
        mDetector.stop();
    }

    /**
     * Time series of each dimension: e.g. {{t1, t2, ...}, {x1, x2, ...}}
     * Deep copies are returned. Time is seconds in {@link System#nanoTime} timebase.
     *
     * @return sensor data recorded since {@link ISensor#start} was called
     */
    @Override
    public List<double[]> getSamples() { return mData.getSamples(); }

    /** @return number of samples */
    @Override
    public int length() { return mData.length(); }

    /** events are delivered to the main thread, but pixel handling occurs in a separate thread */
    @Override
    public void setListener(ISensor.Listener listener) { mListener = listener; }

    /** obtain details after FAILED state */
    @Override
    public Exception getError() {
        // TODO(david) implement me
        return null;
    }
}
