/*
 * Copyright (C) 2013-2017 Intel Corporation
 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
 *
 * 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 "Camera3Request"

#include "LogHelper.h"
#include "PerformanceTraces.h"
#include "CameraStream.h"
#include "Camera3Request.h"
#include "PlatformData.h"
#include <fcntl.h>


NAMESPACE_DECLARATION {

/**
 * \def RESULT_ENTRY_CAP
 * Maximum number of metadata entries stored in a result buffer. Used for
 * memory allocation purposes.
 */
#define RESULT_ENTRY_CAP 256
/**
 * \def RESULT_DATA_CAP
 * Maximum amount of data storage in bytes allocated in result buffers.
 */
#define RESULT_DATA_CAP 73728


Camera3Request::Camera3Request():
        mCallback(nullptr),
        mRequestId(0),
        mCameraId(-1),
        mSequenceId(-1)
{
    LOGI("@%s Creating request with pointer %p", __FUNCTION__, this);
    deInit();
    /**
     * Allocate the CameraBuffer objects that will be recycled for each request
     * Since CameraBuf is refCounted the objects will be deleted when the Request
     * is destroyed. No need to manually delete them
     * These Camerabuffer objects are mere wrappers that will be filled when
     * we initialize the Request object
     */
    for (int i = 0; i < MAX_NUMBER_OUTPUT_STREAMS; i++) {
        mOutputBufferPool[i] = std::make_shared<CameraBuffer>();
    }

    mInputBuffer = std::make_shared<CameraBuffer>();
    mResultBufferAllocated = false;
}

Camera3Request::~Camera3Request()
{
    LOGI("@%s Destroying request with pointer %p", __FUNCTION__, this);
    mInitialized = false;
    if (mResultBufferAllocated) {
        freePartialResultBuffers();
    }
}

void Camera3Request::dumpSetting()
{
    static unsigned int count = 0;
    count++;

    if (gDumpInterval > 1) {
        if (count % gDumpInterval != 0) {
            return;
        }
    }
    LOGD("%s:%d: enter", __func__, __LINE__);
    std::string fileName(gDumpPath);
    if (CC_UNLIKELY(LogHelper::isDumpTypeEnable(CAMERA_DUMP_META))) {
        const char intent_val[7][20] = {"CUSTOM", "PREVIEW", "STILL_CAPTURE", "VIDEO_RECORD", "VIDEO_SNAPSHOT", "ZERO_SHUTTER_LAG", "MANUAL"};

        camera_metadata_entry_t entry;
        uint8_t intent = 0;
        entry = mSettings.find(ANDROID_CONTROL_CAPTURE_INTENT);
        if (entry.count == 1) {
            intent = entry.data.u8[0];
        }

        std::string strIntentName = intent_val[intent];

        fileName += "dumpmeta_" + std::to_string(mCameraId) + "_" + strIntentName  + "_setting_" + std::to_string(mRequestId);
        LOGI("%s filename is %s", __FUNCTION__, fileName.data());
        int fd = open(fileName.data(), O_RDWR | O_CREAT, 0666);
        if (fd != -1) {
            mSettings.dump(fd, 2);
        } else {
            LOGE("dumpSetting: open failed, errmsg: %s\n", strerror(errno));
        }
        close(fd);
    }
}

void Camera3Request::dumpResults()
{
    static unsigned int count = 0;
    count++;

    if (gDumpInterval > 1) {
        if (count % gDumpInterval != 0) {
            return;
        }
    }
    if (CC_UNLIKELY(LogHelper::isDumpTypeEnable(CAMERA_DUMP_META))) {
        std::string fileName(gDumpPath);
        const char intent_val[7][20] = {"CUSTOM", "PREVIEW", "STILL_CAPTURE", "VIDEO_RECORD", "VIDEO_SNAPSHOT", "ZERO_SHUTTER_LAG", "MANUAL"};

        camera_metadata_entry_t entry;
        uint8_t intent = 0;
        entry = mSettings.find(ANDROID_CONTROL_CAPTURE_INTENT);
        if (entry.count == 1) {
            intent = entry.data.u8[0];
        }

        std::string strIntentName = intent_val[intent];

        fileName += "dumpmeta_" + std::to_string(mCameraId) + "_" + strIntentName  + "_result_" + std::to_string(mRequestId);
        LOGI("%s filename is %s", __FUNCTION__, fileName.data());
        int fd = open(fileName.data(), O_RDWR | O_CREAT, 0666);
        if (fd != -1) {
            mPartialResultBuffers[0].metaBuf->dump(fd, 2);
        } else {
            LOGE("dumpResult: open failed, errmsg: %s\n", strerror(errno));
        }
        close(fd);
    }
}

