/*
 * xcam_plugin_dvs.cpp - Digital Video Stabilizer plugin
 *
 *  Copyright (c) 2014-2016 Intel Corporation
 *
 * 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.
 *
 * Author: Zong Wei <wei.zong@intel.com>
 */
#include <base/xcam_common.h>
#include <base/xcam_smart_description.h>
#include <base/xcam_smart_result.h>
#include <base/xcam_3a_result.h>
#include <base/xcam_buffer.h>

#include <smartptr.h>
#if HAVE_LIBDRM
#include <drm_display.h>
#endif
#include <dma_video_buffer.h>

#include <ocl/cl_utils.h>
#include <ocl/cl_context.h>
#include <ocl/cl_device.h>
#include <ocl/cl_memory.h>

#include <opencv2/core/ocl.hpp>

#include "libdvs/libdvs.h"

#define DVS_MOTION_FILTER_RADIUS   15

struct DvsBuffer : public DvsData
{
    XCamVideoBuffer* buffer;

    DvsBuffer () { }

    DvsBuffer (XCamVideoBuffer* buf, cv::UMat& frame)
        : buffer (buf)
    {
        buffer->ref(buffer);
        data = frame;
    }

    ~DvsBuffer () {
        buffer->unref(buffer);
    }
};

XCamReturn dvs_create_context(XCamSmartAnalysisContext **context, uint32_t *async_mode, XcamPostResultsFunc post_func)
{
    XCAM_UNUSED (async_mode);
    XCAM_UNUSED (post_func);

    DvsInterface* theDVS = NULL;

    theDVS = getDigitalVideoStabilizer();
    if (theDVS == NULL) {
        return XCAM_RETURN_ERROR_MEM;
    }
    theDVS->init(640, 480, false);

    *context = (XCamSmartAnalysisContext *)theDVS;

    cl_platform_id platform_id = XCam::CLDevice::instance()->get_platform_id();
    char* platform_name = XCam::CLDevice::instance()->get_platform_name ();
    cl_device_id device_id = XCam::CLDevice::instance()->get_device_id();
    cl_context cl_context_id = XCam::CLDevice::instance()->get_context()->get_context_id();

    clRetainContext (cl_context_id);
    cv::ocl::attachContext (platform_name, platform_id, cl_context_id, device_id);

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn dvs_destroy_context(XCamSmartAnalysisContext *context)
{
    DvsInterface *theDVS = (DvsInterface *)context;

    theDVS->release ();

    delete (theDVS);

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn dvs_update_params(XCamSmartAnalysisContext *context, const XCamSmartAnalysisParam *params)
{
    XCAM_UNUSED (context);
    XCAM_UNUSED (params);

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn dvs_analyze(XCamSmartAnalysisContext *context, XCamVideoBuffer *buffer, XCam3aResultHead *results[], uint32_t *res_count)
{
    DvsInterface *theDVS = (DvsInterface *)context;
    DvsResult dvsResult;

    if (buffer->info.format != V4L2_PIX_FMT_NV12 || buffer->mem_type != XCAM_MEM_TYPE_PRIVATE_BO)
        return XCAM_RETURN_ERROR_PARAM;

    int buffer_fd = xcam_video_buffer_get_fd(buffer);
    XCam::VideoBufferInfo buffer_info;
    buffer_info.init (buffer->info.format, buffer->info.width, buffer->info.height,
                      buffer->info.aligned_width, buffer->info.aligned_height, buffer->info.size);
    XCam::SmartPtr<XCam::VideoBuffer> new_buffer = new XCam::DmaVideoBuffer(buffer_info, buffer_fd);

    XCam::SmartPtr<XCam::VideoBuffer> video_buffer;
#if HAVE_LIBDRM
    XCam::SmartPtr<XCam::DrmDisplay> display = XCam::DrmDisplay::instance ();
    video_buffer = display->convert_to_drm_bo_buf (display, new_buffer);
#else
    video_buffer = new_buffer;
#endif

    XCam::SmartPtr<XCam::CLContext> cl_Context = XCam::CLDevice::instance()->get_context();
    XCam::SmartPtr<XCam::CLBuffer> cl_buffer = XCam::convert_to_clbuffer (cl_Context, video_buffer);
    cl_mem cl_mem_id = cl_buffer->get_mem_id();

    clRetainMemObject(cl_mem_id);
    cv::UMat frame;
    cv::ocl::convertFromBuffer(cl_mem_id, buffer->info.strides[0], buffer->info.height, buffer->info.width, CV_8U, frame);

    DvsBuffer* dvs_buf = new DvsBuffer(buffer, frame);
    //set default config
    DvsConfig config;
    config.use_ocl  = true;
    config.frame_width = buffer->info.width;
    config.frame_height = buffer->info.height;
    config.radius = DVS_MOTION_FILTER_RADIUS;
    config.stdev = 10.0f;
    config.features = 1000;
    config.minDistance = 20.0f;

    theDVS->setConfig(&config);

    theDVS->nextStabilizedMotion(dvs_buf, &dvsResult);

    delete(dvs_buf);

    if ((dvsResult.frame_id < 0) && (dvsResult.valid == false))
    {
        results[0] = NULL;
        *res_count = 0;
        XCAM_LOG_WARNING ("dvs_analyze not ready! ");
    } else {
        XCamDVSResult *dvs_result = (XCamDVSResult *)malloc(sizeof(XCamDVSResult));
        if (!dvs_result) {
            XCAM_LOG_ERROR ("dvs_result: malloc failed!");
            results[0] = NULL;
            *res_count = 0;

            return XCAM_RETURN_ERROR_MEM;
        }

        memset(dvs_result, 0, sizeof(XCamDVSResult));

        dvs_result->head.type = XCAM_3A_RESULT_DVS;
        dvs_result->head.process_type = XCAM_IMAGE_PROCESS_POST;
        dvs_result->head.version = 0x080;
        dvs_result->frame_id = dvsResult.frame_id;
        dvs_result->frame_width = dvsResult.frame_width;
        dvs_result->frame_height = dvsResult.frame_height;
        memcpy(dvs_result->proj_mat, dvsResult.proj_mat, sizeof(DvsResult::proj_mat));

        results[0] = (XCam3aResultHead *)dvs_result;
        *res_count = 1;
    }

    return XCAM_RETURN_NO_ERROR;
}

void dvs_free_results(XCamSmartAnalysisContext *context, XCam3aResultHead *results[], uint32_t res_count)
{
    XCAM_UNUSED (context);
    for (uint32_t i = 0; i < res_count; ++i) {
        if (results[i]) {
            free (results[i]);
        }
    }
}

XCAM_BEGIN_DECLARE

XCamSmartAnalysisDescription xcam_smart_analysis_desciption =
{
    0x080,
    sizeof (XCamSmartAnalysisDescription),
    10,
    "digital_video_stabilizer",
    dvs_create_context,
    dvs_destroy_context,
    dvs_update_params,
    dvs_analyze,
    dvs_free_results,
};

XCAM_END_DECLARE

