/*
Copyright 2018 Google LLC

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.google.android.imaging.pixelvisualcorecamera.api1;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.Toast;
import com.google.android.imaging.pixelvisualcorecamera.R;
import com.google.android.imaging.pixelvisualcorecamera.api1.Camera1Controller.CaptureCallback;
import com.google.android.imaging.pixelvisualcorecamera.common.FileSystem;
import com.google.android.imaging.pixelvisualcorecamera.common.Intents;
import com.google.android.imaging.pixelvisualcorecamera.common.Preferences;
import com.google.android.imaging.pixelvisualcorecamera.common.Toasts;
import com.google.android.imaging.pixelvisualcorecamera.common.Utils;
import java.io.IOException;

/**
 * Primary activity for an API 1 camera.
 */
@SuppressWarnings("deprecation")
public final class CameraApi1Activity extends Activity {

  private static final String TAG = "PvcCamApi1";
  private static final String STATE_ZOOM = "zoom";

  private Camera1Controller cameraController;
  private CameraPreview mPreview;
  private Preferences preferences;

  private int cameraId;
  private ScaleGestureDetector zoomScaleGestureDetector;
  private ZoomScaleGestureListener zoomScaleGestureListener;

  // ===============================================================================================
  // Activity Framework Callbacks
  // ===============================================================================================

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "[onCreate]");
    preferences = new Preferences(this);
    setContentView(R.layout.camera1);
    Utils.setSystemUiOptionsForFullscreen(this);
    Button captureButton = findViewById(R.id.button_capture);
    captureButton.setOnClickListener(v -> cameraController.takePicture(captureCallback));

    cameraController = new Camera1Controller(captureButton);

    zoomScaleGestureListener = new ZoomScaleGestureListener(
        cameraController, findViewById(R.id.zoom_level_label), STATE_ZOOM);
    zoomScaleGestureListener.restoreInstanceState(savedInstanceState);
    zoomScaleGestureDetector = new ScaleGestureDetector(this, zoomScaleGestureListener);

    initTopControls();
  }

  /** Configure the system UI elements when we receive focus. */
  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    Log.d(TAG, "[onWindowFocusChanged] hasFocus = " + hasFocus);
    if (hasFocus) {
      Utils.setSystemUiOptionsForFullscreen(this);
    }
  }

  /** Acquire the camera and start the preview. */
  @Override
  public void onResume() {
    super.onResume();
    Log.d(TAG, "[onResume]");
    try {
      cameraController.acquireCamera(cameraId);
      configureOutputSize();
      cameraController.setDefaultParameters(getWindowManager().getDefaultDisplay().getRotation());
      zoomScaleGestureListener.initZoomParameters();
      cameraController.setZoom(zoomScaleGestureListener.getZoom());
      configurePreview();
    } catch (IOException e) {
      String errorMessage = "Failed to acquire camera";
      Toasts.showToast(this, errorMessage, Toast.LENGTH_LONG);
      Log.w(TAG, errorMessage, e);
      finish();
    }
  }

  /** Release the camera. */
  @Override
  protected void onPause() {
    super.onPause();
    Log.d(TAG, "[onPause]");
    if (cameraController.isAcquired()) {
      cameraController.releaseCamera();
    }
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    zoomScaleGestureListener.saveInstanceState(outState);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    return zoomScaleGestureDetector.onTouchEvent(event);
  }

  // ===============================================================================================
  // UI Management
  // ===============================================================================================

  private void configurePreview() {
    if (mPreview == null) {
      FrameLayout preview = findViewById(R.id.camera_preview);
      mPreview = new CameraPreview(this, cameraController);
      preview.addView(mPreview);
    } else {
      mPreview.reset();
    }
  }

  // ===============================================================================================
  // Capture Callback
  // ===============================================================================================

  private final CaptureCallback captureCallback = new CaptureCallback() {
    @Override
    public void onPictureTaken(byte[] bytes, Camera camera) {
      FileSystem.saveImage(CameraApi1Activity.this, bytes, /*isApi1=*/ true);
      // Post this on the UI thread to allow the controller state machine to complete it's
      // transitions.
      mPreview.post(() -> mPreview.reset());
    }
  };

  // ===============================================================================================
  // Top Controls
  // ===============================================================================================

  private void initTopControls() {
    initApiSwitch();
    initCameraSelection();
    setCameraIconForCurrentCamera();
    ImageButton cameraSelectionButton = findViewById(R.id.control_camera_selection);
    cameraSelectionButton.setOnClickListener(cameraSelectionOnClickListener);
  }

  @SuppressLint("SetTextI18n")
  private void initApiSwitch() {
    Button button = findViewById(R.id.api_selector);
    button.setText("API 1");
    button.setOnClickListener(v -> {
      Log.i(TAG, "switching to API 2");
      preferences.setModeApi1(false);
      finish();
      startActivity(Intents.createApi2Intent());
    });
  }

  /** Initializes cameraId state from global preferences. */
  private void initCameraSelection() {
    cameraId = preferences.getCameraId();
    if (cameraId > CameraInfo.CAMERA_FACING_FRONT) {
      Log.e(TAG, "out of bounds camera id: " + cameraId);
      cameraId = CameraInfo.CAMERA_FACING_BACK;
      preferences.setCameraId(cameraId);
    }
  }

  private void setCameraIconForCurrentCamera() {
    ImageButton button = findViewById(R.id.control_camera_selection);
    switch (cameraId) {
      case CameraInfo.CAMERA_FACING_BACK:
        button.setImageResource(R.drawable.ic_camera_rear_white_24);
        break;
      case CameraInfo.CAMERA_FACING_FRONT:
        button.setImageResource(R.drawable.ic_camera_front_white_24);
        break;
      default:
        break;
    }
  }

  /** Handles clicks on the camera selection button. */
  private final OnClickListener cameraSelectionOnClickListener = new OnClickListener() {

    @Override
    public void onClick(View view) {
      if (cameraController.isAcquired()) {
        Log.i(TAG, "changing cameras, releasing camera");
        cameraController.releaseCamera();
      }

      // Swap camera ids.
      switch (cameraId) {
        case CameraInfo.CAMERA_FACING_BACK:
          cameraId = CameraInfo.CAMERA_FACING_FRONT;
          break;
        case CameraInfo.CAMERA_FACING_FRONT:
          cameraId = CameraInfo.CAMERA_FACING_BACK;
          break;
        default:
          Log.e(TAG, "out of bounds camera id: " + cameraId);
          cameraId = CameraInfo.CAMERA_FACING_BACK;
      }
      preferences.setCameraId(cameraId);
      setCameraIconForCurrentCamera();
      zoomScaleGestureListener.reset();

      Log.i(TAG, "restarting with new camera");
      try {
        cameraController.acquireCamera(cameraId);
        configureOutputSize();
        cameraController.setDefaultParameters(getWindowManager().getDefaultDisplay().getRotation());
        zoomScaleGestureListener.initZoomParameters();
        cameraController.setZoom(zoomScaleGestureListener.getZoom());
        configurePreview();
      }  catch (IOException e) {
        Log.w(TAG, "failed to acquire camera", e);
      }
    }
  };

  /** Call this after a camera has been acquired. */
  private void configureOutputSize() {
    Log.d(TAG, "configureOutputSize");
    android.util.Size[] supportedSizes = cameraController.getSupportedPictureSizes();
    cameraController.setPictureSize(supportedSizes[0]);
  }
}