void
Camera3Request::deInit()
{
    std::lock_guard<std::mutex> l(mAccessLock);
    mOutBuffers.clear();
    mInBuffers.clear();
    mInStreams.clear();
    mOutStreams.clear();
    mInitialized = false;
    mMembers.mSettings.clear();
    mSettings.clear();
    mOutputBuffers.clear();
    mInputBuffer.reset();
    mBuffersPerFormat.clear();
    CLEAR(mRequest3);
}

status_t
Camera3Request::init(camera3_capture_request* req,
                     IRequestCallback* cb,
                     CameraMetadata &settings, int cameraId)
{
    status_t status = NO_ERROR;
    PERFORMANCE_HAL_ATRACE_PARAM1("reqId", req->frame_number);
    LOGD("@%s req, framenum:%d, inputbuf:%p, outnum:%d, outputbuf:%p",
                __FUNCTION__, req->frame_number, req->input_buffer,
                req->num_output_buffers, req->output_buffers);

    if (CC_UNLIKELY(cb == nullptr)) {
        LOGE("@%s: Invalid callback object", __FUNCTION__);
        return BAD_VALUE;
    }
    /**
     * clean everything before we start
     */
    deInit();

    /**
     * initialize the partial metadata result buffers
     */
    status = initPartialResultBuffers(cameraId);
    if (CC_UNLIKELY(status != NO_ERROR)) {
        LOGE("@%s: failed to initialize partial results", __FUNCTION__);
        return NO_INIT;
    }
    const camera3_stream_buffer * buffer = req->output_buffers;

    if (CC_UNLIKELY(req->num_output_buffers > MAX_NUMBER_OUTPUT_STREAMS)) {
        LOGE("Too many output buffers for this request %dm max is %d",
                req->num_output_buffers, MAX_NUMBER_OUTPUT_STREAMS);
        return BAD_VALUE;
    }

    std::unique_lock<std::mutex> l(mAccessLock);

    for (uint32_t i = 0; i < req->num_output_buffers; i++) {
        LOGD("@%s, req, width:%d, stream type:0x%x", __FUNCTION__,
                                                   buffer->stream->width,
                                                   buffer->stream->stream_type);

        mOutputBufferPool[i]->setRequestId(req->frame_number);
        status = mOutputBufferPool[i]->init(buffer, cameraId);
        if (status != NO_ERROR) {
            LOGE("init output buffer fail");
            l.unlock();
            deInit();
            return BAD_VALUE;
        }
        mOutputBuffers.push_back(mOutputBufferPool[i]);

        /*
         * Keep track of the number buffers per format
         */
        std::map<int, int>::const_iterator it;
        it = mBuffersPerFormat.find(buffer->stream->format);
        if (it == mBuffersPerFormat.end()) {
            mBuffersPerFormat.insert(std::pair<int, int>(buffer->stream->format, 0));
        }

        int typeCount = mBuffersPerFormat.at(buffer->stream->format);
        mBuffersPerFormat.at(buffer->stream->format) = ++typeCount;

        mOutBuffers.push_back(*buffer);
        mOutBuffers.at(i).release_fence = -1;

        CameraStream *stream = reinterpret_cast<CameraStream *>(buffer->stream->priv);
        if (stream)
            stream->incOutBuffersInHal();

        buffer++;
    }
    if (req->input_buffer) {
        if (!mInputBuffer.get())
            mInputBuffer = std::make_shared<CameraBuffer>();
        status = mInputBuffer->init(req->input_buffer, cameraId);
        if (status != NO_ERROR) {
            LOGE("init input buffer fail");
            l.unlock();
            deInit();
            return BAD_VALUE;
        }
        mInBuffers.push_back(*(req->input_buffer));
    }

    status = checkInputStreams(req);
    status |= checkOutputStreams(req);
    if (status != NO_ERROR) {
        LOGE("error with the request's buffers");
        l.unlock();
        deInit();
        return BAD_VALUE;
    }

    mRequestId = req->frame_number;
    mCameraId = cameraId;
    mRequest3 = *req;
    mCallback = cb;
    mSettings = settings;   // Read only setting metadata buffer
    mInitialized = true;
    mError = false;
    mMetadtaFilled = false;
    LOGD("<Request %d> camera id %d successfully initialized", mRequestId, mCameraId);
    return NO_ERROR;
}

