package com.xsignpostprocessing;

import android.util.Log;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.jsoniter.JsonIterator;
import com.jsoniter.any.Any;
import com.jsoniter.output.JsonStream;
import com.viettel.vcar.xsign.xvision.model.BBox;
import com.viettel.vcar.xsign.xvision.model.DetectedFrame;
import com.viettel.vcar.xsign.xvision.model.DetectedObject;
import com.viettel.vcar.xsign.xvision.model.DetectedSign;
import com.viettel.vcar.xsign.xvision.model.Location;
import com.viettel.vcar.xsign.xvision.model.json.JsonParsers;
import com.viettel.vcar.xsign.xvision.model.wrappers.WDetectedSign;
import com.viettel.vcar.xsign.xvision.postprocessing.filter.AspectRatioFilter;
import com.viettel.vcar.xsign.xvision.postprocessing.filter.Filter;
import com.viettel.vcar.xsign.xvision.postprocessing.filter.NMSFilter;
import com.viettel.vcar.xsign.xvision.postprocessing.filter.NormedSmallObjectFilter;
import com.viettel.vcar.xsign.xvision.postprocessing.nms.NormalNMS;
import com.viettel.vcar.xsign.xvision.postprocessing.tracking.CentroidTracker;
import com.viettel.vcar.xsign.xvision.util.SerDerUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@ReactModule(name = XsignPostProcessingModule.NAME)
public class XsignPostProcessingModule extends ReactContextBaseJavaModule {
    public static final String NAME = "XsignPostProcessing";

    public XsignPostProcessingModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    @NonNull
    public String getName() {
        return NAME;
    }

    // Example method
    // See https://reactnative.dev/docs/native-modules-android
    @ReactMethod
    public void multiply(int a, int b, Promise promise) {
        promise.resolve(a * b);
    }

    @ReactMethod
    public void detectSign(ReadableArray data, Integer inputImgWidth, Promise promise) {
      Log.d("XVISION", data.size() + "");
      List<DetectedFrame> detectedFrames = new ArrayList<>();

      for (int i = 0; i < data.size(); i++) {
        ReadableMap recognition = data.getMap(i);

        try {
          Integer frameId = recognition.getInt("frameId");
          String recognitionObjStr = recognition.getString("recognitions");
          Double lat = recognition.getDouble("lat");
          Double lng = recognition.getDouble("lng");
          Integer processedTime = recognition.getInt("processTime");
          Long receivedTime = Long.parseLong(recognition.getString("receivedTime"));

          long frameTimestamp = receivedTime - processedTime;

          Location location = Location.newBuilder()
            .setLat(lat)
            .setLng(lng)
            .build();
//          List<DetectedObject> detectedObjects = parseDetections(recognitionObjStr);
          List<DetectedObject> detectedObjects = JsonParsers.readDetectionList(recognitionObjStr);
          DetectedFrame detectedFrame = DetectedFrame.newBuilder()
            .setFrameId(frameId)
            .setLocation(location)
            .setTimestamp(frameTimestamp)
            .addAllObjects(detectedObjects)
            .build();

          detectedFrames.add(detectedFrame);
        } catch (Exception ex) {
          Log.d("XVISION", recognition.toString());
        }
      }

      List<Filter> filters =
        Arrays.asList(
          new NormedSmallObjectFilter(0.00024),
          new AspectRatioFilter(0.5, 2.0),
          new NMSFilter(new NormalNMS(0.25)));

      CentroidTracker tracker =
        CentroidTracker.newInstance(2000, 2, 0.25, filters);

      tracker.apply(detectedFrames.iterator());

      List<DetectedSign> signs = tracker.getResults();
      WritableArray array = Arguments.createArray();

      for (DetectedSign sign: signs) {
        String label = sign.getLabel();
        Location location = sign.getLocation();
        Double lat = location.getLat();
        Double lng = location.getLng();
//        String serialized = JsonStream.serialize(new WDetectedSign(sign));
        String serialized = SerDerUtils.serialize(new WDetectedSign(sign));
        WritableMap params = Arguments.createMap();
        params.putInt("id", sign.getId());
        params.putString("label", label);
        params.putDouble("lat", lat);
        params.putDouble("lng", lng);
        params.putString("receivedTime", sign.getTimestamp() + "");
        params.putString("serialized", serialized);

        array.pushMap(params);
      }
      Log.d("XVISION", "DetectedSign Size: " + signs.size());
      promise.resolve(array);
    }

    public static native int nativeMultiply(int a, int b);

    public static List<DetectedObject> parseDetections(String json) {
      Any any = JsonIterator.deserialize(json);

      List<DetectedObject> detectedObjects = new ArrayList<>();

      List<Any> detections = any.asList();

      for (Any det : detections) {
        Any location = det.get("location");
        BBox bbox = BBox.newBuilder()
          .setLeft(location.toInt("left"))
          .setTop(location.toInt("top"))
          .setRight(location.toInt("right"))
          .setBottom(location.toInt("bottom"))
          .build();

        float confidence = det.get("confidence").toFloat();
        String title = det.get("title").toString();

        DetectedObject detectedObject = DetectedObject.newBuilder()
          .setBbox(bbox)
          .setConfidence(confidence)
          .setTitle(title)
          .build();

        detectedObjects.add(detectedObject);
      }

      return detectedObjects;
    }
}
