/**
 * Copyright 2019 The Google Research Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googleresearch.capturesync;

import static android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW;
import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_OFF;
import static android.hardware.camera2.CameraMetadata.CONTROL_AWB_MODE_AUTO;
import static android.hardware.camera2.CameraMetadata.CONTROL_MODE_AUTO;
import static android.hardware.camera2.CaptureRequest.CONTROL_AE_MODE;
import static android.hardware.camera2.CaptureRequest.CONTROL_AF_MODE;
import static android.hardware.camera2.CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
import static android.hardware.camera2.CaptureRequest.CONTROL_AWB_MODE;
import static android.hardware.camera2.CaptureRequest.CONTROL_MODE;
import static android.hardware.camera2.CaptureRequest.SENSOR_EXPOSURE_TIME;
import static android.hardware.camera2.CaptureRequest.SENSOR_SENSITIVITY;

import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.view.Surface;
import com.googleresearch.capturesync.ImageMetadataSynchronizer.CaptureRequestTag;
import java.util.ArrayList;
import java.util.List;

/** Helper class for creating common {@link CaptureRequest.Builder} instances. */
public class CaptureRequestFactory {

  private final CameraDevice device;

  public CaptureRequestFactory(CameraDevice camera) {
    device = camera;
  }

  /**
   * Makes a {@link CaptureRequest.Builder} for the viewfinder preview. This always adds the
   * viewfinder.
   */
  public CaptureRequest.Builder makePreview(
      Surface viewfinderSurface,
      List<Surface> imageSurfaces,
      long sensorExposureTimeNs,
      int sensorSensitivity)
      throws CameraAccessException {

    CaptureRequest.Builder builder = device.createCaptureRequest(TEMPLATE_PREVIEW);
    // Manually set exposure and sensitivity using UI sliders on the leader.
    builder.set(CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
    builder.set(SENSOR_EXPOSURE_TIME, sensorExposureTimeNs);
    builder.set(SENSOR_SENSITIVITY, sensorSensitivity);

    // Auto white balance used, these could be locked and sent from the leader instead.
    builder.set(CONTROL_AWB_MODE, CONTROL_AWB_MODE_AUTO);

    // Auto focus is used since different devices may have different manual focus values.
    builder.set(CONTROL_AF_MODE, CONTROL_AF_MODE_CONTINUOUS_PICTURE);

    if (viewfinderSurface != null) {
      builder.addTarget(viewfinderSurface);
    }
    List<Integer> targetIndices = new ArrayList<>();
    for (int i = 0; i < imageSurfaces.size(); i++) {
      builder.addTarget(imageSurfaces.get(i));
      targetIndices.add(i);
    }
    builder.setTag(new CaptureRequestTag(targetIndices, null));
    return builder;
  }

  public CaptureRequest.Builder makeFrameInjectionRequest(
      long desiredExposureTimeNs, List<Surface> imageSurfaces) throws CameraAccessException {
    CaptureRequest.Builder builder = device.createCaptureRequest(TEMPLATE_PREVIEW);
    builder.set(CONTROL_MODE, CONTROL_MODE_AUTO);
    builder.set(CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
    builder.set(SENSOR_EXPOSURE_TIME, desiredExposureTimeNs);
    // TODO: Inserting frame duration directly would be more accurate than inserting exposure since
    // {@code frame duration ~ exposure + variable overhead}. However setting frame duration may not
    // be supported on many android devices, so we use exposure time here.

    List<Integer> targetIndices = new ArrayList<>();
    for (int i = 0; i < imageSurfaces.size(); i++) {
      builder.addTarget(imageSurfaces.get(i));
      targetIndices.add(i);
    }
    builder.setTag(new CaptureRequestTag(targetIndices, PhaseAlignController.INJECT_FRAME));

    return builder;
  }
}