int Camera3Request::getBufferCountOfFormat(int format) const
{
    std::map<int, int>::const_iterator it;

    it = mBuffersPerFormat.find(format);
    if (it == mBuffersPerFormat.end()) {
        return 0;
    }

    return it->second;
}

/**
 * getNumberOutputBufs()
 *
 * returns the number of output buffers that this request has attached.
 * This determines how many buffers need to be returned to the client
 */
unsigned int
Camera3Request::getNumberOutputBufs()
{
    std::lock_guard<std::mutex> l(mAccessLock);
    return mInitialized ? mOutBuffers.size() : 0;
}

/**
 * getNumberInputBufs()
 *
 * returns the number of input buffers that this request has attached.
 */
unsigned int
Camera3Request::getNumberInputBufs()
{
    std::lock_guard<std::mutex> l(mAccessLock);
    return mInitialized ? mInBuffers.size() : 0;
}

int
Camera3Request::getId()
{
    std::lock_guard<std::mutex> l(mAccessLock);
    return mInitialized ? mRequestId : -1;
}

bool
Camera3Request::isAnyBufActive()
{
    if(!mInitialized) {
        ALOGD("@%s not init, return!", __FUNCTION__);
        return false;
    }
    for (auto it : mOutputBuffers) {
        if(it->isfenceActive() == true)
            return true;
    }
    return false;
}

int
Camera3Request::waitAllBufsSignaled()
{
    PERFORMANCE_ATRACE_CALL();
    LOGD("@%s : reqId %d", __FUNCTION__, mRequestId);
    int ret = 0;
    for (auto it : mOutputBuffers) {
        ret = it->fenceWait();
        CheckError(ret != 0, ret;, "@%s, wait buffer fence signaled failed for req %d",
                       __FUNCTION__, mRequestId);
    }
    LOGD("@%s : done for reqId %d", __FUNCTION__, mRequestId);
    return ret;
}

const std::vector<CameraStreamNode*>*
Camera3Request::getInputStreams()
{
    return mInitialized ? &mInStreams : nullptr;
}

const std::vector<CameraStreamNode*>*
Camera3Request::getOutputStreams()
{
    return mInitialized ? &mOutStreams : nullptr;
}

const std::vector<camera3_stream_buffer>*
Camera3Request::getOutputBuffers()
{
    std::lock_guard<std::mutex> l(mAccessLock);
    return mInitialized ? &mOutBuffers : nullptr;
}

const std::vector<camera3_stream_buffer>*
Camera3Request::getInputBuffers()
{
    std::lock_guard<std::mutex> l(mAccessLock);
    return mInitialized ? &mInBuffers : nullptr;
}

/**
 * getPartialResultBuffer
 *
 * PSL implementations that produce metadata buffers in several chunks will
 * call this method to acquire its own metadata buffer. Coordination on
 * the usage of those buffers is responsibility of the PSL
 */
CameraMetadata*
Camera3Request::getPartialResultBuffer(unsigned int index)
{
    if (CC_UNLIKELY(mPartialResultBuffers.empty() ||
                   (index > mPartialResultBuffers.size()-1))) {
        LOGE("Requesting a partial buffer that does not exist");
        return nullptr;
    }

    return mPartialResultBuffers[index].metaBuf;
}

void
Camera3Request::notifyFinalmetaFilled()
{
    std::unique_lock<std::mutex> l(mResultLock);
    mMetadtaFilled = true;
    mCondition.notify_all();
}

/**
 * getAndWaitforFilledResults
 * the final result metadata, check if you really need the final result or
 * only wants to get the result meta instance. the func now only used
 * in JpegEncodeTask::readExifInfoFromAndroidResult.
 */
CameraMetadata*
Camera3Request::getAndWaitforFilledResults(unsigned int index)
{
    std::unique_lock<std::mutex> l(mResultLock);
    if (mMetadtaFilled == false)
    {
        if(mCondition.wait_for(l, std::chrono::seconds(1)) == std::cv_status::timeout) {
            LOGW("@%s %d: request %d wait for CLmetadataFilled timeout", __FUNCTION__, __LINE__, mRequestId);
        } else {
            LOGI("@%s %d: request %d wait for CLmetadataFilled ", __FUNCTION__, __LINE__, mRequestId);
        }
    }

    return getPartialResultBuffer(index);
}

/**
 * getSettings
 *
 * returns the pointer to the read-only metadata buffer with the settings
 * for this request.
 */
const CameraMetadata*
Camera3Request::getSettings() const
{
    return mInitialized? &mSettings : nullptr;
}

