/*
 * Copyright (C) 2022 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.
 */

#define LOG_TAG "AidlCameraDeviceCallbacks"

#include <aidl/AidlCameraDeviceCallbacks.h>
#include <aidl/AidlUtils.h>
#include <aidl/android/frameworks/cameraservice/common/Status.h>
#include <hidl/Utils.h>
#include <utility>

namespace android::frameworks::cameraservice::device::implementation {

// VNDK classes
using SCameraMetadata = ::aidl::android::frameworks::cameraservice::device::CameraMetadata;
using SCaptureResultExtras =
        ::aidl::android::frameworks::cameraservice::device::CaptureResultExtras;
using SPhysicalCaptureResultInfo =
        ::aidl::android::frameworks::cameraservice::device::PhysicalCaptureResultInfo;
using SStatus = ::aidl::android::frameworks::cameraservice::common::Status;
// NDK classes
using UCaptureResultExtras = ::android::hardware::camera2::impl::CaptureResultExtras;
using UPhysicalCaptureResultInfo = ::android::hardware::camera2::impl::PhysicalCaptureResultInfo;

using ::android::hardware::cameraservice::utils::conversion::aidl::convertToAidl;
using ::android::hardware::cameraservice::utils::conversion::aidl::filterVndkKeys;

const char *AidlCameraDeviceCallbacks::kResultKey = "CaptureResult";


bool AidlCameraDeviceCallbacks::initializeLooper(int vndkVersion) {
    mCbLooper = new ALooper;
    mCbLooper->setName("cs-looper");
    status_t err = mCbLooper->start(/*runOnCallingThread*/ false, /*canCallJava*/ false,
                                    PRIORITY_DEFAULT);
    if (err !=OK) {
        ALOGE("Unable to start camera device callback looper");
        return false;
    }
    mHandler = new CallbackHandler(this, vndkVersion);
    mCbLooper->registerHandler(mHandler);
    return true;
}

AidlCameraDeviceCallbacks::AidlCameraDeviceCallbacks(
        const std::shared_ptr<SICameraDeviceCallback>& base):
      mBase(base), mDeathPipe(this, base->asBinder()) {}

AidlCameraDeviceCallbacks::~AidlCameraDeviceCallbacks() {
    if (mCbLooper != nullptr) {
        if (mHandler != nullptr) {
            mCbLooper->unregisterHandler(mHandler->id());
        }
        mCbLooper->stop();
    }
    mCbLooper.clear();
    mHandler.clear();
}

binder::Status AidlCameraDeviceCallbacks::onDeviceError(
    int32_t errorCode, const CaptureResultExtras& resultExtras) {
    using hardware::cameraservice::utils::conversion::aidl::convertToAidl;
    SCaptureResultExtras cre = convertToAidl(resultExtras);
    auto ret = mBase->onDeviceError(convertToAidl(errorCode), cre);
    LOG_STATUS_ERROR_IF_NOT_OK(ret, "onDeviceError")
    return binder::Status::ok();
}

binder::Status AidlCameraDeviceCallbacks::onClientSharedAccessPriorityChanged(bool primaryClient) {
    if (!flags::camera_multi_client()) {
        return binder::Status::ok();
    }
    auto ret = mBase->onClientSharedAccessPriorityChanged(primaryClient);
    LOG_STATUS_ERROR_IF_NOT_OK(ret, "onClientSharedAccessPriorityChanged")
    return binder::Status::ok();
 }

binder::Status AidlCameraDeviceCallbacks::onDeviceIdle() {
    auto ret = mBase->onDeviceIdle();
    LOG_STATUS_ERROR_IF_NOT_OK(ret, "onDeviceIdle")
    return binder::Status::ok();
}

binder::Status AidlCameraDeviceCallbacks::onCaptureStarted(
        const CaptureResultExtras& resultExtras, int64_t timestamp) {
    using hardware::cameraservice::utils::conversion::aidl::convertToAidl;
    SCaptureResultExtras hCaptureResultExtras = convertToAidl(resultExtras);
    auto ret = mBase->onCaptureStarted(hCaptureResultExtras, timestamp);
    LOG_STATUS_ERROR_IF_NOT_OK(ret, "onCaptureStarted")
    return binder::Status::ok();
}

void AidlCameraDeviceCallbacks::convertResultMetadataToAidl(const camera_metadata_t* src,
                                                            SCaptureMetadataInfo* dst) {
    // First try writing to fmq.
    size_t metadata_size = get_camera_metadata_size(src);
    if ((metadata_size > 0) &&
        (mCaptureResultMetadataQueue->availableToWrite() > 0)) {
        if (mCaptureResultMetadataQueue->write((int8_t *)src, metadata_size)) {
            dst->set<SCaptureMetadataInfo::fmqMetadataSize>(metadata_size);
        } else {
            ALOGW("%s Couldn't use fmq, falling back to hwbinder", __FUNCTION__);
            SCameraMetadata metadata;
            hardware::cameraservice::utils::conversion::aidl::cloneToAidl(src, &metadata);
            dst->set<SCaptureMetadataInfo::metadata>(std::move(metadata));
        }
    }
}

void AidlCameraDeviceCallbacks::CallbackHandler::onMessageReceived(const sp<AMessage> &msg) {
    sp<RefBase> obj = nullptr;
    sp<ResultWrapper> resultWrapper = nullptr;
    bool found = false;
    switch (msg->what()) {
        case kWhatResultReceived:
            found = msg->findObject(kResultKey, &obj);
            if (!found || obj == nullptr) {
                ALOGE("Cannot find result object in callback message");
                return;
            }
            resultWrapper = static_cast<ResultWrapper*>(obj.get());
            processResultMessage(resultWrapper);
            break;
        default:
            ALOGE("Unknown callback sent");
            break;
    }
    }

void AidlCameraDeviceCallbacks::CallbackHandler::processResultMessage(
    sp<ResultWrapper> &resultWrapper) {
    sp<AidlCameraDeviceCallbacks> converter = mConverter.promote();
    if (converter == nullptr) {
        ALOGE("Callback wrapper has died, result callback cannot be made");
        return;
    }
    CameraMetadataNative &result = resultWrapper->mResult;
    auto resultExtras = resultWrapper->mResultExtras;
    SCaptureResultExtras convResultExtras =
            hardware::cameraservice::utils::conversion::aidl::convertToAidl(resultExtras);

    // Convert Metadata into HCameraMetadata;
    SCaptureMetadataInfo captureMetadataInfo;
    if (filterVndkKeys(mVndkVersion, result, /*isStatic*/false) != OK) {
        ALOGE("%s: filtering vndk keys from result failed, not sending onResultReceived callback",
                __FUNCTION__);
        return;
    }
    const camera_metadata_t *rawMetadata = result.getAndLock();
    converter->convertResultMetadataToAidl(rawMetadata, &captureMetadataInfo);
    result.unlock(rawMetadata);

    auto &physicalCaptureResultInfos = resultWrapper->mPhysicalCaptureResultInfos;
    std::vector<SPhysicalCaptureResultInfo> stableCaptureResInfo =
            convertToAidl(physicalCaptureResultInfos, converter->mCaptureResultMetadataQueue);
    auto ret = converter->mBase->onResultReceived(captureMetadataInfo,
                                                  convResultExtras,
                                                  stableCaptureResInfo);

    LOG_STATUS_ERROR_IF_NOT_OK(ret, "OnResultReceived")
}

binder::Status AidlCameraDeviceCallbacks::onResultReceived(
    const CameraMetadataNative& result,
    const UCaptureResultExtras& resultExtras,
    const ::std::vector<UPhysicalCaptureResultInfo>& physicalCaptureResultInfos) {
    // Wrap CameraMetadata, resultExtras and physicalCaptureResultInfos in on
    // sp<RefBase>-able structure and post it.
    sp<ResultWrapper> resultWrapper = new ResultWrapper(const_cast<CameraMetadataNative &>(result),
                                                        resultExtras, physicalCaptureResultInfos);
    sp<AMessage> msg = new AMessage(kWhatResultReceived, mHandler);
    msg->setObject(kResultKey, resultWrapper);
    msg->post();
    return binder::Status::ok();
}

binder::Status AidlCameraDeviceCallbacks::onPrepared(int32_t streamId) {
    auto ret = mBase->onPrepared(streamId);
    LOG_STATUS_ERROR_IF_NOT_OK(ret, "onPrepared")
    return binder::Status::ok();
}

binder::Status AidlCameraDeviceCallbacks::onRepeatingRequestError(
    int64_t lastFrameNumber,
    int32_t repeatingRequestId) {
    auto ret =
        mBase->onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
    LOG_STATUS_ERROR_IF_NOT_OK(ret, "onRepeatingRequestError")
    return binder::Status::ok();
}

binder::Status AidlCameraDeviceCallbacks::onRequestQueueEmpty() {
    // not implemented
    return binder::Status::ok();
}

status_t AidlCameraDeviceCallbacks::linkToDeath(const sp<DeathRecipient>& recipient,
                                                void* cookie, uint32_t flags) {
    return mDeathPipe.linkToDeath(recipient, cookie, flags);
}
status_t AidlCameraDeviceCallbacks::unlinkToDeath(const wp<DeathRecipient>& recipient,
                                                  void* cookie,
                                                  uint32_t flags,
                                                  wp<DeathRecipient>* outRecipient) {
    return mDeathPipe.unlinkToDeath(recipient, cookie, flags, outRecipient);
}

} // namespace android::frameworks::cameraservice::device::implementation
