/*
 * aiq_handler.cpp - AIQ handler
 *
 *  Copyright (c) 2012-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>
 * Author: Yan Zhang <yan.y.zhang@intel.com>
 */

#include "aiq_handler.h"
#include "x3a_isp_config.h"

#include <string.h>
#include <math.h>

#include "ia_isp_2_2.h"

#define MAX_STATISTICS_WIDTH 150
#define MAX_STATISTICS_HEIGHT 150

//#define USE_RGBS_GRID_WEIGHTING
#define USE_HIST_GRID_WEIGHTING

namespace XCam {

struct IspInputParameters {
    ia_aiq_frame_use            frame_use;
    ia_aiq_frame_params        *sensor_frame_params;
    ia_aiq_exposure_parameters *exposure_results;
    ia_aiq_awb_results         *awb_results;
    ia_aiq_gbce_results        *gbce_results;
    ia_aiq_pa_results          *pa_results;
#ifdef HAVE_AIQ_2_7
    ia_aiq_sa_results          *sa_results;
#endif
    int8_t                      manual_brightness;
    int8_t                      manual_contrast;
    int8_t                      manual_hue;
    int8_t                      manual_saturation;
    int8_t                      manual_sharpness;
    int8_t                      manual_nr_level;
    ia_isp_effect               effects;

