package com.paris.neuromorphic;

import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.HandlerThread;
import android.util.Log;

import com.chronocam.libatis.Atis;
import com.chronocam.libatis.AtisBiases;
import com.chronocam.libatis.AtisInstance;
import com.example.chronocam.atis.IS_Usb;
import com.example.chronocam.atis.USB_Android;

import java.text.DecimalFormat;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * Created by gregorlenz on 26/12/17.
 */

public class CameraPollingThread extends HandlerThread {
    private final String TAG = getClass().getSimpleName();
    private Intent intent;
    private UsbManager usbManager;

    private volatile boolean isCameraAttached = true;

    //Counters
    private long maxCount, iterationCount;

    private final ArrayBlockingQueue<ToExchange> buffer;

    //Timestamps
    private long startTimeStamp, endTimestamp, endTransferTimestamp, lastTimeStamp;

    static final int PACKET_SIZE = 16 * 1024; //until Android 9, everything gets truncated to 16k
    private static final int TIMEOUT = 100;
    DecimalFormat df = new DecimalFormat("####.##");

    CameraPollingThread(Intent intent, UsbManager usbManager, ArrayBlockingQueue<ToExchange> blockingQueue) {
        super(CameraPollingThread.class.getName());
        this.intent = intent;
        this.usbManager = usbManager;
        this.buffer = blockingQueue;
    }

    @Override
    protected void onLooperPrepared() {
        Log.d(TAG, "Looper prepared");
        if (intent != null) {
            pollCamera();
        }
    }

    @Override
    public void run() {
        Log.d(TAG, "inside run method. ");
        super.run();
    }

    void setCameraAttached(boolean flag) {
        isCameraAttached = flag;
    }

    private void pollCamera() {
        if (intent.hasExtra("usbDevice")) {
            UsbDevice usbDevice = intent.getParcelableExtra("usbDevice");
            String biasFilePath = intent.getStringExtra("filePath");

            UsbInterface intf = usbDevice.getInterface(0);
            UsbDeviceConnection connection = usbManager.openDevice(usbDevice);
            connection.claimInterface(intf, true);
            UsbEndpoint endpoint = null;
            if (intf.getEndpointCount() > 0)
                endpoint = intf.getEndpoint(0);

            final USB_Android usb_android = new USB_Android(connection, usbDevice, endpoint);

            IS_Usb.set_usb(usb_android);
            IS_Usb.set_JNIEnv();

            AtisBiases biases = AtisBiases.from_file(biasFilePath);

            if (biases != null) {
                Atis atis = new Atis();

                Log.d(TAG, "open cam...");
                Log.d(TAG, IS_Usb.getStringCallback());

                AtisInstance camera = atis.open_atis_auto("", "");

                Log.d(TAG, "set biases...");
                camera.set_biases(biases);

                Log.d(TAG, "set couple...");
                camera.set_couple(false);

                Log.d(TAG, "start cam...");
                camera.start();
            }

            while (isCameraAttached) {
                if (buffer.remainingCapacity() != 0) {
                    try {
                        ToExchange toExchange = new ToExchange();
                        startTimeStamp = System.nanoTime();
                        toExchange.size = usb_android.bulkTransfer(0x81, toExchange.data, PACKET_SIZE, TIMEOUT);
                        endTransferTimestamp = System.nanoTime();

                        if (toExchange.size > 0) {
                            if (toExchange.size >= PACKET_SIZE) {
                                maxCount++;
                                Log.i(TAG, "hit maximum package size " + maxCount + " times.");
                            }
                            iterationCount++;
                            buffer.put(toExchange);

                            endTimestamp = System.nanoTime();
                            Log.d(TAG, "Producer: Iteration " + iterationCount + " took "
                                    + df.format((endTimestamp - startTimeStamp) / 1000000f)
                                    + "ms, bulkTransfer of size " + toExchange.size + " took "
                                    + df.format((endTransferTimestamp - startTimeStamp) / 1000000f)
                                    + "ms, it's been "
                                    + df.format((startTimeStamp - lastTimeStamp) / 1000000f)
                                    + "ms since last call.");

                            lastTimeStamp = endTimestamp;
                        } else {
                            Log.d(TAG, "Hit max package size " + maxCount + " times.");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    try {
                        Thread.sleep(20);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            Log.d(TAG, "ended cameraIsAttached while loop");
        }
    }

    @Override
    public boolean quit() {
        return super.quit();
    }
}
