/*
 *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

package tee3.webrtc;
import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.util.Log;
import android.view.Surface;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

//import com.serenegiant.usb.DeviceFilter;
//import com.serenegiant.usb.Size;
//import com.serenegiant.usb.USBMonitor;
//import com.serenegiant.usb.UVCCamera;

/**
 * ANR报错
 * 1. tee3.webrtc.UsbCameraCapturerAndroid.stopCapture(UsbCameraCapturerAndroid.java)
 *
 * 2. com.serenegiant.usb.UVCCamera.nativeStopPreview(Native Method)
 *    com.serenegiant.usb.UVCCamera.stopPreview(UVCCamera.java:452)
 *    com.serenegiant.usb.UVCCamera.close(UVCCamera.java:247)
 *    com.serenegiant.usb.UVCCamera.destroy(UVCCamera.java:460)
 */

@TargetApi(21)
public class UsbCameraCapturerAndroid implements VideoCapturer, SurfaceTextureHelper.OnTextureFrameAvailableListener {

  public static final String TAG = UsbCameraCapturerAndroid.class.getSimpleName();

  @Nullable private SurfaceTextureHelper surfaceTextureHelper;
  @Nullable private CapturerObserver capturerObserver;

  private final Object mSync = new Object();
  private int width;
  private int height;
  private long numCapturedFrames = 0;
  private boolean isRequestedPermission = false;
  public  boolean isAttached = false;
  private List<UsbCameraEvent> usbCameraEvents = new ArrayList<>();
  private Context context;

//  private USBMonitor mUSBMonitor;
//  private UVCCamera mUVCCamera;

  private UsbCameraCapturerAndroid() {
  }

  public static UsbCameraCapturerAndroid getInstance() {
    return Builder.INSTANCE;
  }

  private static class Builder {
    private static final UsbCameraCapturerAndroid INSTANCE = new UsbCameraCapturerAndroid();
  }

  private void checkNotDisposed() {
//    if (isDisposed) {
//      throw new RuntimeException("capturer is disposed.");
//    }
  }

  public UsbCameraCapturerAndroid addUsbCameraListener(UsbCameraEvent usbCameraEvent) {
    if (usbCameraEvent != null && !usbCameraEvents.contains(usbCameraEvent)) {
      this.usbCameraEvents.add(usbCameraEvent);
    }
    return this;
  }

  public UsbCameraCapturerAndroid setupUsbCameraMonitor(Context applicationContext) {
//    if (mUSBMonitor == null) {
//      mUSBMonitor = USBMonitor.instance().init(applicationContext, mOnDeviceConnectListener);
//      mUSBMonitor.register();
//    }
    return this;
  }

  public UsbCameraCapturerAndroid stopUsbCameraMonitor() {
//    if (mUSBMonitor != null) {
//      mUSBMonitor.unregister();
//      mUSBMonitor.destroy();
//      mUSBMonitor = null;
//      usbCameraEvents.clear();
//    }
    return this;
  }

  @Override
  public synchronized void initialize(final SurfaceTextureHelper surfaceTextureHelper,
      final Context applicationContext, final CapturerObserver capturerObserver) {
    this.context = applicationContext;
    checkNotDisposed();
    if (capturerObserver == null) {
      throw new RuntimeException("capturerObserver not set.");
    }
    this.capturerObserver = capturerObserver;

    if (surfaceTextureHelper == null) {
      throw new RuntimeException("surfaceTextureHelper not set.");
    }
    this.surfaceTextureHelper = surfaceTextureHelper;

    setupUsbCameraMonitor(applicationContext);
  }

  @Override
  public synchronized void startCapture(final int width, final int height, final int ignoredFramerate) {
    checkNotDisposed();
    Logging.d(TAG, "startCapture() in, width=" + width +", height=" + height);
    this.width = width;
    this.height = height;

    if (!isRequestedPermission && !requestUsbPermission()) {
      for (UsbCameraEvent listener : usbCameraEvents) {
        listener.onCancel();
      }
      return;
    }

    capturerObserver.onCapturerStarted(true);
    surfaceTextureHelper.startListening(UsbCameraCapturerAndroid.this);
    Logging.d(TAG, "startCapture() out");
  }

