/*===========================================================================
FUNCTION      isp_tintless_close

DESCRIPTION   close the library and free all allocated memory
===========================================================================*/
static tintless_return_t isp_tintless_close(void ** const res)
{
    tintless_return_t rc = TINTLESS_SUCCESS;
    tintless_lib_t * tintless_lib = NULL;

    CDBG_TINTLESS("%s: Enter\n",__func__);

    if (res != NULL)
        tintless_lib = (tintless_lib_t *) *res;

    if (tintless_lib) {
        CDBG_TINTLESS("%s: tint_lib %p, deinit_func %p \n", __func__,
            tintless_lib, tintless_lib->deinit_func);
        if (tintless_lib->deinit_func) {
            tintless_lib->deinit_func(&tintless_lib->dmlroc_res);
        }

        if (tintless_lib->plib) {
            dlclose(tintless_lib->plib);
        }

        free(*res);
        *res = NULL;
    } else {
        rc = TINTLESS_LIB_NOT_LOADED;
    }

    CDBG_TINTLESS("%s: close/unload tintless lib %d", __func__, rc);
    return rc;
} /* isp_tintless_close */
/*===========================================================================
FUNCTION      isp_tintless_stat_config

DESCRIPTION   Update the stat params for the tintless algo.
              Should be called after the BG stat config has
              been called.
===========================================================================*/
static tintless_return_t isp_tintless_bg_pca_stat_config(void * const res, tintless_stats_config_t * cfg)
{
    tintless_return_t rc;
    tintless_cfg_t c;
    tintless_lib_t * const tintless_lib = (tintless_lib_t *) res;

    CDBG_TINTLESS("%s: Enter \n", __func__);

    CDBG_TINTLESS("%s: stats : camif hxw %d x %d, hxw %d x %d, type %d",__func__,
               cfg->camif_win_h, cfg->camif_win_w,
               cfg->stat_elem_h, cfg->stat_elem_w,
               cfg->stats_type);

    if (tintless_lib == NULL)
    {
        rc = TINTLESS_LIB_NOT_LOADED;
    }
    else
    {
        CDBG_ERROR("%s: pointer okay \n", __func__);
        c.stats = cfg;
        if (tintless_lib->updates & ( 1 << UPDATES_STAT_CONFIG)) {
            rc = isp_tintless_config(tintless_lib, UPDATES_STAT_CONFIG, c);
            CDBG_ERROR("%s: lib returned config err=%d", __func__, rc);
        } else {
            CDBG_ERROR("%s: Stat cfg updates not needed", __func__);
            rc = TINTLESS_UPDATES_NOT_SUPPORTED;
        }
    }

    return rc;
} /* isp_tintless_stat_config */
/*===========================================================================
FUNCTION      isp_tintless_update_chromatix_params

DESCRIPTION   Update the tintless related chromatix tuning parameters.
===========================================================================*/
static tintless_return_t isp_tintless_update_chromatix_params(
   void * const res, chromatix_color_tint_correction_type * p)
{
    tintless_return_t rc;
    tintless_cfg_t param;
    tintless_lib_t * const tintless_lib = (tintless_lib_t *) res;

    CDBG_TINTLESS("%s: chromatix : strength %d",__func__,
               p->tint_correction_strength);

    if (tintless_lib == NULL || tintless_lib->init_func == NULL)
    {
        rc = TINTLESS_LIB_NOT_LOADED;
    } else {
        param.chromatix = p;
        if (tintless_lib->updates & ( 1 << UPDATES_CHROMATIX_PARAMS)) {
            rc = isp_tintless_config(tintless_lib, UPDATES_CHROMATIX_PARAMS, param);
            CDBG_ERROR("%s: lib returned config err=%d", __func__, rc);
        } else {
            CDBG_ERROR("%s: chromatix parameter updates not needed", __func__);
            rc = TINTLESS_UPDATES_NOT_SUPPORTED;
        }
    }

    return rc;
} /* isp_tintless_update_chromatix_params */
/*===========================================================================
FUNCTION      isp_tintless_mesh_config

DESCRIPTION   Update the stat params for the tintless algo.
              Should be called after the BG stat config has
              been called.
===========================================================================*/
static tintless_return_t isp_tintless_mesh_config(void * const res, tintless_mesh_config_t * cfg)
{
    tintless_return_t rc;
    tintless_cfg_t c;
    tintless_lib_t * const tintless_lib = (tintless_lib_t *) res;

    CDBG_TINTLESS("%s: mesh : col, row %d ,%d offset h,v %d,%d, SG hxw %d x %d, subgrids %d",__func__,
               cfg->num_mesh_elem_cols, cfg->num_mesh_elem_rows,
               cfg->offset_horizontal, cfg->offset_vertical,
               cfg->subgrid_height, cfg->subgrid_width,
               cfg->subgrids);


    if (tintless_lib == NULL || tintless_lib->init_func == NULL)
    {
        rc = TINTLESS_LIB_NOT_LOADED;
    } else {
        c.mesh = cfg;
        if (tintless_lib->updates & ( 1 << UPDATES_MESH_CONFIG)) {
            rc = isp_tintless_config(tintless_lib, UPDATES_MESH_CONFIG, c);
            CDBG_ERROR("%s: lib returned mesh config err=%d", __func__, rc);
        } else {
            CDBG_ERROR("%s: Mesh cfg updates not needed", __func__);
            rc = TINTLESS_UPDATES_NOT_SUPPORTED;
        }
    }

    return rc;
} /* isp_tintless_mesh_config */
/*===========================================================================
FUNCTION      isp_tintless_algo

DESCRIPTION   calling the main algorithm for tintless processing
===========================================================================*/
static tintless_return_t isp_tintless_algo(void * const res,
                                    tintless_stats_t * be_stats,
                                    tintless_mesh_rolloff_array_t * ptable_3a,
                                    tintless_mesh_rolloff_array_t * ptable_cur,
                                    tintless_mesh_rolloff_array_t * const ptable_correction)
{
    tintless_return_t rc;
    tintless_lib_t * const tintless_lib = (tintless_lib_t *) res;
    dmlroc_mesh_rolloff_array_t p_tbl_3a;
    dmlroc_mesh_rolloff_array_t p_tbl_correction;
    dmlroc_bayer_stats_info_t pbayer_r, pbayer_gr, pbayer_gb, pbayer_b;

    CDBG_TINTLESS("%s: Enter !\n", __func__);

    if (tintless_lib == NULL || tintless_lib->update_func == NULL) {
        rc = TINTLESS_LIB_NOT_LOADED;
    } else {
        p_tbl_3a.table_size = MESH_ROLL_OFF_V4_TABLE_SIZE;
        p_tbl_3a.r_gain     = (float (*)[DMLROC_MESH_ROLLOFF_WIDTH])ptable_3a->r_gain;
        p_tbl_3a.gr_gain    = (float (*)[DMLROC_MESH_ROLLOFF_WIDTH])ptable_3a->gr_gain;
        p_tbl_3a.gb_gain    = (float (*)[DMLROC_MESH_ROLLOFF_WIDTH])ptable_3a->gb_gain;
        p_tbl_3a.b_gain     = (float (*)[DMLROC_MESH_ROLLOFF_WIDTH])ptable_3a->b_gain;

        p_tbl_correction.table_size = MESH_ROLL_OFF_V4_TABLE_SIZE;
        p_tbl_correction.r_gain     = (float (*)[DMLROC_MESH_ROLLOFF_WIDTH])ptable_correction->r_gain;
        p_tbl_correction.gr_gain    = (float (*)[DMLROC_MESH_ROLLOFF_WIDTH])ptable_correction->gr_gain;
        p_tbl_correction.gb_gain    = (float (*)[DMLROC_MESH_ROLLOFF_WIDTH])ptable_correction->gb_gain;
        p_tbl_correction.b_gain     = (float (*)[DMLROC_MESH_ROLLOFF_WIDTH])ptable_correction->b_gain;
        pbayer_r.channel_counts = be_stats->r.channel_counts;
        pbayer_r.channel_sums   = be_stats->r.channel_sums;
        pbayer_r.array_length   = be_stats->r.array_length;
        pbayer_gr.channel_counts = be_stats->gr.channel_counts;
        pbayer_gr.channel_sums   = be_stats->gr.channel_sums;
        pbayer_gr.array_length   = be_stats->gr.array_length;
        pbayer_gb.channel_counts = be_stats->gb.channel_counts;
        pbayer_gb.channel_sums   = be_stats->gb.channel_sums;
        pbayer_gb.array_length   = be_stats->gb.array_length;
        pbayer_b.channel_counts = be_stats->b.channel_counts;
        pbayer_b.channel_sums   = be_stats->b.channel_sums;
        pbayer_b.array_length   = be_stats->b.array_length;
        rc = translate_return_code(
            tintless_lib->update_func(tintless_lib->dmlroc_res,
                                      &pbayer_r,
                                      &pbayer_gr,
                                      &pbayer_gb,
                                      &pbayer_b,
                                      NULL,
                                      &p_tbl_3a,
                                      &p_tbl_correction));
    }

    if (rc != TINTLESS_SUCCESS) {
        CDBG_ERROR("%s: dmlroCorrection returned err=%d", __func__, rc);
    }

  return rc;
} /* isp_tintless_algo */
/*===========================================================================
FUNCTION      isp_tintless_algo

DESCRIPTION   calling the main algorithm for tintless processing
===========================================================================*/
static tintless_return_t isp_tintless_bg_pca_algo(void * const res,
                                    tintless_stats_t * bg_stats,
                                    tintless_mesh_rolloff_array_t * ptable_3a,
                                    tintless_mesh_rolloff_array_t * ptable_cur,
                                    tintless_mesh_rolloff_array_t * const ptable_correction)
{
    tintless_return_t rc;
    tintless_lib_t * const tintless_lib = (tintless_lib_t *) res;
    mesh_rolloff_array_t p_tbl_3a;
    mesh_rolloff_array_t p_tbl_correction;
    bayer_grid_stats_info_t pbayer_r, pbayer_gr, pbayer_gb, pbayer_b;

    CDBG_TINTLESS("%s: Enter !\n", __func__);

    if (tintless_lib == NULL || tintless_lib->update_func == NULL) {
        rc = TINTLESS_LIB_NOT_LOADED;
    } else {
        pbayer_r.channel_counts = bg_stats->r.channel_counts;
        pbayer_r.channel_sums   = bg_stats->r.channel_sums;
        pbayer_r.array_length   = bg_stats->r.array_length;

        pbayer_gr.channel_counts = bg_stats->gr.channel_counts;
        pbayer_gr.channel_sums   = bg_stats->gr.channel_sums;
        pbayer_gr.array_length   = bg_stats->gr.array_length;

        pbayer_gb.channel_counts = bg_stats->gb.channel_counts;
        pbayer_gb.channel_sums   = bg_stats->gb.channel_sums;
        pbayer_gb.array_length   = bg_stats->gb.array_length;

        pbayer_b.channel_counts = bg_stats->b.channel_counts;
        pbayer_b.channel_sums   = bg_stats->b.channel_sums;
        pbayer_b.array_length   = bg_stats->b.array_length;
        rc = translate_return_code(
            tintless_lib->update_func(&pbayer_r,
                                      &pbayer_gr,
                                      &pbayer_gb,
                                      &pbayer_b,
                                      (mesh_rolloff_array_t *)ptable_cur,
                                      (mesh_rolloff_array_t *)ptable_3a,
                                      (mesh_rolloff_array_t *)ptable_correction));
    }

    if (rc != TINTLESS_SUCCESS) {
        CDBG_ERROR("%s: dmlroCorrection returned err=%d", __func__, rc);
    }

  return rc;
} /* isp_tintless_algo */
/*===========================================================================
FUNCTION      isp_tintless_open

DESCRIPTION
===========================================================================*/
static tintless_return_t isp_tintless_open(void ** const res, uint32_t * updates_needed)
{
    tintless_return_t rc = TINTLESS_LIB_NOT_LOADED;
    dmlroc_version_t v;
    tintless_lib_t * tintless_lib = NULL;
    tintless_lib_t ** pp_tintless;
    char lib_name[BUFF_SIZE_255] = { 0 };

    CDBG_TINTLESS("%s : Enter!\n", __func__);
    if (res != NULL){
        pp_tintless = (tintless_lib_t **)res;
    } else {
        CDBG_ERROR("%s : res pointer NULL!\n", __func__);
        goto ERROR;
    }


    *pp_tintless = (tintless_lib_t *) malloc(sizeof (tintless_lib_t));
    if (*pp_tintless == NULL) {
        rc = TINTLESS_NO_MEMORY;
        goto ERROR;
    }
    tintless_lib = (tintless_lib_t *) *pp_tintless;

    memset(tintless_lib, 0, sizeof(tintless_lib_t));

    strlcpy(lib_name, "libmmcamera_tintless_algo.so", BUFF_SIZE_255);

    dlerror();
    tintless_lib->plib = dlopen(lib_name, RTLD_NOW);

    if (!tintless_lib->plib) {
        CDBG_ERROR("%s:Failed to dlopen %s: %s", __func__, lib_name, dlerror());
        goto ERROR;
    }

  *(void **)&(tintless_lib->init_func) = dlsym(tintless_lib->plib,
                                              "dmlroc_init");
  if (!tintless_lib->init_func)
    CDBG_ERROR("%s:init Failed to dlsym %s: %s",
               __func__, lib_name, dlerror());

  *(void **)&(tintless_lib->update_func) = dlsym(tintless_lib->plib,
                                                "dmlroc_entry");
  if (!tintless_lib->update_func)
    CDBG_ERROR("%s:update Failed to dlsym %s: %s",
               __func__, lib_name, dlerror());

  *(void **)&(tintless_lib->get_version_func) =
                  dlsym(tintless_lib->plib, "dmlroc_get_version");
  if (!tintless_lib->get_version_func)
    CDBG_ERROR("%s:version Failed to dlsym %s: %s",
               __func__, lib_name, dlerror());

  *(void **)&(tintless_lib->deinit_func) = dlsym(tintless_lib->plib,
                                                "dmlroc_deinit");
  if (!tintless_lib->deinit_func)
    CDBG_ERROR("%s:deinit Failed to dlsym %s: %s",
               __func__, lib_name, dlerror());

  if (!tintless_lib->init_func || !tintless_lib->update_func ||
      !tintless_lib->get_version_func || !tintless_lib->deinit_func) {
    CDBG_ERROR("%s:Failed to dlsym %s: %s", __func__, lib_name, dlerror());
    goto ERROR;
  }

    // subscribe to cfg updates
    *updates_needed = (TINTLESS_UPDATE_STATS |
                       TINTLESS_UPDATE_MESH |
                       TINTLESS_UPDATE_CHROMATIX_PARAMS);
    tintless_lib->updates = *updates_needed;
    tintless_lib->cfg.tint_correction_strength = UINT8_MAX;
    return TINTLESS_SUCCESS;


ERROR:
    if (tintless_lib != NULL) {
        if (tintless_lib->plib) {
            dlclose(tintless_lib->plib);
        }
        free(tintless_lib);
        *pp_tintless = NULL;
    }
    return rc;
} /* isp_tintless_open */