package net.heartshield.data;

import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.util.Log;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import junit.framework.TestCase;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Before;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;


public class RemoteClassifierTest extends TestCase {
    private static final String TAG = "RemoteClassifierTest";

    private static final String VERSION = "0.5.0";
    private static final String CODENAME = "adha";

    RequestQueue mRequestQueue;
    List<List<Double>> mTRGBSeries;

    @Before
    public void setUp() throws Exception {
        Context appContext = InstrumentationRegistry.getTargetContext();
        mRequestQueue = Volley.newRequestQueue(appContext);

        mTRGBSeries = new ArrayList<>();
    }

    private JSONObject getAndroidVersions() throws JSONException {
        JSONObject ver = new JSONObject();
        ver.put("os.version", System.getProperty("os.version"));
        ver.put("Build.VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
        ver.put("Build.VERSION.SDK_INT", Build.VERSION.SDK_INT); // API level
        ver.put("Build.DEVICE", Build.DEVICE);
        ver.put("Build.MODEL", Build.MODEL);
        ver.put("Build.PRODUCT", Build.PRODUCT);
        return ver;
    }

    private JSONObject getAppInfo() throws JSONException {
        JSONObject appInfo = new JSONObject();
        appInfo.put("version", VERSION);
        appInfo.put("codename", CODENAME);
        appInfo.put("install_android_versions", getAndroidVersions());

        // TODO(david) strong persistence (kept between reinstalls) below
        appInfo.put("id", "01234567"); // TODO random
        // 0123456789ABCDEF
        appInfo.put("first_install_date", System.currentTimeMillis() / 1e3 - 80.0);

        // TODO(david) persistence below
        appInfo.put("install_date", System.currentTimeMillis() / 1e3 - 80.0);

        return appInfo;
    }

    private JSONObject getRequestData() throws JSONException {
        JSONObject requestData = new JSONObject();

        //double[] ts = mPlotFilter.getEvenTimes();
        //double meanFps = ((double) ts.length - 1) / (ts[ts.length-1] - ts[0]);

        double FPS = 30.0;
        double meanFps = FPS; // TODO(david)

        ////

        double mStartTime = ((double) System.currentTimeMillis()) / 1000;

        double dt = 1.0 / FPS;
        double duration = 75.0;
        double f = 1.0;
        double end = mStartTime + duration;

        mTRGBSeries.clear();
        for(double t = mStartTime; t < end; t += dt) {
            double val = Math.sin(2*Math.PI*f*t);

            // put sample
            List<Double> trgbEntry = new ArrayList<>();
            trgbEntry.add(t);
            double[] rgb = new double[]{200.0 + val, 0.0, 0.0};
            for (int i = 0; i < rgb.length; i++)
                trgbEntry.add(rgb[i]);
            mTRGBSeries.add(trgbEntry);
        }

        ////

        JSONObject metaData = new JSONObject();
        metaData.put("start_time", mStartTime);
        metaData.put("ppg_mean_fps", meanFps);
        metaData.put("app_info", getAppInfo());
        // TODO(david): camera_param_summary

        JSONObject seriesData = new JSONObject();

        JSONArray ppgData = new JSONArray(mTRGBSeries);
        seriesData.put("ppg_data", ppgData);

        //requestData.put("meta_data", metaData); // TODO(david)

        JSONObject meta2 = new JSONObject(DemoData.meta_data);
        meta2.put("start_time", mStartTime);

        requestData.put("meta_data", metaData);
        requestData.put("series_data", seriesData);

        return requestData;
    }

    private class TestResults {
        Exception error = null;
    }

    private void onMeasurementFinished(final CountDownLatch signal, final TestResults results) {
        //String url = "http://my-json-feed";
        //String url = "https://mlapi.heartshield.net/v2/rawrfclassify2";
        String url = "http://192.168.40.246:8000/v3/measurement";


        Log.i(TAG, "onMeasurementFinished() - building request.");

        //final Activity activity = this;
        final Context appContext = InstrumentationRegistry.getTargetContext();

        JSONObject requestData;
        try {
            requestData = getRequestData();
        } catch(JSONException e) {
            throw new RuntimeException(e);
        }

        Log.i(TAG, "onMeasurementFinished() - sending measurement...");
        Log.i(TAG, "onMeasurementFinished() requestData=" + requestData.toString());

        // TODO(david): request timeout
        JsonObjectRequest request = new JsonObjectRequest
                (Request.Method.POST, url, requestData, new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        String pred;
                        Log.i(TAG, "Response: " + response.toString());
                        try {
                            pred = response.get("pred").toString();
                        } catch(JSONException e) {
                            pred = "JSONException: " + e.toString();
                            results.error = e;
                        }
                        Log.i(TAG, "pred: " + pred);
                        signal.countDown();
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        // TODO(david): handle properly
                        Log.e(TAG, "HTTP request failed", error);
                        Toast.makeText(appContext, "HTTP request failed", Toast.LENGTH_LONG).show();
                        results.error = error;
                        signal.countDown();
                    }
                });
        mRequestQueue.add(request);
    }

    public void testRemoteClassifier() {
        Log.i(TAG, "running onMeasurementFinished()...");
        final CountDownLatch signal = new CountDownLatch(1);

        final TestResults results = new TestResults();

        onMeasurementFinished(signal, results);

        try {
            signal.await(30, TimeUnit.SECONDS); // wait for callback
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        if(results.error != null)
            throw new RuntimeException(results.error);
    }
}