  @Override
  public synchronized void stopCapture() {
    checkNotDisposed();
    ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() {
      @Override
      public void run() {
        surfaceTextureHelper.stopListening();
        capturerObserver.onCapturerStopped();
      }
    });
    Logging.d(TAG, "stopCapture() out");
  }

  @Override
  public synchronized void dispose() {
    isRequestedPermission = false;
//    if (mUVCCamera != null) {
//      mUVCCamera.stopPreview();
//      mUVCCamera.stopCapture();
//      mUVCCamera.close();
//      mUVCCamera.destroy();
//      mUVCCamera = null;
//    }
  }

  @Override
  public synchronized void changeCaptureFormat(
      final int width, final int height, final int ignoredFramerate) {
    checkNotDisposed();
    this.width = width;
    this.height = height;
  }

  @Override
  public void onTextureFrameAvailable(int oesTextureId, float[] transformMatrix, long timestampNs) {
//    numCapturedFrames++;
    final VideoFrame.Buffer buffer = surfaceTextureHelper.createTextureBuffer(
        width, height, RendererCommon.convertMatrixToAndroidGraphicsMatrix(transformMatrix));
    final VideoFrame frame = new VideoFrame(buffer, 0 /* rotation */, timestampNs);
    capturerObserver.onFrameCaptured(frame);
    frame.release();
  }

  @Override
  public boolean isScreencast() {
    return true;
  }

  public long getNumCapturedFrames() {
    return numCapturedFrames;
  }

  public boolean isAttached() {
    return isAttached;
  }

//  private USBMonitor.OnDeviceConnectListener mOnDeviceConnectListener = new USBMonitor.OnDeviceConnectListener() {
//    @Override
//    public void onAttach(final UsbDevice device) {
//      isAttached = true;
//      for (UsbCameraEvent listener : usbCameraEvents) {
//        listener.onAttach();
//      }
//    }
//
//    @Override
//    public void onConnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock, final boolean createNew) {
//      synchronized (mSync) {
//        if (mUVCCamera != null && isRequestedPermission) {
//          return;
//        }
//        isRequestedPermission = true;
//
//        int width = 1280, height = 720;
//
//        final UVCCamera camera = new UVCCamera();
//        camera.open(ctrlBlock);
//        List<Size> supportedSizeList = camera.getSupportedSizeList();
//        String sizeListString = supportedSizeList.toString();
//        if (sizeListString.contains("1920x1080")) {
//          width = 1920;
//          height = 1080;
//        }
//
//        Logging.d(TAG, "supportedSize:" + camera.getSupportedSize()); // supportedSize:{"formats":[{"index":1,"type":6,"default":1,"size":["720x480","1280x720","1920x1080"]}]}
//        try {
////          camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, 14, 30,UVCCamera.FRAME_FORMAT_MJPEG, 1.0f);
//          camera.setPreviewSize(width, height, 14, 30, UVCCamera.FRAME_FORMAT_MJPEG, 1.0f);
//        } catch (final IllegalArgumentException e) {
//          try {
//            // fallback to YUV mode
//            camera.setPreviewSize(width, height, UVCCamera.DEFAULT_PREVIEW_MODE);
//          } catch (final IllegalArgumentException e1) {
//            camera.destroy();
//            for (UsbCameraEvent listener : usbCameraEvents) {
//              listener.onDisconnect();
//            }
//            return;
//          }
//        }
//        Surface surface = new Surface(surfaceTextureHelper.getSurfaceTexture());
////        camera.startCapture(surface);
//        camera.setPreviewDisplay(surface);
//        camera.startPreview();
//        synchronized (mSync) {
//          mUVCCamera = camera;
//        }
//        for (UsbCameraEvent listener : usbCameraEvents) {
//          listener.onConnect();
//        }
//      }
//    }
//
//    @Override
//    public void onDisconnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock) {
//      synchronized (mSync) {
//        isAttached = false;
//        if (mUVCCamera != null) {
//          for (UsbCameraEvent listener : usbCameraEvents) {
//            listener.onDisconnect();
//          }
//        }
//      }
//    }
//
//    @Override
//    public void onDetach(final UsbDevice device) {
//      isAttached = false;
//      for (UsbCameraEvent listener : usbCameraEvents) {
//        listener.onDetach();
//      }
//    }
//
//    @Override
//    public void onCancel(final UsbDevice device) {
//      for (UsbCameraEvent listener : usbCameraEvents) {
//        listener.onCancel();
//      }
//    }
//  };

  private boolean requestUsbPermission() {
//    if (mUSBMonitor == null) {
//      mUSBMonitor = USBMonitor.instance().init(context, mOnDeviceConnectListener);
//      mUSBMonitor.register();
//    }
//    final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(context, com.serenegiant.uvccamera.R.xml.device_filter);
//    List<UsbDevice> deviceList = mUSBMonitor.getDeviceList(filter.get(0));
//    Logging.d(TAG, "usbDeviceList -> " + deviceList.toString());
//    if (deviceList.size() >= 1) {
//      mUSBMonitor.requestPermission(deviceList.get(0));
//    } else {
//      Logging.w(TAG, "usbDeviceList.size = 0");
//      return false;
//    }
    return true;
  }

  public interface UsbCameraEvent {
    void onAttach();
    void onDetach();
    void onConnect();
    void onDisconnect();
    void onCancel();
  }

}