/******************************************************************************
 *  PRIVATE PARTS
 *****************************************************************************/

/**
 * Check that the input buffers are associated to a known input stream
 * A known input stream is the one that the private field
 * is initialized with a pointer to the corresponding CameraStream object
 */
status_t
Camera3Request::checkInputStreams(camera3_capture_request * request3)
{
    camera3_stream_t* stream = nullptr;
    CameraStreamNode* streamNode = nullptr;


    if (request3->input_buffer != nullptr) {
        stream = request3->input_buffer->stream;
        if (stream->stream_type != CAMERA3_STREAM_INPUT
           && stream->stream_type != CAMERA3_STREAM_BIDIRECTIONAL) {
            LOGE("%s: Request %d: Input buffer not from input stream!",
                    __FUNCTION__, request3->frame_number);
            return BAD_VALUE;
        }
        if (stream->priv == nullptr) {
            LOGE("Input Stream not configured");
            return BAD_VALUE;
        }
        streamNode = reinterpret_cast<CameraStreamNode *>(stream->priv);
        mInStreams.push_back(streamNode);
    }
    return NO_ERROR;
}

/**
 * findBuffer
 * return a buffer associated with the stream passes as parameter
 * in this request
 * \param stream: CameraStream that we want to find buffers for
 * \param warn boolean indicating if warning should be printed, if not found
 * \return: sp to the CameraBuffer object
 */
std::shared_ptr<CameraBuffer>
Camera3Request::findBuffer(CameraStreamNode *stream, bool warn)
{
    for (size_t i = 0; i< mOutputBuffers.size(); i++) {
        if (mOutputBuffers[i]->getOwner() == stream)
            return mOutputBuffers[i];
    }
    if (mInputBuffer != nullptr && mInputBuffer->getOwner() == stream)
        return mInputBuffer;

    if (warn)
        LOGW("could not find requested buffer. invalid stream?");

    return nullptr;
}

/**
 * Check whether the aBuffer is an input buffer for the request
 */
bool
Camera3Request::isInputBuffer(std::shared_ptr<CameraBuffer> buffer)
{
    return mInputBuffer != nullptr && buffer.get() == mInputBuffer.get();
}

/**
 * Check that the output buffers belong to a known stream
 */
status_t
Camera3Request::checkOutputStreams(camera3_capture_request * request3)
{
    camera3_stream_t* stream = nullptr;
    CameraStream* s = nullptr;
    const camera3_stream_buffer * buffer = request3->output_buffers;

    for (uint32_t j = 0; j < request3->num_output_buffers; j++) {
        stream = buffer->stream;
        if (stream->priv == nullptr) {
            LOGE("%s no output stream.", __FUNCTION__);
            return BAD_VALUE;
        }
        s = (CameraStream *)stream->priv;
        CameraStream* tmpStream = nullptr;
        unsigned int i = 0;
        bool hasFlag = false;
        for (i = 0; i < mOutStreams.size(); i++) {
             tmpStream = static_cast<CameraStream*>(mOutStreams.at(i));
             if (tmpStream == s) {
                 hasFlag = true;
                 break;
             }
             if (s->width()*s->height() > tmpStream->width()*tmpStream->height()) {
                 mOutStreams.insert(mOutStreams.begin() + i, s);
                 break;
             } else if ((s->width()*s->height() == tmpStream->width()*tmpStream->height())
                         && (s->seqNo() < tmpStream->seqNo())) {
                 mOutStreams.insert(mOutStreams.begin() + i, s);
                 break;
             }
        }
        if ((tmpStream == nullptr || i == mOutStreams.size()) && !hasFlag)
             mOutStreams.push_back(s);
        buffer++;
    }
    return NO_ERROR;
}

/**
 * initPartialResultBuffers
 *
 * Initialize the buffers that will store the partial results for each request
 * The initialization has 2 phases:
 * - Allocation: this is done only once in the lifetime of the request
 * - Reset: this is done for all initialization. It means to clear the buffers
 * where the result metadata is stored.
 * The number of partial results is PSL specific and is queried via PlatformData
 * Different cameraId may use different PSL
 */
