package com.paris.neuromorphic;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.concurrent.ArrayBlockingQueue;

import androidx.annotation.Nullable;

/**
 * Created by gregorlenz on 31/10/18.
 */

public class CameraService extends Service {
    private final String TAG = getClass().getName();
    private final IBinder iBinder = new LocalBinder();

    CameraPollingThread cameraPollingThread;
    ProcessingThread processingThread;
    Looper cameraPollingThreadLooper, processingThreadLooper;

    private Intent intent;

    ArrayBlockingQueue<ToExchange> buffer;

    @Override
    public void onCreate() {
    }

    //using a service to make ATIS polling activity-independent
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "Camera service starting...");

        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent =
                PendingIntent.getActivity(this, 0, notificationIntent, 0);

        //sticky notification to be able to run service in foreground
        Notification notification =
                new Notification.Builder(this)
                        .setContentTitle("ATIS camera connected")
                        .setContentText("reading events...")
                        .setSmallIcon(R.mipmap.camera_dt)
                        .setContentIntent(pendingIntent)
                        .setTicker("Camera is being polled...")
                        .build();

        startForeground(182903, notification);

        this.intent = intent;
        buffer = new ArrayBlockingQueue<>(2000, true);

        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        stopCameraThreads();
        super.onDestroy();
        Toast.makeText(this, "Camera service has stopped", Toast.LENGTH_SHORT).show();
        Log.d(TAG, "camera service has stopped");
    }

    private void startProducer() {
        Log.d(TAG, "Starting producer thread from service.");
        UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        cameraPollingThread = new CameraPollingThread(intent, usbManager, buffer);
        cameraPollingThread.start();
        cameraPollingThreadLooper = cameraPollingThread.getLooper();
    }

    private void startConsumer() {
        Log.d(TAG, "Starting consumer thread from service.");
        processingThread = new ProcessingThread(buffer);
        processingThread.start();
        processingThreadLooper = processingThread.getLooper();
    }

    public boolean setCameraPolling(boolean flag) {
        if (flag && !isCameraPollingThreadRunning()) {
            startProducer();
            Log.d(TAG, "CameraPollingThread started");
            startConsumer();
            Log.d(TAG, "Consumer started");
            sendBroadcast(new Intent(MainActivity.ACTION_CAMERA_ACTIVATED));
            return true;
        } else if (flag && isCameraPollingThreadRunning()) {
            Log.d(TAG, "CameraPollingThread already running");
            return false;
        } else if (!flag && isCameraPollingThreadRunning()) {
            stopCameraThreads();
            Log.d(TAG, "CameraPollingThread and consumer stopped");
            return true;
        } else if (!flag && !isCameraPollingThreadRunning()) {
            Log.d(TAG, "CameraPollingThread cannot be stopped because it is not running");
            return false;
        } else return false;
    }

    public void startPlaybackThread() {
        startConsumer();

        Kryo kryo = new Kryo();
        kryo.register(ToExchange.class);
        kryo.register(byte[].class);

        try {
            Input input = new Input(new FileInputStream(ProcessingThread.filePath));
            long objectCounter = 0;
            while (!input.end()){
                ToExchange object1 = kryo.readObject(input, ToExchange.class);
                try {
                    buffer.put(object1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                objectCounter++;
            }
            Log.i(TAG, "input ended, counted " + objectCounter + " objects.");
            input.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public boolean isCameraPollingThreadRunning() {
        if (cameraPollingThread == null) {
            return false;
        }
        return cameraPollingThread.isAlive();
    }

    private void stopCameraThreads() {
        if (isCameraPollingThreadRunning()) {
            cameraPollingThread.setCameraAttached(false);
            cameraPollingThreadLooper.quit();
        }
        processingThread.setCameraAttached(false);
        //processingThreadLooper.quitSafely();
    }

    class LocalBinder extends Binder {
        CameraService getService() {
            return CameraService.this;
        }
    }

    //TODO implement bindings for activities
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }

}
