/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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.android.camera.one.v2.common;

import android.annotation.TargetApi;
import android.graphics.Rect;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.MeteringRectangle;
import android.os.Build.VERSION_CODES;

import com.android.camera.async.Lifetime;
import com.android.camera.async.Observable;
import com.android.camera.async.SafeCloseable;
import com.android.camera.one.OneCamera;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.one.OneCameraCharacteristics.FaceDetectMode;
import com.android.camera.one.Settings3A;
import com.android.camera.one.v2.autofocus.ManualAutoFocus;
import com.android.camera.one.v2.autofocus.ManualAutoFocusFactory;
import com.android.camera.one.v2.commands.CameraCommand;
import com.android.camera.one.v2.commands.CameraCommandExecutor;
import com.android.camera.one.v2.commands.ResettingRunnableCameraCommand;
import com.android.camera.one.v2.commands.PreviewCommandFactory;
import com.android.camera.one.v2.core.FrameServer;
import com.android.camera.one.v2.core.RequestBuilder;
import com.android.camera.one.v2.core.RequestTemplate;
import com.android.camera.one.v2.face.FaceDetect;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.util.concurrent.MoreExecutors;

import java.util.concurrent.Executors;

/**
 * Wires together functionality common to all cameras:
 * <ul>
 * <li>Tap-to-focus</li>
 * <li>Auto exposure, based on the current flash-setting</li>
 * <li>Metering regions</li>
 * <li>Zoom</li>
 * <li>TODO Logging of OS/driver-level errors</li>
 * </ul>
 * <p>
 * Note that this does not include functionality for taking pictures, since this
 * varies depending on hardware capability.
 * </p>
 */
@TargetApi(VERSION_CODES.LOLLIPOP)
public class BasicCameraFactory {
    private final ManualAutoFocus mManualAutoFocus;
    private final RequestBuilder.Factory mMeteredZoomedRequestBuilder;
    private final Runnable mPreviewUpdater;

    /**
     * @param lifetime The lifetime of all created objects and their associated
     *            resources.
     * @param cameraCharacteristics
     * @param rootTemplate Provides preconfigured request builders to be used for
     *            all requests to mFrameServer.
     * @param cameraCommandExecutor The
     * @param templateType The template (e.g. CameraDevice.TEMPLATE_PREVIEW) to
     *            use for repeating requests.
     */
    public BasicCameraFactory(Lifetime lifetime,
            OneCameraCharacteristics cameraCharacteristics,
            FrameServer frameServer,
            RequestBuilder.Factory rootTemplate,
            CameraCommandExecutor cameraCommandExecutor,
            PreviewCommandFactory previewCommandFactory,
            Observable<OneCamera.PhotoCaptureParameters.Flash> flash,
            Observable<Integer> exposure,
            Observable<Float> zoom,
            Observable<Boolean> hdrSceneSetting,
            int templateType) {
        RequestTemplate requestTemplate = new RequestTemplate(rootTemplate);
        if (cameraCharacteristics.isContinuousPictureAutoFocusSupported()) {
            requestTemplate.setParam(
                    CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        }

        requestTemplate.setParam(
              CaptureRequest.CONTROL_AE_MODE, new FlashBasedAEMode(flash, hdrSceneSetting));
        requestTemplate.setParam(
              CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, exposure);

        Supplier<FaceDetectMode> faceDetectMode = Suppliers.ofInstance(
              FaceDetect.getHighestFaceDetectMode(cameraCharacteristics));

        requestTemplate.setParam(CaptureRequest.CONTROL_MODE,
              new ControlModeSelector(hdrSceneSetting,
                    faceDetectMode,
                    cameraCharacteristics.getSupportedHardwareLevel()));
        requestTemplate.setParam(
              CaptureRequest.CONTROL_SCENE_MODE, new ControlSceneModeSelector(
                    hdrSceneSetting,
                    faceDetectMode,
                    cameraCharacteristics.getSupportedHardwareLevel()));
        requestTemplate.setParam(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
              new StatisticsFaceDetectMode(faceDetectMode));

        Supplier<Rect> cropRegion = new ZoomedCropRegion(
                cameraCharacteristics.getSensorInfoActiveArraySize(), zoom);
        requestTemplate.setParam(CaptureRequest.SCALER_CROP_REGION, cropRegion);

        CameraCommand previewUpdaterCommand =
              previewCommandFactory.get(requestTemplate, templateType);

        // Use a resetting command to ensure that many rapid settings changes do
        // not result in many rapid (>30fps) requests to restart the preview.
        mPreviewUpdater = new ResettingRunnableCameraCommand(cameraCommandExecutor,
              previewUpdaterCommand);

        // Resend the repeating preview request when the zoom or flash state
        // changes to apply the new setting.
        // Also, de-register these callbacks when the camera is closed (to
        // not leak memory).
        SafeCloseable zoomCallback = zoom.addCallback(mPreviewUpdater, MoreExecutors
                .directExecutor());
        lifetime.add(zoomCallback);
        SafeCloseable flashCallback = flash.addCallback(mPreviewUpdater, MoreExecutors
                .directExecutor());
        lifetime.add(flashCallback);
        SafeCloseable exposureCallback = exposure.addCallback(mPreviewUpdater, MoreExecutors
                .directExecutor());
        lifetime.add(exposureCallback);
        SafeCloseable hdrCallback = hdrSceneSetting.addCallback(mPreviewUpdater, MoreExecutors
                .directExecutor());
        lifetime.add(hdrCallback);

        int sensorOrientation = cameraCharacteristics.getSensorOrientation();

        ManualAutoFocusFactory manualAutoFocusFactory = ManualAutoFocusFactory.create(new
                Lifetime(lifetime), frameServer, cameraCommandExecutor, cropRegion,
                sensorOrientation, mPreviewUpdater, requestTemplate,
                templateType, new Settings3A(), Executors.newScheduledThreadPool(1),
                3 /* afHoldSeconds */, cameraCharacteristics.isAutoExposureSupported(),
                cameraCharacteristics.isAutoFocusSupported());
        mManualAutoFocus = manualAutoFocusFactory.provideManualAutoFocus();
        Supplier<MeteringRectangle[]> aeRegions =
                manualAutoFocusFactory.provideAEMeteringRegion();
        Supplier<MeteringRectangle[]> afRegions =
                manualAutoFocusFactory.provideAFMeteringRegion();

        requestTemplate.setParam(CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
        requestTemplate.setParam(CaptureRequest.CONTROL_AF_REGIONS, afRegions);

        mMeteredZoomedRequestBuilder = requestTemplate;
    }

    public RequestBuilder.Factory provideMeteredZoomedRequestBuilder() {
        return mMeteredZoomedRequestBuilder;
    }

    public ManualAutoFocus provideManualAutoFocus() {
        return mManualAutoFocus;
    }

    public Runnable providePreviewUpdater() {
        return mPreviewUpdater;
    }
}