status_t
Camera3Request::initPartialResultBuffers(int cameraId)
{
    status_t status = NO_ERROR;

    if (CC_UNLIKELY(!mResultBufferAllocated)) {
        int partialBufferCount = PlatformData::getPartialMetadataCount(cameraId);
        status = allocatePartialResultBuffers(partialBufferCount);
        if (CC_UNLIKELY(status != NO_ERROR)) {
            return status;
        }
    }
    // Reset the metadata buffers
    camera_metadata_t *m;
    for (unsigned int i = 0; i < mPartialResultBuffers.size(); i++) {
        if (mPartialResultBuffers[i].baseBuf != nullptr) {
            m = mPartialResultBuffers.at(i).metaBuf->release();
            /**
             * It may happen that a PSL may resize the result buffer if the
             * originally allocated is not big enough. Check for it
             */
            if (CC_UNLIKELY(m != mPartialResultBuffers[i].baseBuf)) {
                if (m == nullptr) {
                    LOGE("Cannot get metadata from result buffers.");
                    return UNKNOWN_ERROR;
                }
                LOGW("PSL resized result buffer (%d) in request %p", i, this);
                reAllocateResultBuffer(m, i);
            }

            memset(mPartialResultBuffers[i].baseBuf, 0 ,
                   mPartialResultBuffers[i].size);

            /* This should not fail since it worked first time when we
             * allocated */
           m = place_camera_metadata(mPartialResultBuffers[i].baseBuf,
                                     mPartialResultBuffers[i].size,
                                     mPartialResultBuffers[i].entryCap,
                                     mPartialResultBuffers[i].dataCap);
           mPartialResultBuffers.at(i).metaBuf->acquire(m);
        }
    }
    return status;
}

/**
 * reAllocateResultBuffer
 *
 * In situations when the PSL needs to re-size the result buffer we need to
 * re-allocate the result buffer to regain the ownership of the memory
 * In that case we free the metadata buffer allocated by the framework during
 * resize and we re-allocate it ourselves.
 *
 * \param m[IN]: metadata buffer allocated by the framework
 * \param index[IN]: index in the mPartialResultBuffer vector.
 */
void Camera3Request::reAllocateResultBuffer(camera_metadata_t* m, int index)
{
    MemoryManagedMetadata &mmdata = mPartialResultBuffers.at(index);

    mmdata.size = get_camera_metadata_size(m);
    mmdata.dataCap = get_camera_metadata_data_capacity(m);
    mmdata.entryCap = get_camera_metadata_entry_capacity(m);
    free_camera_metadata(m);
    if (mmdata.baseBuf) {
        delete [] reinterpret_cast<char*>(mmdata.baseBuf);
    }
    mmdata.baseBuf = (void*) new char[mmdata.size];
    LOGI("Need to resize meta result buffers to %zu entry cap %d, data cap %d"
            ,mmdata.size, mmdata.entryCap, mmdata.dataCap);
}

/*
 * allocatePartialResultBuffers
 * Allocates the raw buffers that will be used to store the result metadata
 * buffers. The memory of these metadata buffers is managed by this class
 * so that we do not need to re-allocate the buffers for each request
 *
 *  This allows us to clean the metadata buffer without having to re-allocate.
 */
status_t
Camera3Request::allocatePartialResultBuffers(int partialResultCount)
{
    MemoryManagedMetadata  mmdata;
    size_t bufferSize = calculate_camera_metadata_size(RESULT_ENTRY_CAP,
                                                       RESULT_DATA_CAP);
    for (int i = 0; i < partialResultCount; i++) {
        CLEAR(mmdata);
        camera_metadata_t *m;
        mmdata.baseBuf = (void*) new char[bufferSize];

        m = place_camera_metadata(mmdata.baseBuf, bufferSize, RESULT_ENTRY_CAP,
                                  RESULT_DATA_CAP);
        if (m == nullptr) {
            LOGE("Failed to allocate memory for result metadata buffer");
            goto bailout;
        }
        mmdata.metaBuf = new CameraMetadata(m);
        mmdata.size = bufferSize;
        mmdata.entryCap = RESULT_ENTRY_CAP;
        mmdata.dataCap = RESULT_DATA_CAP;
        mPartialResultBuffers.push_back(mmdata);
    }

    mResultBufferAllocated = true;
    return NO_ERROR;

bailout:
    freePartialResultBuffers();
    return NO_MEMORY;
}

void
Camera3Request::freePartialResultBuffers(void)
{
    for (unsigned int i = 0; i < mPartialResultBuffers.size(); i++) {
        if (mPartialResultBuffers[i].baseBuf != nullptr)
            mPartialResultBuffers[i].metaBuf->release();
            delete[] reinterpret_cast<char*>(mPartialResultBuffers[i].baseBuf);
            delete mPartialResultBuffers[i].metaBuf;
    }
    mPartialResultBuffers.clear();
    mResultBufferAllocated = false;
}

} NAMESPACE_DECLARATION_END