/*
 * x3a_analyzer_aiq.h - 3a analyzer from AIQ
 *
 *  Copyright (c) 2014-2015 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: Wind Yuan <feng.yuan@intel.com>
 */

#include "x3a_analyzer_aiq.h"
#include "aiq_handler.h"
#include "isp_controller.h"
#include "xcam_cpf_reader.h"
#include "ia_types.h"

namespace XCam {

class CpfReader {
public:
    explicit CpfReader (const char *name);
    ~CpfReader();
    bool read (ia_binary_data &binary);
private:
    XCAM_DEAD_COPY (CpfReader);

private:
    XCamCpfBlob *_aiq_cpf;
    char *_name;
};

CpfReader::CpfReader (const char *name)
    : _name (strndup(name, XCAM_MAX_STR_SIZE))
{
    _aiq_cpf = xcam_cpf_blob_new ();
    XCAM_ASSERT (name);
}
CpfReader::~CpfReader()
{
    if (_aiq_cpf)
        xcam_cpf_blob_free (_aiq_cpf);
    if (_name)
        xcam_free (_name);
}

bool CpfReader::read (ia_binary_data &binary)
{
    if (!xcam_cpf_read (_name, _aiq_cpf, NULL)) {
        XCAM_LOG_ERROR ("parse CPF(%s) failed", XCAM_STR (_name));
        return false;
    }
    binary.data  = _aiq_cpf->data;
    binary.size = _aiq_cpf->size;
    XCAM_LOG_INFO ("read cpf(%s) ok", XCAM_STR (_name));
    return true;
}

X3aAnalyzerAiq::X3aAnalyzerAiq (SmartPtr<IspController> &isp, const char *cpf_path)
    : X3aAnalyzer ("X3aAnalyzerAiq")
    , _isp (isp)
    , _sensor_data_ready (false)
    , _cpf_path (NULL)
{
    if (cpf_path)
        _cpf_path = strndup (cpf_path, XCAM_MAX_STR_SIZE);

    _aiq_compositor = new AiqCompositor ();
    XCAM_ASSERT (_aiq_compositor.ptr());
    xcam_mem_clear (_sensor_mode_data);

    XCAM_LOG_DEBUG ("X3aAnalyzerAiq constructed");
}

X3aAnalyzerAiq::X3aAnalyzerAiq (struct atomisp_sensor_mode_data &sensor_data, const char *cpf_path)
    : X3aAnalyzer ("X3aAnalyzerAiq")
    , _sensor_mode_data (sensor_data)
    , _sensor_data_ready (true)
    , _cpf_path (NULL)
{
    if (cpf_path)
        _cpf_path = strndup (cpf_path, XCAM_MAX_STR_SIZE);

    _aiq_compositor = new AiqCompositor ();
    XCAM_ASSERT (_aiq_compositor.ptr());

    XCAM_LOG_DEBUG ("X3aAnalyzerAiq constructed");
}

X3aAnalyzerAiq::~X3aAnalyzerAiq()
{
    if (_cpf_path)
        xcam_free (_cpf_path);

    XCAM_LOG_DEBUG ("~X3aAnalyzerAiq destructed");
}

SmartPtr<AeHandler>
X3aAnalyzerAiq::create_ae_handler ()
{
    SmartPtr<AiqAeHandler> ae_handler = new AiqAeHandler (_aiq_compositor);
    _aiq_compositor->set_ae_handler (ae_handler);
    return ae_handler;
}

SmartPtr<AwbHandler>
X3aAnalyzerAiq::create_awb_handler ()
{
    SmartPtr<AiqAwbHandler> awb_handler = new AiqAwbHandler (_aiq_compositor);
    _aiq_compositor->set_awb_handler (awb_handler);
    return awb_handler;
}

SmartPtr<AfHandler>
X3aAnalyzerAiq::create_af_handler ()
{

    SmartPtr<AiqAfHandler> af_handler = new AiqAfHandler (_aiq_compositor);
    _aiq_compositor->set_af_handler (af_handler);
    return af_handler;
}

SmartPtr<CommonHandler>
X3aAnalyzerAiq::create_common_handler ()
{
    SmartPtr<AiqCommonHandler> common_handler = new AiqCommonHandler (_aiq_compositor);
    _aiq_compositor->set_common_handler (common_handler);
    return common_handler;
}

XCamReturn
X3aAnalyzerAiq::internal_init (uint32_t width, uint32_t height, double framerate)
{
    XCAM_ASSERT (_cpf_path);
    CpfReader reader (_cpf_path);
    ia_binary_data binary;

    XCAM_ASSERT (_aiq_compositor.ptr ());

    _aiq_compositor->set_framerate (framerate);

    xcam_mem_clear (binary);
    XCAM_FAIL_RETURN (
        ERROR,
        reader.read(binary),
        XCAM_RETURN_ERROR_AIQ,
        "read cpf file(%s) failed", _cpf_path);

    _aiq_compositor->set_size (width, height);
    XCAM_FAIL_RETURN (
        ERROR,
        _aiq_compositor->open (binary),
        XCAM_RETURN_ERROR_AIQ,
        "AIQ open failed");

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn
X3aAnalyzerAiq::internal_deinit ()
{
    if (_aiq_compositor.ptr ())
        _aiq_compositor->close ();

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn
X3aAnalyzerAiq::configure_3a ()
{
    XCamReturn ret = XCAM_RETURN_NO_ERROR;
    X3aResultList first_results;

    if (!_sensor_data_ready) {
        struct atomisp_sensor_mode_data sensor_mode_data;
        xcam_mem_clear (sensor_mode_data);
        XCAM_ASSERT (_isp.ptr());

        ret = _isp->get_sensor_mode_data (sensor_mode_data);
        XCAM_FAIL_RETURN (WARNING, ret == XCAM_RETURN_NO_ERROR, ret, "get sensor mode data failed");
        _sensor_mode_data = sensor_mode_data;
        _sensor_data_ready = true;
    }

    if (!_aiq_compositor->set_sensor_mode_data (&_sensor_mode_data)) {
        XCAM_LOG_WARNING ("AIQ configure 3a failed");
        return XCAM_RETURN_ERROR_AIQ;
    }

    XCAM_LOG_DEBUG ("X3aAnalyzerAiq got sensor mode data, coarse_time_min:%u, "
                    "coarse_time_max_margin:%u, "
                    "fine_time_min:%u, fine_time_max_margin:%u, "
                    "fine_time_def:%u, "
                    "frame_length_lines:%u, line_length_pck:%u, "
                    "vt_pix_clk_freq_mhz:%u, "
                    "crop_horizontal_start:%u, crop_vertical_start:%u, "
                    "crop_horizontal_end:%u, crop_vertical_end:%u, "
                    "output_width:%u, output_height:%u, "
                    "binning_factor_x:%u, binning_factor_y:%u",
                    _sensor_mode_data.coarse_integration_time_min,
                    _sensor_mode_data.coarse_integration_time_max_margin,
                    _sensor_mode_data.fine_integration_time_min,
                    _sensor_mode_data.fine_integration_time_max_margin,
                    _sensor_mode_data.fine_integration_time_def,
                    _sensor_mode_data.frame_length_lines,
                    _sensor_mode_data.line_length_pck,
                    _sensor_mode_data.vt_pix_clk_freq_mhz,
                    _sensor_mode_data.crop_horizontal_start,
                    _sensor_mode_data.crop_vertical_start,
                    _sensor_mode_data.crop_horizontal_end,
                    _sensor_mode_data.crop_vertical_end,
                    _sensor_mode_data.output_width,
                    _sensor_mode_data.output_height,
                    (uint32_t)_sensor_mode_data.binning_factor_x,
                    (uint32_t)_sensor_mode_data.binning_factor_y);

    // initialize ae and awb
    get_ae_handler ()->analyze (first_results);
    get_awb_handler ()->analyze (first_results);

    ret = _aiq_compositor->integrate (first_results);
    XCAM_FAIL_RETURN (WARNING, ret == XCAM_RETURN_NO_ERROR, ret, "AIQ configure_3a failed on integrate results");

    if (!first_results.empty()) {
        notify_calculation_done (first_results);
    }

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn
X3aAnalyzerAiq::pre_3a_analyze (SmartPtr<X3aStats> &stats)
{
    SmartPtr<X3aIspStatistics> isp_stats = stats.dynamic_cast_ptr<X3aIspStatistics> ();

    XCAM_ASSERT (isp_stats.ptr ());
    if (!_aiq_compositor->set_3a_stats (isp_stats)) {
        XCAM_LOG_WARNING ("Aiq compositor set 3a stats failed");
        return XCAM_RETURN_ERROR_UNKNOWN;
    }

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn
X3aAnalyzerAiq::post_3a_analyze (X3aResultList &results)
{
    XCamReturn ret = XCAM_RETURN_NO_ERROR;

    ret = _aiq_compositor->integrate (results);
    XCAM_FAIL_RETURN (WARNING, ret == XCAM_RETURN_NO_ERROR, ret, "AIQ integrate 3A results failed");

    return XCAM_RETURN_NO_ERROR;
}

};