    IspInputParameters ()
        : frame_use (ia_aiq_frame_use_preview)
        , sensor_frame_params (NULL)
        , exposure_results (NULL)
        , awb_results (NULL)
        , gbce_results (NULL)
        , pa_results (NULL)
#ifdef HAVE_AIQ_2_7
        , sa_results (NULL)
#endif
        , manual_brightness (0)
        , manual_contrast (0)
        , manual_hue (0)
        , manual_saturation (0)
        , manual_sharpness (0)
        , manual_nr_level (0)
        , effects (ia_isp_effect_none)
    {}
};

class IaIspAdaptor22
    : public IaIspAdaptor
{
public:
    IaIspAdaptor22 () {
        xcam_mem_clear (_input_params);
    }
    ~IaIspAdaptor22 () {
        if (_handle)
            ia_isp_2_2_deinit (_handle);
    }

    virtual bool init (
        const ia_binary_data *cpf,
        unsigned int max_width,
        unsigned int max_height,
        ia_cmc_t *cmc,
        ia_mkn *mkn);

    virtual bool convert_statistics (
        void *statistics,
        ia_aiq_rgbs_grid **out_rgbs_grid,
        ia_aiq_af_grid **out_af_grid);

    virtual bool run (
        const IspInputParameters *isp_input_params,
        ia_binary_data *output_data);

private:
    ia_isp_2_2_input_params  _input_params;

};

bool
IaIspAdaptor22::init (
    const ia_binary_data *cpf,
    unsigned int max_width,
    unsigned int max_height,
    ia_cmc_t *cmc,
    ia_mkn *mkn)
{
    xcam_mem_clear (_input_params);
    _input_params.isp_vamem_type = 1;
    _handle = ia_isp_2_2_init (cpf, max_width, max_height, cmc, mkn);
    XCAM_FAIL_RETURN (ERROR, _handle, false, "ia_isp 2.2 init failed");
    return true;
}

bool
IaIspAdaptor22::convert_statistics (
    void *statistics,
    ia_aiq_rgbs_grid **out_rgbs_grid,
    ia_aiq_af_grid **out_af_grid)
{
    ia_err err;
    err = ia_isp_2_2_statistics_convert (_handle, statistics, out_rgbs_grid, out_af_grid);
    XCAM_FAIL_RETURN (ERROR, err == ia_err_none, false, "ia_isp 2.2 convert stats failed");
    return true;
}

bool
IaIspAdaptor22::run (
    const IspInputParameters *isp_input_params,
    ia_binary_data *output_data)
{
    ia_err err;

    _input_params.frame_use = isp_input_params->frame_use;
    _input_params.sensor_frame_params = isp_input_params->sensor_frame_params;
    _input_params.exposure_results = isp_input_params->exposure_results;
    _input_params.awb_results = isp_input_params->awb_results;
    _input_params.gbce_results = isp_input_params->gbce_results;
    _input_params.pa_results = isp_input_params->pa_results;
#ifdef HAVE_AIQ_2_7
    _input_params.sa_results = isp_input_params->sa_results;
#endif
    _input_params.manual_brightness = isp_input_params->manual_brightness;
    _input_params.manual_contrast = isp_input_params->manual_contrast;
    _input_params.manual_hue = isp_input_params->manual_hue;
    _input_params.manual_saturation = isp_input_params->manual_saturation;
    _input_params.nr_setting.feature_level = ia_isp_feature_level_high;
    _input_params.nr_setting.strength = isp_input_params->manual_nr_level;
    _input_params.ee_setting.feature_level = ia_isp_feature_level_high;
    _input_params.ee_setting.strength = isp_input_params->manual_sharpness;
    _input_params.effects = isp_input_params->effects;

    err = ia_isp_2_2_run (_handle, &_input_params, output_data);
    XCAM_FAIL_RETURN (ERROR, err == ia_err_none, false, "ia_isp 2.2 run failed");
    return true;
}

#if 0

class IaIspAdaptor15
    : public IaIspAdaptor
{
public:
    IaIspAdaptor15 () {
        xcam_mem_clear (&_input_params);
    }
    ~IaIspAdaptor15 () {
        if (_handle)
            ia_isp_1_5_deinit (_handle);
    }
    virtual bool init (
        const ia_binary_data *cpf,
        unsigned int max_width,
        unsigned int max_height,
        ia_cmc_t *cmc,
        ia_mkn *mkn);
    virtual bool convert_statistics (
        void *statistics,
        ia_aiq_rgbs_grid **out_rgbs_grid,
        ia_aiq_af_grid **out_af_grid);
    virtual bool run (
        const IspInputParameters *isp_input_params,
        ia_binary_data *output_data);

private:
    ia_isp_1_5_input_params  _input_params;

};

bool
IaIspAdaptor15::init (
    const ia_binary_data *cpf,
    unsigned int max_width,
    unsigned int max_height,
    ia_cmc_t *cmc,
    ia_mkn *mkn)
{
    xcam_mem_clear (&_input_params);
    _input_params.isp_vamem_type = 1;
    _handle = ia_isp_1_5_init (cpf, max_width, max_height, cmc, mkn);
    XCAM_FAIL_RETURN (ERROR, _handle, false, "ia_isp 1.5 init failed");
    return true;
}

bool
IaIspAdaptor15::convert_statistics (
    void *statistics,
    ia_aiq_rgbs_grid **out_rgbs_grid,
    ia_aiq_af_grid **out_af_grid)
{
    ia_err err;
    err = ia_isp_1_5_statistics_convert (_handle, statistics, out_rgbs_grid, out_af_grid);
    XCAM_FAIL_RETURN (ERROR, err == ia_err_none, false, "ia_isp 1.5 convert stats failed");
    return true;
}

bool
IaIspAdaptor15::run (
    const IspInputParameters *isp_input_params,
    ia_binary_data *output_data)
{
    ia_err err;

    _input_params.frame_use = isp_input_params->frame_use;
    _input_params.sensor_frame_params = isp_input_params->sensor_frame_params;
    _input_params.exposure_results = isp_input_params->exposure_results;
    _input_params.awb_results = isp_input_params->awb_results;
    _input_params.gbce_results = isp_input_params->gbce_results;
    _input_params.pa_results = isp_input_params->pa_results;
    _input_params.manual_brightness = isp_input_params->manual_brightness;
    _input_params.manual_contrast = isp_input_params->manual_contrast;
    _input_params.manual_hue = isp_input_params->manual_hue;
    _input_params.manual_saturation = isp_input_params->manual_saturation;
    _input_params.nr_setting.feature_level = ia_isp_feature_level_high;
    _input_params.nr_setting.strength = isp_input_params->manual_nr_level;
    _input_params.ee_setting.feature_level = ia_isp_feature_level_high;
    _input_params.ee_setting.strength = isp_input_params->manual_sharpness;
    _input_params.effects = isp_input_params->effects;

    err = ia_isp_1_5_run (_handle, &_input_params, output_data);
    XCAM_FAIL_RETURN (ERROR, err == ia_err_none, false, "ia_isp 1.5 run failed");
    return true;
}

#endif

static double
_calculate_new_value_by_speed (double start, double end, double speed)
{
    XCAM_ASSERT (speed >= 0.0 && speed <= 1.0);
    static const double value_equal_range = 0.000001;

    if (fabs (end - start) <= value_equal_range)
        return end;
    return (start * (1.0 - speed) + end * speed);
}

static double
_imx185_sensor_gain_code_to_mutiplier (uint32_t code)
{
    /* 185 sensor code : DB = 160 : 48 */
    double db;
    db = code * 48.0 / 160.0;
    return pow (10.0, db / 20.0);
}

static uint32_t
_mutiplier_to_imx185_sensor_gain_code (double mutiplier)
{
    double db = log10 (mutiplier) * 20;
    if (db > 48)
        db = 48;
    return (uint32_t) (db * 160 / 48);
}

static uint32_t
_time_to_coarse_line (const ia_aiq_exposure_sensor_descriptor *desc, uint32_t time_us)
{
    float value =  time_us * desc->pixel_clock_freq_mhz;

    value = (value + desc->pixel_periods_per_line / 2) / desc->pixel_periods_per_line;
    return (uint32_t)(value);
}

static uint32_t
_coarse_line_to_time (const ia_aiq_exposure_sensor_descriptor *desc, uint32_t coarse_line)
{
    return coarse_line * desc->pixel_periods_per_line / desc->pixel_clock_freq_mhz;
}

AiqAeHandler::AiqAeResult::AiqAeResult()
{
    xcam_mem_clear (ae_result);
    xcam_mem_clear (ae_exp_ret);
    xcam_mem_clear (aiq_exp_param);
    xcam_mem_clear (sensor_exp_param);
    xcam_mem_clear (weight_grid);
    xcam_mem_clear (flash_param);
}

void
AiqAeHandler::AiqAeResult::copy (ia_aiq_ae_results *result)
{
    XCAM_ASSERT (result);

    this->ae_result = *result;
    this->aiq_exp_param = *result->exposures[0].exposure;
    this->sensor_exp_param = *result->exposures[0].sensor_exposure;
    this->weight_grid = *result->weight_grid;
#ifdef HAVE_AIQ_2_7
    this->flash_param = result->flashes[0];
#else
    this->flash_param = *result->flash;
#endif

    this->ae_exp_ret.exposure = &this->aiq_exp_param;
    this->ae_exp_ret.sensor_exposure = &this->sensor_exp_param;
    this->ae_result.exposures = &this->ae_exp_ret;
    this->ae_result.weight_grid = &this->weight_grid;
#ifdef HAVE_AIQ_2_7
    this->ae_result.flashes[0] = this->flash_param;
#else
    this->ae_result.flash = &this->flash_param;
#endif
    this->ae_result.num_exposures = 1;
}

AiqAeHandler::AiqAeHandler (SmartPtr<AiqCompositor> &aiq_compositor)
    : _aiq_compositor (aiq_compositor)
    , _started (false)
{
    xcam_mem_clear (_ia_ae_window);
    xcam_mem_clear (_sensor_descriptor);
    xcam_mem_clear (_manual_limits);
    xcam_mem_clear (_input);
    _input.num_exposures = 1;
    _input.frame_use = _aiq_compositor->get_frame_use();
    _input.flash_mode = ia_aiq_flash_mode_off;
    _input.operation_mode = ia_aiq_ae_operation_mode_automatic;
    _input.metering_mode = ia_aiq_ae_metering_mode_evaluative;
    _input.priority_mode = ia_aiq_ae_priority_mode_normal;
    _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_auto;
    _input.sensor_descriptor = NULL;
    _input.exposure_window = NULL;
    _input.exposure_coordinate = NULL;
    _input.ev_shift = 0.0;
    _input.manual_exposure_time_us = -1;
    _input.manual_analog_gain = -1.0;
    _input.manual_iso = -1.0;
    _input.aec_features = NULL;
    _input.manual_limits = &_manual_limits;
}

bool
AiqAeHandler::set_description (struct atomisp_sensor_mode_data *sensor_data)
{
    XCAM_ASSERT (sensor_data);

    _sensor_descriptor.pixel_clock_freq_mhz = sensor_data->vt_pix_clk_freq_mhz / 1000000.0f;
    _sensor_descriptor.pixel_periods_per_line = sensor_data->line_length_pck;
    _sensor_descriptor.line_periods_per_field = sensor_data->frame_length_lines;
    _sensor_descriptor.line_periods_vertical_blanking = sensor_data->frame_length_lines
            - (sensor_data->crop_vertical_end - sensor_data->crop_vertical_start + 1)
            / sensor_data->binning_factor_y;
    _sensor_descriptor.fine_integration_time_min = sensor_data->fine_integration_time_def;
    _sensor_descriptor.fine_integration_time_max_margin = sensor_data->line_length_pck - sensor_data->fine_integration_time_def;
    _sensor_descriptor.coarse_integration_time_min = sensor_data->coarse_integration_time_min;
    _sensor_descriptor.coarse_integration_time_max_margin = sensor_data->coarse_integration_time_max_margin;

    return true;
}

bool
AiqAeHandler::ensure_ia_parameters ()
{
    bool ret = true;
    ret = ret && ensure_ae_mode ();
    ret = ret && ensure_ae_metering_mode ();
    ret = ret && ensure_ae_priority_mode ();
    ret = ret && ensure_ae_flicker_mode ();
    ret = ret && ensure_ae_manual ();
    ret = ret && ensure_ae_ev_shift ();
    _input.sensor_descriptor = &_sensor_descriptor;
    return ret;
}

bool AiqAeHandler::ensure_ae_mode ()
{
    XCamAeMode mode = this->get_mode_unlock();
    switch (mode) {
    case XCAM_AE_MODE_AUTO:
    case XCAM_AE_MODE_MANUAL:
        _input.operation_mode = ia_aiq_ae_operation_mode_automatic;
        break;

    case XCAM_AE_MODE_NOT_SET:
    default:
        XCAM_LOG_ERROR("unsupported ae mode:%d", mode);
        return false;
    }
    return true;
}
bool AiqAeHandler::ensure_ae_metering_mode ()
{
    XCamAeMeteringMode mode = this->get_metering_mode_unlock();

    _input.exposure_window = NULL;

    switch (mode) {
    case XCAM_AE_METERING_MODE_AUTO:
        _input.metering_mode = ia_aiq_ae_metering_mode_evaluative;
        break;
    case XCAM_AE_METERING_MODE_SPOT:
    {
        _input.metering_mode = ia_aiq_ae_metering_mode_evaluative;
        const XCam3AWindow & window = this->get_window_unlock();
        if (window.x_end > window.x_start &&
                window.y_end > window.y_start) {
            _aiq_compositor->convert_window_to_ia(window, _ia_ae_window);
            _input.exposure_window = &_ia_ae_window;
        }
    }
    break;
    case XCAM_AE_METERING_MODE_CENTER:
        _input.metering_mode = ia_aiq_ae_metering_mode_center;
        break;
    case XCAM_AE_METERING_MODE_WEIGHTED_WINDOW:
    {
        _input.metering_mode = ia_aiq_ae_metering_mode_evaluative;
        const XCam3AWindow & weighted_window = this->get_window_unlock();

        XCAM_LOG_DEBUG ("ensure_ae_metering_mode weighted_window x_start = %d, y_start = %d, x_end = %d, y_end = %d ",
                        weighted_window.x_start, weighted_window.y_start, weighted_window.x_end, weighted_window.y_end);

        if (weighted_window.x_end > weighted_window.x_start &&
                weighted_window.y_end > weighted_window.y_start) {
            _aiq_compositor->convert_window_to_ia(weighted_window, _ia_ae_window);
            _input.exposure_window = &_ia_ae_window;
        }
    }
    break;
    default:
        XCAM_LOG_ERROR("unsupported ae mode:%d", mode);
        return false;
    }
    return true;
}

bool AiqAeHandler::ensure_ae_priority_mode ()
{
    _input.priority_mode = ia_aiq_ae_priority_mode_normal;
    return true;
}

bool AiqAeHandler::ensure_ae_flicker_mode ()
{
    XCamFlickerMode mode = this->get_flicker_mode_unlock ();
    switch (mode) {
    case XCAM_AE_FLICKER_MODE_AUTO:
        _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_auto;
        break;
    case XCAM_AE_FLICKER_MODE_50HZ:
        _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_50hz;
        break;
    case XCAM_AE_FLICKER_MODE_60HZ:
        _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_60hz;
        break;
    case XCAM_AE_FLICKER_MODE_OFF:
        _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_off;
        break;
    default:
        XCAM_LOG_ERROR ("flicker mode(%d) unknown", mode);
        return false;
    }
    return true;
}

bool AiqAeHandler::ensure_ae_manual ()
{
    if (this->get_mode_unlock () == XCAM_AE_MODE_MANUAL) {
        _input.manual_exposure_time_us = get_manual_exposure_time_unlock ();
        _input.manual_analog_gain = get_manual_analog_gain_unlock ();
    }
    else {
        _input.manual_exposure_time_us = -1;
        _input.manual_analog_gain = -1;
    }

    _input.manual_limits->manual_exposure_time_min =
        _sensor_descriptor.coarse_integration_time_min
        * _sensor_descriptor.pixel_periods_per_line
        / _sensor_descriptor.pixel_clock_freq_mhz;
    _input.manual_limits->manual_exposure_time_max =
        (_sensor_descriptor.line_periods_per_field - _sensor_descriptor.coarse_integration_time_max_margin)
        * _sensor_descriptor.pixel_periods_per_line
        / _sensor_descriptor.pixel_clock_freq_mhz;

    uint64_t exp_min_us = 0, exp_max_us = 0;
    get_exposure_time_range_unlock (exp_min_us, exp_max_us);
    if (exp_min_us && (int64_t)exp_min_us > _input.manual_limits->manual_exposure_time_min) {
        _input.manual_limits->manual_exposure_time_min = exp_min_us;
    }
    if (exp_max_us && (int64_t)exp_max_us < _input.manual_limits->manual_exposure_time_max) {
        _input.manual_limits->manual_exposure_time_max = exp_max_us;
    }

    _input.manual_limits->manual_frame_time_us_min = -1;
    _input.manual_limits->manual_frame_time_us_max = 1000000 / _aiq_compositor->get_framerate ();
    _input.manual_limits->manual_iso_min = -1;
    _input.manual_limits->manual_iso_max = -1;

    return true;
}

bool AiqAeHandler::ensure_ae_ev_shift ()
{
    _input.ev_shift = this->get_ev_shift_unlock();
    return true;
}

SmartPtr<X3aResult>
AiqAeHandler::pop_result ()
{
    //AnalyzerHandler::HandlerLock lock(this);

    X3aIspExposureResult *result = new X3aIspExposureResult(XCAM_IMAGE_PROCESS_ONCE);
    struct atomisp_exposure sensor;
    XCam3aResultExposure exposure;

    xcam_mem_clear (sensor);
    sensor.integration_time[0] = _result.sensor_exp_param.coarse_integration_time;
    sensor.integration_time[1] = _result.sensor_exp_param.fine_integration_time;
    sensor.gain[0] = _result.sensor_exp_param.analog_gain_code_global;
    sensor.gain[1] = _result.sensor_exp_param.digital_gain_global;
    result->set_isp_config (sensor);

    xcam_mem_clear (exposure);
    exposure.exposure_time = _result.aiq_exp_param.exposure_time_us;
    exposure.analog_gain = _result.aiq_exp_param.analog_gain;
    exposure.digital_gain = _result.aiq_exp_param.digital_gain;
    exposure.aperture = _result.aiq_exp_param.aperture_fn;
    result->set_standard_result (exposure);

    return result;
}

XCamReturn
AiqAeHandler::analyze (X3aResultList &output)
{
    ia_aiq  *ia_handle = NULL;
    ia_aiq_ae_results *ae_result = NULL;
    ia_aiq_exposure_sensor_parameters *cur_sensor_result = NULL;
    ia_err ia_error = ia_err_none;
    bool need_apply = false;
    SmartPtr<X3aResult> result;

    AnalyzerHandler::HandlerLock lock(this);

    if (!ensure_ia_parameters ()) {
        XCAM_LOG_ERROR ("AIQ AE ensure ia parameters failed");
        return XCAM_RETURN_ERROR_PARAM;
    }

    ia_handle = _aiq_compositor->get_handle ();
    XCAM_ASSERT (ia_handle);
    ia_error = ia_aiq_ae_run (ia_handle, &_input, &ae_result);
    XCAM_FAIL_RETURN (ERROR, ia_error == ia_err_none, XCAM_RETURN_ERROR_AIQ, "AIQ run AE failed");

    cur_sensor_result = ae_result->exposures[0].sensor_exposure;

    if (!_started) {
        _result.copy (ae_result);
        _started = true;
        need_apply = true;
    } else {
        //TODO
        ia_aiq_exposure_sensor_parameters *last_sensor_res = &_result.sensor_exp_param;
        if (last_sensor_res->coarse_integration_time !=  cur_sensor_result->coarse_integration_time ||
                last_sensor_res->fine_integration_time !=  cur_sensor_result->fine_integration_time ||
                last_sensor_res->analog_gain_code_global !=  cur_sensor_result->analog_gain_code_global ||
                last_sensor_res->digital_gain_global !=  cur_sensor_result->digital_gain_global) {
            ia_aiq_exposure_sensor_parameters cur_cp_res = *cur_sensor_result;
            ia_aiq_exposure_parameters cur_aiq_exp = *ae_result->exposures[0].exposure;
            if (!manual_control_result (cur_cp_res, cur_aiq_exp, *last_sensor_res)) {
                XCAM_LOG_WARNING ("manual control AE result failed");
            }
            _result.copy (ae_result);
            _result.sensor_exp_param = cur_cp_res;
            _result.aiq_exp_param = cur_aiq_exp;

            need_apply = true;
        }
    }

    if (need_apply) {
        result = pop_result ();
        if (result.ptr())
            output.push_back (result);
    }

    return XCAM_RETURN_NO_ERROR;
}

bool
AiqAeHandler::manual_control_result (
    ia_aiq_exposure_sensor_parameters &cur_res,
    ia_aiq_exposure_parameters &cur_aiq_exp,
    const ia_aiq_exposure_sensor_parameters &last_res)
{
    adjust_ae_speed (cur_res, cur_aiq_exp, last_res, this->get_speed_unlock());
    adjust_ae_limitation (cur_res, cur_aiq_exp);

    return true;
}

void
AiqAeHandler::adjust_ae_speed (
    ia_aiq_exposure_sensor_parameters &cur_res,
    ia_aiq_exposure_parameters &cur_aiq_exp,
    const ia_aiq_exposure_sensor_parameters &last_res,
    double ae_speed)
{
    double last_gain, input_gain, ret_gain;
    ia_aiq_exposure_sensor_parameters tmp_res;

    if (XCAM_DOUBLE_EQUAL_AROUND(ae_speed, 1.0 ))
        return;
    xcam_mem_clear (tmp_res);
    tmp_res.coarse_integration_time = _calculate_new_value_by_speed (
                                          last_res.coarse_integration_time,
                                          cur_res.coarse_integration_time,
                                          ae_speed);

    last_gain = _imx185_sensor_gain_code_to_mutiplier (last_res.analog_gain_code_global);
    input_gain = _imx185_sensor_gain_code_to_mutiplier (cur_res.analog_gain_code_global);
    ret_gain = _calculate_new_value_by_speed (last_gain, input_gain, ae_speed);

    tmp_res.analog_gain_code_global = _mutiplier_to_imx185_sensor_gain_code (ret_gain);

    XCAM_LOG_DEBUG ("AE speed: from (shutter:%d, gain:%d[%.03f]) to (shutter:%d, gain:%d[%.03f])",
                    cur_res.coarse_integration_time, cur_res.analog_gain_code_global, input_gain,
                    tmp_res.coarse_integration_time, tmp_res.analog_gain_code_global, ret_gain);

    cur_res.coarse_integration_time = tmp_res.coarse_integration_time;
    cur_res.analog_gain_code_global = tmp_res.analog_gain_code_global;
    cur_aiq_exp.exposure_time_us = _coarse_line_to_time (&_sensor_descriptor,
                                   cur_res.coarse_integration_time);
    cur_aiq_exp.analog_gain = ret_gain;
}

void
AiqAeHandler::adjust_ae_limitation (ia_aiq_exposure_sensor_parameters &cur_res,
                                    ia_aiq_exposure_parameters &cur_aiq_exp)
{
    ia_aiq_exposure_sensor_descriptor * desc = &_sensor_descriptor;
    uint64_t exposure_min = 0, exposure_max = 0;
    double analog_max = get_max_analog_gain_unlock ();
    uint32_t min_coarse_value = desc->coarse_integration_time_min;
    uint32_t max_coarse_value = desc->line_periods_per_field - desc->coarse_integration_time_max_margin;
    uint32_t value;

    get_exposure_time_range_unlock (exposure_min, exposure_max);

    if (exposure_min) {
        value = _time_to_coarse_line (desc, (uint32_t)exposure_min);
        min_coarse_value = (value > min_coarse_value) ? value : min_coarse_value;
    }
    if (cur_res.coarse_integration_time < min_coarse_value) {
        cur_res.coarse_integration_time = min_coarse_value;
        cur_aiq_exp.exposure_time_us = _coarse_line_to_time (desc, min_coarse_value);
    }

    if (exposure_max) {
        value = _time_to_coarse_line (desc, (uint32_t)exposure_max);
        max_coarse_value = (value < max_coarse_value) ? value : max_coarse_value;
    }
    if (cur_res.coarse_integration_time > max_coarse_value) {
        cur_res.coarse_integration_time = max_coarse_value;
        cur_aiq_exp.exposure_time_us = _coarse_line_to_time (desc, max_coarse_value);
    }

    if (analog_max >= 1.0) {
        /* limit gains */
        double gain = _imx185_sensor_gain_code_to_mutiplier (cur_res.analog_gain_code_global);
        if (gain > analog_max) {
            cur_res.analog_gain_code_global = _mutiplier_to_imx185_sensor_gain_code (analog_max);
            cur_aiq_exp.analog_gain = analog_max;
        }
    }
}

XCamFlickerMode
AiqAeHandler::get_flicker_mode ()
{
    {
        AnalyzerHandler::HandlerLock lock(this);
    }
    return AeHandler::get_flicker_mode ();
}

int64_t
AiqAeHandler::get_current_exposure_time ()
{
    AnalyzerHandler::HandlerLock lock(this);

    return (int64_t)_result.aiq_exp_param.exposure_time_us;
}

double
AiqAeHandler::get_current_analog_gain ()
{
    AnalyzerHandler::HandlerLock lock(this);
    return (double)_result.aiq_exp_param.analog_gain;
}

double
AiqAeHandler::get_max_analog_gain ()
{
    {
        AnalyzerHandler::HandlerLock lock(this);
    }
    return AeHandler::get_max_analog_gain ();
}

XCamReturn
AiqAeHandler::set_RGBS_weight_grid (ia_aiq_rgbs_grid **out_rgbs_grid)
{
    AnalyzerHandler::HandlerLock lock(this);

    rgbs_grid_block *rgbs_grid_ptr = (*out_rgbs_grid)->blocks_ptr;
    uint32_t rgbs_grid_index = 0;
    uint16_t rgbs_grid_width = (*out_rgbs_grid)->grid_width;
    uint16_t rgbs_grid_height = (*out_rgbs_grid)->grid_height;

    XCAM_LOG_DEBUG ("rgbs_grid_width = %d, rgbs_grid_height = %d", rgbs_grid_width, rgbs_grid_height);

    uint64_t weight_sum = 0;

    uint32_t image_width = 0;
    uint32_t image_height = 0;
    _aiq_compositor->get_size (image_width, image_height);
    XCAM_LOG_DEBUG ("image_width = %d, image_height = %d", image_width, image_height);

    uint32_t hor_pixels_per_grid = (image_width + (rgbs_grid_width >> 1)) / rgbs_grid_width;
    uint32_t vert_pixels_per_gird = (image_height + (rgbs_grid_height >> 1)) / rgbs_grid_height;
    XCAM_LOG_DEBUG ("rgbs grid: %d x %d pixels per grid cell", hor_pixels_per_grid, vert_pixels_per_gird);

    XCam3AWindow weighted_window = this->get_window_unlock ();
    uint32_t weighted_grid_width = ((weighted_window.x_end - weighted_window.x_start + 1) +
                                    (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid;
    uint32_t weighted_grid_height = ((weighted_window.y_end - weighted_window.y_start + 1) +
                                     (vert_pixels_per_gird >> 1)) / vert_pixels_per_gird;
    XCAM_LOG_DEBUG ("weighted_grid_width = %d, weighted_grid_height = %d", weighted_grid_width, weighted_grid_height);

    uint32_t *weighted_avg_gr = (uint32_t*)xcam_malloc0 (5 * weighted_grid_width * weighted_grid_height * sizeof(uint32_t));
    if (NULL == weighted_avg_gr) {
        return XCAM_RETURN_ERROR_MEM;
    }
    uint32_t *weighted_avg_r = weighted_avg_gr + (weighted_grid_width * weighted_grid_height);
    uint32_t *weighted_avg_b = weighted_avg_r + (weighted_grid_width * weighted_grid_height);
    uint32_t *weighted_avg_gb = weighted_avg_b + (weighted_grid_width * weighted_grid_height);
    uint32_t *weighted_sat = weighted_avg_gb + (weighted_grid_width * weighted_grid_height);

    for (uint32_t win_index = 0; win_index < XCAM_AE_MAX_METERING_WINDOW_COUNT; win_index++) {
        XCAM_LOG_DEBUG ("window start point(%d, %d), end point(%d, %d), weight = %d",
                        _params.window_list[win_index].x_start, _params.window_list[win_index].y_start,
                        _params.window_list[win_index].x_end, _params.window_list[win_index].y_end,
                        _params.window_list[win_index].weight);

        if ((_params.window_list[win_index].weight <= 0) ||
                (_params.window_list[win_index].x_start < 0) ||
                ((uint32_t)_params.window_list[win_index].x_end > image_width) ||
                (_params.window_list[win_index].y_start < 0) ||
                ((uint32_t)_params.window_list[win_index].y_end > image_height) ||
                (_params.window_list[win_index].x_start >= _params.window_list[win_index].x_end) ||
                (_params.window_list[win_index].y_start >= _params.window_list[win_index].y_end) ||
                ((uint32_t)_params.window_list[win_index].x_end - (uint32_t)_params.window_list[win_index].x_start > image_width) ||
                ((uint32_t)_params.window_list[win_index].y_end - (uint32_t)_params.window_list[win_index].y_start > image_height)) {
            XCAM_LOG_DEBUG ("skip window index = %d ", win_index);
            continue;
        }

        rgbs_grid_index = (_params.window_list[win_index].x_start +
                           (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid +
                          ((_params.window_list[win_index].y_start + (vert_pixels_per_gird >> 1))
                           / vert_pixels_per_gird) * rgbs_grid_width;

        weight_sum += _params.window_list[win_index].weight;

        XCAM_LOG_DEBUG ("cumulate rgbs grid statistic, window index = %d ", win_index);
        for (uint32_t i = 0; i < weighted_grid_height; i++) {
            for (uint32_t j = 0; j < weighted_grid_width; j++) {
                weighted_avg_gr[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j +
                        i * rgbs_grid_width].avg_gr * _params.window_list[win_index].weight;
                weighted_avg_r[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j +
                        i * rgbs_grid_width].avg_r * _params.window_list[win_index].weight;
                weighted_avg_b[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j +
                        i * rgbs_grid_width].avg_b * _params.window_list[win_index].weight;
                weighted_avg_gb[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j +
                        i * rgbs_grid_width].avg_gb * _params.window_list[win_index].weight;
                weighted_sat[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j +
                        i * rgbs_grid_width].sat * _params.window_list[win_index].weight;
            }
        }
    }
    XCAM_LOG_DEBUG ("sum of weighing factor = %" PRIu64, weight_sum);

    rgbs_grid_index = (weighted_window.x_start + (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid +
                      (weighted_window.y_start + (vert_pixels_per_gird >> 1)) / vert_pixels_per_gird * rgbs_grid_width;
    for (uint32_t i = 0; i < weighted_grid_height; i++) {
        for (uint32_t j = 0; j < weighted_grid_width; j++) {
            rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_gr =
                weighted_avg_gr[j + i * weighted_grid_width] / weight_sum;
            rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_r =
                weighted_avg_r[j + i * weighted_grid_width] / weight_sum;
            rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_b =
                weighted_avg_b[j + i * weighted_grid_width] / weight_sum;
            rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_gb =
                weighted_avg_gb[j + i * weighted_grid_width] / weight_sum;
            rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].sat =
                weighted_sat[j + i * weighted_grid_width] / weight_sum;
        }
    }

    xcam_free (weighted_avg_gr);

    return XCAM_RETURN_NO_ERROR;
}


XCamReturn
AiqAeHandler::set_hist_weight_grid (ia_aiq_hist_weight_grid **out_weight_grid)
{
    AnalyzerHandler::HandlerLock lock(this);

    uint16_t hist_grid_width = (*out_weight_grid)->width;
    uint16_t hist_grid_height = (*out_weight_grid)->height;
    uint32_t hist_grid_index = 0;

    unsigned char* weights_map_ptr = (*out_weight_grid)->weights;

    uint32_t image_width = 0;
    uint32_t image_height = 0;
    _aiq_compositor->get_size (image_width, image_height);

    uint32_t hor_pixels_per_grid = (image_width + (hist_grid_width >> 1)) / hist_grid_width;
    uint32_t vert_pixels_per_gird = (image_height + (hist_grid_height >> 1)) / hist_grid_height;
    XCAM_LOG_DEBUG ("hist weight grid: %d x %d pixels per grid cell", hor_pixels_per_grid, vert_pixels_per_gird);

    memset (weights_map_ptr, 0, hist_grid_width * hist_grid_height);

    for (uint32_t win_index = 0; win_index < XCAM_AE_MAX_METERING_WINDOW_COUNT; win_index++) {
        XCAM_LOG_DEBUG ("window start point(%d, %d), end point(%d, %d), weight = %d",
                        _params.window_list[win_index].x_start, _params.window_list[win_index].y_start,
                        _params.window_list[win_index].x_end, _params.window_list[win_index].y_end,
                        _params.window_list[win_index].weight);

        if ((_params.window_list[win_index].weight <= 0) ||
                (_params.window_list[win_index].weight > 15) ||
                (_params.window_list[win_index].x_start < 0) ||
                ((uint32_t)_params.window_list[win_index].x_end > image_width) ||
                (_params.window_list[win_index].y_start < 0) ||
                ((uint32_t)_params.window_list[win_index].y_end > image_height) ||
                (_params.window_list[win_index].x_start >= _params.window_list[win_index].x_end) ||
                (_params.window_list[win_index].y_start >= _params.window_list[win_index].y_end) ||
                ((uint32_t)_params.window_list[win_index].x_end - (uint32_t)_params.window_list[win_index].x_start > image_width) ||
                ((uint32_t)_params.window_list[win_index].y_end - (uint32_t)_params.window_list[win_index].y_start > image_height)) {
            XCAM_LOG_DEBUG ("skip window index = %d ", win_index);
            continue;
        }

        uint32_t weighted_grid_width =
            ((_params.window_list[win_index].x_end - _params.window_list[win_index].x_start + 1) +
             (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid;
        uint32_t weighted_grid_height =
            ((_params.window_list[win_index].y_end - _params.window_list[win_index].y_start + 1) +
             (vert_pixels_per_gird >> 1)) / vert_pixels_per_gird;

        hist_grid_index = (_params.window_list[win_index].x_start + (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid +
                          ((_params.window_list[win_index].y_start + (vert_pixels_per_gird >> 1)) /
                           vert_pixels_per_gird) * hist_grid_width;

        for (uint32_t i = 0; i < weighted_grid_height; i++) {
            for (uint32_t j = 0; j < weighted_grid_width; j++) {
                weights_map_ptr[hist_grid_index + j + i * hist_grid_width] = _params.window_list[win_index].weight;
            }
        }
    }
    return XCAM_RETURN_NO_ERROR;
}

XCamReturn
AiqAeHandler::dump_hist_weight_grid (const ia_aiq_hist_weight_grid *weight_grid)
{
    XCAM_LOG_DEBUG ("E dump_hist_weight_grid");
    if (NULL == weight_grid) {
        return XCAM_RETURN_ERROR_PARAM;
    }

    uint16_t grid_width = weight_grid->width;
    uint16_t grid_height = weight_grid->height;

    for (uint32_t i = 0; i < grid_height; i++) {
        for (uint32_t j = 0; j < grid_width; j++) {
            printf ("%d  ", weight_grid->weights[j + i * grid_width]);
        }
        printf("\n");
    }

    XCAM_LOG_DEBUG ("X dump_hist_weight_grid");
    return XCAM_RETURN_NO_ERROR;
}

XCamReturn
AiqAeHandler::dump_RGBS_grid (const ia_aiq_rgbs_grid *rgbs_grid)
{
    XCAM_LOG_DEBUG ("E dump_RGBS_grid");
    if (NULL == rgbs_grid) {
        return XCAM_RETURN_ERROR_PARAM;
    }

    uint16_t grid_width = rgbs_grid->grid_width;
    uint16_t grid_height = rgbs_grid->grid_height;

    printf("AVG B\n");
    for (uint32_t i = 0; i < grid_height; i++) {
        for (uint32_t j = 0; j < grid_width; j++) {
            printf ("%d  ", rgbs_grid->blocks_ptr[j + i * grid_width].avg_b);
        }
        printf("\n");
    }
    printf("AVG Gb\n");
    for (uint32_t i = 0; i < grid_height; i++) {
        for (uint32_t j = 0; j < grid_width; j++) {
            printf ("%d  ", rgbs_grid->blocks_ptr[j + i * grid_width].avg_gb);
        }
        printf("\n");
    }
    printf("AVG Gr\n");
    for (uint32_t i = 0; i < grid_height; i++) {
        for (uint32_t j = 0; j < grid_width; j++) {
            printf ("%d  ", rgbs_grid->blocks_ptr[j + i * grid_width].avg_gr);
        }
        printf("\n");
    }
    printf("AVG R\n");
    for (uint32_t i = 0; i < grid_height; i++) {
        for (uint32_t j = 0; j < grid_width; j++) {
            printf ("%d  ", rgbs_grid->blocks_ptr[j + i * grid_width].avg_r);
            //printf ("%d  ", rgbs_grid->blocks_ptr[j + i * grid_width].sat);
        }
        printf("\n");
    }

    XCAM_LOG_DEBUG ("X dump_RGBS_grid");
    return XCAM_RETURN_NO_ERROR;
}

AiqAwbHandler::AiqAwbHandler (SmartPtr<AiqCompositor> &aiq_compositor)
    : _aiq_compositor (aiq_compositor)
    , _started (false)
{
    xcam_mem_clear (_cct_range);
    xcam_mem_clear (_result);
    xcam_mem_clear (_history_result);
    xcam_mem_clear (_cct_range);
    xcam_mem_clear (_input);

    _input.frame_use = aiq_compositor->get_frame_use ();
    _input.scene_mode = ia_aiq_awb_operation_mode_auto;
    _input.manual_cct_range = NULL;
    _input.manual_white_coordinate = NULL;
}

XCamReturn
AiqAwbHandler::analyze (X3aResultList &output)
{
    ia_aiq  *ia_handle = NULL;
    ia_aiq_awb_results *awb_ret = NULL;
    ia_err ia_error = ia_err_none;

    XCAM_UNUSED (output);

    AnalyzerHandler::HandlerLock lock(this);

    if (!ensure_ia_parameters ()) {
        XCAM_LOG_ERROR ("AIQ AE ensure ia parameters failed");
        return XCAM_RETURN_ERROR_PARAM;
    }

    ia_handle = _aiq_compositor->get_handle ();
    XCAM_ASSERT (ia_handle);
    ia_error = ia_aiq_awb_run (ia_handle, &_input, &awb_ret);
    XCAM_FAIL_RETURN (ERROR, ia_error == ia_err_none, XCAM_RETURN_ERROR_AIQ, "AIQ run AWB failed");

    _result = *awb_ret;
    if (!_started) {
        _history_result = _result;
        _started = true;
    }
    adjust_speed (_history_result);
    _history_result = _result;

    return XCAM_RETURN_NO_ERROR;
}

bool
AiqAwbHandler::ensure_ia_parameters ()
{
    bool ret = true;

    _input.frame_use = _aiq_compositor->get_frame_use ();
    ret = ret && ensure_awb_mode ();
    return ret;
}

bool
AiqAwbHandler::ensure_awb_mode ()
{
    XCamAwbMode mode = get_mode_unlock();

    _input.manual_cct_range = NULL;
    _input.scene_mode = ia_aiq_awb_operation_mode_auto;

    switch (mode) {
    case XCAM_AWB_MODE_AUTO:
        _input.scene_mode = ia_aiq_awb_operation_mode_auto;
        break;
    case XCAM_AWB_MODE_MANUAL: {
        uint32_t cct_min = 0, cct_max = 0;
        get_cct_range_unlock (cct_min, cct_max);
        if (cct_min  && cct_max) {
            _input.scene_mode = ia_aiq_awb_operation_mode_manual_cct_range;
            _cct_range.max_cct = cct_min;
            _cct_range.min_cct = cct_max;
            _input.manual_cct_range = &_cct_range;
        } else
            _input.scene_mode = ia_aiq_awb_operation_mode_auto;
        break;
    }
    case XCAM_AWB_MODE_DAYLIGHT:
        _input.scene_mode = ia_aiq_awb_operation_mode_daylight;
        break;
    case XCAM_AWB_MODE_SUNSET:
        _input.scene_mode = ia_aiq_awb_operation_mode_sunset;
        break;
    case XCAM_AWB_MODE_CLOUDY:
        _input.scene_mode = ia_aiq_awb_operation_mode_partly_overcast;
        break;
    case XCAM_AWB_MODE_TUNGSTEN:
        _input.scene_mode = ia_aiq_awb_operation_mode_incandescent;
        break;
    case XCAM_AWB_MODE_FLUORESCENT:
        _input.scene_mode = ia_aiq_awb_operation_mode_fluorescent;
        break;
    case XCAM_AWB_MODE_WARM_FLUORESCENT:
        _input.scene_mode = ia_aiq_awb_operation_mode_incandescent;
        break;
    case XCAM_AWB_MODE_SHADOW:
        _input.scene_mode = ia_aiq_awb_operation_mode_fully_overcast;
        break;
    case XCAM_AWB_MODE_WARM_INCANDESCENT:
        _input.scene_mode = ia_aiq_awb_operation_mode_incandescent;
        break;
    case XCAM_AWB_MODE_NOT_SET:
        break;

    default:
        XCAM_LOG_ERROR ("unknown or unsupported AWB mode(%d)", mode);
        return false;
    }
    return true;
}

void
AiqAwbHandler::adjust_speed (const ia_aiq_awb_results &last_ret)
{
    _result.final_r_per_g =
        _calculate_new_value_by_speed (
            last_ret.final_r_per_g, _result.final_r_per_g, get_speed_unlock ());
    _result.final_b_per_g =
        _calculate_new_value_by_speed (
            last_ret.final_b_per_g, _result.final_b_per_g, get_speed_unlock ());
}

uint32_t
AiqAwbHandler::get_current_estimate_cct ()
{
    AnalyzerHandler::HandlerLock lock(this);
    return (uint32_t)_result.cct_estimate;
}

XCamReturn
AiqAfHandler::analyze (X3aResultList &output)
{
    // TODO
    XCAM_UNUSED (output);
    return XCAM_RETURN_NO_ERROR;
}

AiqCommonHandler::AiqCommonHandler (SmartPtr<AiqCompositor> &aiq_compositor)
    : _aiq_compositor (aiq_compositor)
    , _gbce_result (NULL)
{
}


XCamReturn
AiqCommonHandler::analyze (X3aResultList &output)
{
    ia_aiq  *ia_handle = NULL;
    ia_aiq_gbce_results *gbce_result = NULL;
    ia_err ia_error = ia_err_none;

    XCAM_UNUSED (output);

    AnalyzerHandler::HandlerLock lock(this);

    ia_aiq_gbce_input_params gbce_input;
    xcam_mem_clear (gbce_input);
    if (has_gbce_unlock()) {
        gbce_input.gbce_level = ia_aiq_gbce_level_use_tuning;
    }
    else {
        gbce_input.gbce_level = ia_aiq_gbce_level_bypass;
    }
    gbce_input.frame_use = _aiq_compositor->get_frame_use ();
    gbce_input.ev_shift = _aiq_compositor->get_ae_ev_shift_unlock ();
    ia_handle = _aiq_compositor->get_handle ();
    XCAM_ASSERT (ia_handle);
    ia_error = ia_aiq_gbce_run (ia_handle, &gbce_input, &gbce_result);

    XCAM_FAIL_RETURN (ERROR, ia_error == ia_err_none, XCAM_RETURN_ERROR_AIQ, "AIQ run GBCE failed");

    //TODO, need copy GBCE result out, not just assign
    _gbce_result = gbce_result;

    return XCAM_RETURN_NO_ERROR;
}

class CmcParser {
public:
    explicit CmcParser (ia_binary_data &cpf)
    {
        _cmc = ia_cmc_parser_init (&cpf);
    }
    ~CmcParser ()
    {
        if (_cmc)
            ia_cmc_parser_deinit (_cmc);
    }
    ia_cmc_t *get() {
        return _cmc;
    }

private:
    ia_cmc_t *_cmc;
};

void
AiqCompositor::convert_window_to_ia (const XCam3AWindow &window, ia_rectangle &ia_window)
{
    ia_rectangle source;
    ia_coordinate_system source_system;
    ia_coordinate_system target_system = {IA_COORDINATE_TOP, IA_COORDINATE_LEFT, IA_COORDINATE_BOTTOM, IA_COORDINATE_RIGHT};

    source_system.left = 0;
    source_system.top = 0;
    source_system.right = this->_width;
    source_system.bottom = this->_height;
    XCAM_ASSERT (_width && _height);

    source.left = window.x_start;
    source.top = window.y_start;
    source.right = window.x_end;
    source.bottom = window.y_end;
    ia_coordinate_convert_rect (&source_system, &source, &target_system, &ia_window);
}

AiqCompositor::AiqCompositor ()
    : _ia_handle (NULL)
    , _ia_mkn (NULL)
    , _pa_result (NULL)
#ifdef HAVE_AIQ_2_7
    , _sa_result (NULL)
#endif
    , _frame_use (ia_aiq_frame_use_video)
    , _width (0)
    , _height (0)
{
    xcam_mem_clear (_frame_params);
}

AiqCompositor::~AiqCompositor ()
{
}

bool
AiqCompositor::open (ia_binary_data &cpf)
{
    CmcParser cmc (cpf);

    _ia_mkn = ia_mkn_init (ia_mkn_cfg_compression, 32000, 100000);
    _ia_handle =
        ia_aiq_init (
            &cpf, NULL, NULL,
            MAX_STATISTICS_WIDTH, MAX_STATISTICS_HEIGHT,
            1, //max_num_stats_in
            cmc.get(),
            _ia_mkn);

    if (_ia_handle == NULL) {
        XCAM_LOG_WARNING ("AIQ init failed");
        return false;
    }

    _adaptor = new IaIspAdaptor22;
    XCAM_ASSERT (_adaptor.ptr());
    if (!_adaptor->init (&cpf, MAX_STATISTICS_WIDTH, MAX_STATISTICS_HEIGHT, cmc.get(), _ia_mkn)) {
        XCAM_LOG_WARNING ("AIQ isp adaptor init failed");
        return false;
    }

    _pa_result = NULL;
#ifdef HAVE_AIQ_2_7
    _sa_result = NULL;
#endif

    XCAM_LOG_DEBUG ("Aiq compositor opened");
    return true;
}

void
AiqCompositor::close ()
{
    _adaptor.release ();
    if (_ia_handle) {
        ia_aiq_deinit (_ia_handle);
        _ia_handle = NULL;
    }

    if (_ia_mkn) {
        ia_mkn_uninit (_ia_mkn);
        _ia_mkn = NULL;
    }

    _ae_handler.release ();
    _awb_handler.release ();
    _af_handler.release ();
    _common_handler.release ();

    _pa_result = NULL;
#ifdef HAVE_AIQ_2_7
    _sa_result = NULL;
#endif

    XCAM_LOG_DEBUG ("Aiq compositor closed");
}

bool
AiqCompositor::set_sensor_mode_data (struct atomisp_sensor_mode_data *sensor_mode)
{
    _frame_params.horizontal_crop_offset = sensor_mode->crop_horizontal_start;
    _frame_params.vertical_crop_offset = sensor_mode->crop_vertical_start;
    _frame_params.cropped_image_height = sensor_mode->crop_vertical_end - sensor_mode->crop_vertical_start + 1;
    _frame_params.cropped_image_width = sensor_mode->crop_horizontal_end - sensor_mode->crop_horizontal_start + 1;

    /* hard code to 254? */
    _frame_params.horizontal_scaling_denominator = 254;
    _frame_params.vertical_scaling_denominator = 254;

    if ((_frame_params.cropped_image_width == 0) || (_frame_params.cropped_image_height == 0)) {
        _frame_params.horizontal_scaling_numerator = 0;
        _frame_params.vertical_scaling_numerator = 0;
    } else {
        _frame_params.horizontal_scaling_numerator =
            sensor_mode->output_width * 254 * sensor_mode->binning_factor_x / _frame_params.cropped_image_width;
        _frame_params.vertical_scaling_numerator =
            sensor_mode->output_height * 254 * sensor_mode->binning_factor_y / _frame_params.cropped_image_height;
    }

    if (!_ae_handler->set_description (sensor_mode)) {
        XCAM_LOG_WARNING ("AIQ set ae description failed");
        return XCAM_RETURN_ERROR_AIQ;
    }
    return true;
}

bool
AiqCompositor::set_3a_stats (SmartPtr<X3aIspStatistics> &stats)
{
    ia_aiq_statistics_input_params aiq_stats_input;
    ia_aiq_rgbs_grid *rgbs_grids = NULL;
    ia_aiq_af_grid *af_grids = NULL;

    xcam_mem_clear (aiq_stats_input);
    aiq_stats_input.frame_timestamp = stats->get_timestamp();
    aiq_stats_input.frame_id = stats->get_timestamp() + 1;
    aiq_stats_input.rgbs_grids = (const ia_aiq_rgbs_grid **)&rgbs_grids;
    aiq_stats_input.num_rgbs_grids = 1;
    aiq_stats_input.af_grids = (const ia_aiq_af_grid **)(&af_grids);
    aiq_stats_input.num_af_grids = 1;

    aiq_stats_input.frame_af_parameters = NULL;
    aiq_stats_input.external_histograms = NULL;
    aiq_stats_input.num_external_histograms = 0;
    aiq_stats_input.camera_orientation = ia_aiq_camera_orientation_unknown;

    if (_pa_result)
        aiq_stats_input.frame_pa_parameters = _pa_result;

#ifdef HAVE_AIQ_2_7
    if (_sa_result)
        aiq_stats_input.frame_sa_parameters = _sa_result;
#endif

    if (_ae_handler->is_started()) {
#ifdef USE_HIST_GRID_WEIGHTING
        if (XCAM_AE_METERING_MODE_WEIGHTED_WINDOW == _ae_handler->get_metering_mode ()) {
            ia_aiq_ae_results* ae_result = _ae_handler->get_result ();

            if (XCAM_RETURN_NO_ERROR != _ae_handler->set_hist_weight_grid (&(ae_result->weight_grid))) {
                XCAM_LOG_ERROR ("ae handler set hist weight grid failed");
            }
        }
#endif
        aiq_stats_input.frame_ae_parameters = _ae_handler->get_result ();
        //_ae_handler->dump_hist_weight_grid (aiq_stats_input.frame_ae_parameters->weight_grid);
    }
    //if (_awb_handler->is_started())
    //    aiq_stats_input.frame_awb_parameters = _awb_handler->get_result();

    if (!_adaptor->convert_statistics (stats->get_isp_stats(), &rgbs_grids, &af_grids)) {
        XCAM_LOG_WARNING ("ia isp adaptor convert 3a stats failed");
        return false;
    }

    if (XCAM_AE_METERING_MODE_WEIGHTED_WINDOW == _ae_handler->get_metering_mode ()) {
#ifdef USE_RGBS_GRID_WEIGHTING
        if (XCAM_RETURN_NO_ERROR != _ae_handler->set_RGBS_weight_grid(&rgbs_grids)) {
            XCAM_LOG_ERROR ("ae handler update RGBS weighted statistic failed");
        }
        //_ae_handler->dump_RGBS_grid (*(aiq_stats_input.rgbs_grids));
#endif
    }
    XCAM_LOG_DEBUG ("statistics grid info, width:%u, height:%u, blk_r:%u, blk_b:%u, blk_gr:%u, blk_gb:%u",
                    aiq_stats_input.rgbs_grids[0]->grid_width,
                    aiq_stats_input.rgbs_grids[0]->grid_height,
                    aiq_stats_input.rgbs_grids[0]->blocks_ptr->avg_r,
                    aiq_stats_input.rgbs_grids[0]->blocks_ptr->avg_b,
                    aiq_stats_input.rgbs_grids[0]->blocks_ptr->avg_gr,
                    aiq_stats_input.rgbs_grids[0]->blocks_ptr->avg_gb);

    if (ia_aiq_statistics_set(get_handle (), &aiq_stats_input) != ia_err_none) {
        XCAM_LOG_ERROR ("Aiq set statistic failed");
        return false;
    }
    return true;
}

XCamReturn AiqCompositor::convert_color_effect (IspInputParameters &isp_input)
{
    AiqCommonHandler *aiq_common = _common_handler.ptr();

    switch (aiq_common->get_color_effect()) {
    case XCAM_COLOR_EFFECT_NONE:
        isp_input.effects = ia_isp_effect_none;
        break;
    case XCAM_COLOR_EFFECT_SKY_BLUE:
        isp_input.effects = ia_isp_effect_sky_blue;
        break;
    case XCAM_COLOR_EFFECT_SKIN_WHITEN_LOW:
        isp_input.effects = ia_isp_effect_skin_whiten_low;
        break;
    case XCAM_COLOR_EFFECT_SKIN_WHITEN:
        isp_input.effects = ia_isp_effect_skin_whiten;
        break;
    case XCAM_COLOR_EFFECT_SKIN_WHITEN_HIGH:
        isp_input.effects = ia_isp_effect_skin_whiten_high;
        break;
    case XCAM_COLOR_EFFECT_SEPIA:
        isp_input.effects = ia_isp_effect_sepia;
        break;
    case XCAM_COLOR_EFFECT_NEGATIVE:
        isp_input.effects = ia_isp_effect_negative;
        break;
    case XCAM_COLOR_EFFECT_GRAYSCALE:
        isp_input.effects = ia_isp_effect_grayscale;
        break;
    default:
        isp_input.effects = ia_isp_effect_none;
        break;
    }

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn
AiqCompositor::apply_gamma_table (struct atomisp_parameters *isp_param)
{
    if (_common_handler->_params.is_manual_gamma) {
        int i;

        if (isp_param->r_gamma_table) {
            isp_param->r_gamma_table->vamem_type = 1; //IA_CSS_VAMEM_TYPE_2 = 1;
            for (i = 0; i < XCAM_GAMMA_TABLE_SIZE; ++i) {
                // change from double to u0.12
                isp_param->r_gamma_table->data.vamem_2[i] =
                    (uint32_t) (_common_handler->_params.r_gamma[i] * 4096.0);
            }
            isp_param->r_gamma_table->data.vamem_2[256] = 4091;
        }

        if (isp_param->g_gamma_table) {
            isp_param->g_gamma_table->vamem_type = 1; //IA_CSS_VAMEM_TYPE_2 = 1;
            for (i = 0; i < XCAM_GAMMA_TABLE_SIZE; ++i) {
                // change from double to u0.12
                isp_param->g_gamma_table->data.vamem_2[i] =
                    (uint32_t) (_common_handler->_params.g_gamma[i] * 4096.0);
            }
            isp_param->g_gamma_table->data.vamem_2[256] = 4091;
        }

        if (isp_param->b_gamma_table) {
            isp_param->b_gamma_table->vamem_type = 1; //IA_CSS_VAMEM_TYPE_2 = 1;
            for (i = 0; i < XCAM_GAMMA_TABLE_SIZE; ++i) {
                // change from double to u0.12
                isp_param->b_gamma_table->data.vamem_2[i] =
                    (uint32_t) (_common_handler->_params.b_gamma[i] * 4096.0);
            }
            isp_param->b_gamma_table->data.vamem_2[256] = 4091;
        }
    }

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn
AiqCompositor::apply_night_mode (struct atomisp_parameters *isp_param)
{
    static const struct atomisp_cc_config night_yuv2rgb_cc_config = {
        10,
        {   1 << 10, 0, 0,  /* 1.0, 0, 0 */
            1 << 10, 0, 0,  /* 1.0, 0, 0 */
            1 << 10, 0, 0
        }
    }; /* 1.0, 0, 0 */
    static const struct atomisp_wb_config night_wb_config = {
        1,
        1 << 15, 1 << 15, 1 << 15, 1 << 15
    }; /* 1.0, 1.0, 1.0, 1.0*/

    if (isp_param->ctc_config)
        isp_param->ctc_config = NULL;

    *isp_param->wb_config = night_wb_config;
    *isp_param->yuv2rgb_cc_config = night_yuv2rgb_cc_config;

    return XCAM_RETURN_NO_ERROR;
}

double
AiqCompositor::calculate_value_by_factor (double factor, double min, double mid, double max)
{
    XCAM_ASSERT (factor >= -1.0 && factor <= 1.0);
    XCAM_ASSERT (min <= mid && max >= mid);

    if (factor >= 0.0)
        return (mid * (1.0 - factor) + max * factor);
    else
        return (mid * (1.0 + factor) + min * (-factor));
}

XCamReturn
AiqCompositor::limit_nr_levels (struct atomisp_parameters *isp_param)
{
#define NR_MIN_FACOTR 0.1
#define NR_MAX_FACOTR 6.0
#define NR_MID_FACOTR 1.0
    SmartPtr<AiqCommonHandler> aiq_common = _common_handler;

    if (!XCAM_DOUBLE_EQUAL_AROUND (aiq_common->_params.nr_level, 0.0)) {
        double nr_factor;
        nr_factor = calculate_value_by_factor (
                        aiq_common->_params.nr_level, NR_MIN_FACOTR, NR_MID_FACOTR, NR_MAX_FACOTR);
        if (isp_param->nr_config) {
            isp_param->nr_config->bnr_gain =
                XCAM_MIN (isp_param->nr_config->bnr_gain * nr_factor, 65535);
            isp_param->nr_config->ynr_gain =
                XCAM_MIN (isp_param->nr_config->ynr_gain * nr_factor, 65535);
        }
        if (isp_param->cnr_config) {
            isp_param->cnr_config->sense_gain_vy =
                XCAM_MIN (isp_param->cnr_config->sense_gain_vy * nr_factor, 8191);
            isp_param->cnr_config->sense_gain_vu =
                XCAM_MIN (isp_param->cnr_config->sense_gain_vu * nr_factor, 8191);
            isp_param->cnr_config->sense_gain_vv =
                XCAM_MIN (isp_param->cnr_config->sense_gain_vv * nr_factor, 8191);
            isp_param->cnr_config->sense_gain_hy =
                XCAM_MIN (isp_param->cnr_config->sense_gain_hy * nr_factor, 8191);
            isp_param->cnr_config->sense_gain_hu =
                XCAM_MIN (isp_param->cnr_config->sense_gain_hu * nr_factor, 8191);
            isp_param->cnr_config->sense_gain_hv =
                XCAM_MIN (isp_param->cnr_config->sense_gain_hv * nr_factor, 8191);
        }
    }

    if (!XCAM_DOUBLE_EQUAL_AROUND (aiq_common->_params.tnr_level, 0.0)) {
        double tnr_factor;
        tnr_factor = calculate_value_by_factor (
                         aiq_common->_params.tnr_level, NR_MIN_FACOTR, NR_MID_FACOTR, NR_MAX_FACOTR);
        if (isp_param->tnr_config) {
            isp_param->tnr_config->gain =
                XCAM_MIN (isp_param->tnr_config->gain * tnr_factor, 65535);
            if (XCAM_DOUBLE_EQUAL_AROUND (aiq_common->_params.tnr_level, 1.0)) {
                isp_param->tnr_config->gain = 65535;
                isp_param->tnr_config->threshold_y = 0;
                isp_param->tnr_config->threshold_uv = 0;
            }
        }
        XCAM_LOG_DEBUG ("set TNR gain:%u", isp_param->tnr_config->gain);
    }

    return XCAM_RETURN_NO_ERROR;
}

XCamReturn AiqCompositor::integrate (X3aResultList &results)
{
    IspInputParameters isp_params;
    ia_aiq_pa_input_params pa_input;
    ia_aiq_pa_results *pa_result = NULL;
#ifdef HAVE_AIQ_2_7
    ia_aiq_sa_input_params sa_input;
    ia_aiq_sa_results *sa_result = NULL;
#endif
    ia_err ia_error = ia_err_none;
    ia_binary_data output;
    AiqAeHandler *aiq_ae = _ae_handler.ptr();
    AiqAwbHandler *aiq_awb = _awb_handler.ptr();
    AiqAfHandler *aiq_af = _af_handler.ptr();
    AiqCommonHandler *aiq_common = _common_handler.ptr();
    struct atomisp_parameters *isp_3a_result = NULL;
    SmartPtr<X3aResult> isp_results;

    XCAM_FAIL_RETURN (
        ERROR,
        aiq_ae && aiq_awb && aiq_af && aiq_common,
        XCAM_RETURN_ERROR_PARAM,
        "handlers are not AIQ inherited");

    xcam_mem_clear (pa_input);
#ifndef HAVE_AIQ_2_7
    pa_input.frame_use = _frame_use;
    pa_input.sensor_frame_params = &_frame_params;
#endif
    if (aiq_ae->is_started())
        pa_input.exposure_params = (aiq_ae->get_result ())->exposures[0].exposure;
    pa_input.color_gains = NULL;

    if (aiq_common->_params.enable_night_mode) {
        pa_input.awb_results = NULL;
        isp_params.effects = ia_isp_effect_grayscale;
    }
    else {
        pa_input.awb_results = aiq_awb->get_result ();
    }

    ia_error = ia_aiq_pa_run (_ia_handle, &pa_input, &pa_result);
    if (ia_error != ia_err_none) {
        XCAM_LOG_WARNING ("AIQ pa run failed"); // but not return error
    }
    _pa_result = pa_result;

    if (aiq_awb->get_mode_unlock () == XCAM_AWB_MODE_MANUAL) {
        if (XCAM_DOUBLE_EQUAL_AROUND (aiq_awb->_params.gr_gain, 0.0) ||
                XCAM_DOUBLE_EQUAL_AROUND (aiq_awb->_params.r_gain, 0.0)  ||
                XCAM_DOUBLE_EQUAL_AROUND (aiq_awb->_params.b_gain, 0.0)  ||
                XCAM_DOUBLE_EQUAL_AROUND (aiq_awb->_params.gb_gain, 0.0)) {
            XCAM_LOG_DEBUG ("Zero gain would cause ISP division fatal error");
        }
        else {
#ifdef HAVE_AIQ_2_7
            _pa_result->color_gains.gr = aiq_awb->_params.gr_gain;
            _pa_result->color_gains.r = aiq_awb->_params.r_gain;
            _pa_result->color_gains.b = aiq_awb->_params.b_gain;
            _pa_result->color_gains.gb = aiq_awb->_params.gb_gain;
#else
            _pa_result->color_gains[0] = aiq_awb->_params.gr_gain;
            _pa_result->color_gains[1] = aiq_awb->_params.r_gain;
            _pa_result->color_gains[2] = aiq_awb->_params.b_gain;
            _pa_result->color_gains[3] = aiq_awb->_params.gb_gain;
#endif
        }
    }

#ifdef HAVE_AIQ_2_7
    xcam_mem_clear (sa_input);
    sa_input.frame_use = _frame_use;
    sa_input.sensor_frame_params = &_frame_params;
    if (aiq_common->_params.enable_night_mode) {
        sa_input.awb_results = NULL;
    }
    else {
        sa_input.awb_results = aiq_awb->get_result ();
    }

    ia_error = ia_aiq_sa_run (_ia_handle, &sa_input, &sa_result);
    if (ia_error != ia_err_none) {
        XCAM_LOG_WARNING ("AIQ sa run failed"); // but not return error
    }
    _sa_result = sa_result;
#endif

    isp_params.frame_use = _frame_use;
    isp_params.awb_results = aiq_awb->get_result ();
    if (aiq_ae->is_started())
        isp_params.exposure_results = (aiq_ae->get_result ())->exposures[0].exposure;
    isp_params.gbce_results = aiq_common->get_gbce_result ();
    isp_params.sensor_frame_params = &_frame_params;
    isp_params.pa_results = pa_result;
#ifdef HAVE_AIQ_2_7
    isp_params.sa_results = sa_result;
#endif

    isp_params.manual_brightness = (int8_t)(aiq_common->get_brightness_unlock() * 128.0);
    isp_params.manual_contrast = (int8_t)(aiq_common->get_contrast_unlock() * 128.0);
    isp_params.manual_saturation = (int8_t)(aiq_common->get_saturation_unlock() * 128.0);
    isp_params.manual_hue = (int8_t)(aiq_common->get_hue_unlock() * 128.0);
    isp_params.manual_sharpness = (int8_t)(aiq_common->get_sharpness_unlock() * 128.0);
    isp_params.manual_nr_level = (int8_t)(aiq_common->get_nr_level_unlock() * 128.0);

    convert_color_effect(isp_params);

    xcam_mem_clear (output);
    if (!_adaptor->run (&isp_params, &output)) {
        XCAM_LOG_ERROR("Aiq to isp adaptor running failed");
        return XCAM_RETURN_ERROR_ISP;
    }
    isp_3a_result = ((struct atomisp_parameters *)output.data);
    apply_gamma_table (isp_3a_result);
    limit_nr_levels (isp_3a_result);
    if (aiq_common->_params.enable_night_mode)
    {
        apply_night_mode (isp_3a_result);
    }

    isp_results = generate_3a_configs (isp_3a_result);
    results.push_back (isp_results);
    return XCAM_RETURN_NO_ERROR;
}

SmartPtr<X3aResult>
AiqCompositor::generate_3a_configs (struct atomisp_parameters *parameters)
{
    SmartPtr<X3aResult> ret;

    X3aAtomIspParametersResult *x3a_result =
        new X3aAtomIspParametersResult (XCAM_IMAGE_PROCESS_ONCE);
    x3a_result->set_isp_config (*parameters);
    ret = x3a_result;
    return ret;
}

void
AiqCompositor::set_ae_handler (SmartPtr<AiqAeHandler> &handler)
{
    XCAM_ASSERT (!_ae_handler.ptr());
    _ae_handler = handler;
}

void
AiqCompositor::set_awb_handler (SmartPtr<AiqAwbHandler> &handler)
{
    XCAM_ASSERT (!_awb_handler.ptr());
    _awb_handler = handler;
}

void
AiqCompositor::set_af_handler (SmartPtr<AiqAfHandler> &handler)
{
    XCAM_ASSERT (!_af_handler.ptr());
    _af_handler = handler;
}

void
AiqCompositor::set_common_handler (SmartPtr<AiqCommonHandler> &handler)
{
    XCAM_ASSERT (!_common_handler.ptr());
    _common_handler = handler;
}


};