/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 "audio_hw_acdb"
//#define LOG_NDEBUG 0
#define LOG_NDDEBUG 0

#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <dlfcn.h>
#include <log/log.h>
#include <cutils/str_parms.h>
#include <system/audio.h>
#include <tinyalsa/asoundlib.h>
#include "acdb.h"
#include <platform_api.h>

#define PLATFORM_CONFIG_KEY_SOUNDCARD_NAME "snd_card_name"

int acdb_init(int snd_card_num)
{

    int result = -1;
    char *cvd_version = NULL;

    char *snd_card_name = NULL;
    struct mixer *mixer = NULL;
    struct acdb_platform_data *my_data = NULL;

    if(snd_card_num < 0) {
        ALOGE("invalid sound card number");
        return result;
    }

    mixer = mixer_open(snd_card_num);
    if (!mixer) {
        ALOGE("%s: Unable to open the mixer card: %d", __func__,
               snd_card_num);
        goto cleanup;
    }

    my_data = calloc(1, sizeof(struct acdb_platform_data));
    if (!my_data) {
        ALOGE("failed to allocate acdb platform data");
        goto cleanup;
    }

    list_init(&my_data->acdb_meta_key_list);

    my_data->acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);
    if (my_data->acdb_handle == NULL) {
        ALOGE("%s: DLOPEN failed for %s", __func__, LIB_ACDB_LOADER);
        goto cleanup;
    }

    ALOGV("%s: DLOPEN successful for %s", __func__, LIB_ACDB_LOADER);

    my_data->acdb_init_v3 = (acdb_init_v3_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_init_v3");
    if (my_data->acdb_init_v3 == NULL)
        ALOGE("%s: dlsym error %s for acdb_loader_init_v3", __func__, dlerror());

    my_data->acdb_init_v2 = (acdb_init_v2_cvd_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_init_v2");
    if (my_data->acdb_init_v2 == NULL)
        ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror());

    my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
                                                 "acdb_loader_init_ACDB");
    if (my_data->acdb_init == NULL && my_data->acdb_init_v2 == NULL
        && my_data->acdb_init_v3 == NULL) {
        ALOGE("%s: dlsym error %s for acdb_loader_init_ACDB", __func__, dlerror());
        goto cleanup;
    }

    /* Get CVD version */
    cvd_version = calloc(1, MAX_CVD_VERSION_STRING_SIZE);
    if (!cvd_version) {
        ALOGE("%s: Failed to allocate cvd version", __func__);
        goto cleanup;
    } else {
        struct mixer_ctl *ctl = NULL;
        int count = 0;

        ctl = mixer_get_ctl_by_name(mixer, CVD_VERSION_MIXER_CTL);
        if (!ctl) {
            ALOGE("%s: Could not get ctl for mixer cmd - %s",  __func__, CVD_VERSION_MIXER_CTL);
            goto cleanup;
        }
        mixer_ctl_update(ctl);

        count = mixer_ctl_get_num_values(ctl);
        if (count > MAX_CVD_VERSION_STRING_SIZE)
            count = MAX_CVD_VERSION_STRING_SIZE;

        result = mixer_ctl_get_array(ctl, cvd_version, count);
        if (result != 0) {
            ALOGE("%s: ERROR! mixer_ctl_get_array() failed to get CVD Version", __func__);
            goto cleanup;
        }
    }

    /* Get Sound card name */
    snd_card_name = strdup(mixer_get_name(mixer));
    if (!snd_card_name) {
        ALOGE("failed to allocate memory for snd_card_name");
        result = -1;
        goto cleanup;
    }

    if (my_data->acdb_init_v3)
        result = my_data->acdb_init_v3(snd_card_name, cvd_version,
                                       &my_data->acdb_meta_key_list);
    else if (my_data->acdb_init_v2)
        result = my_data->acdb_init_v2(snd_card_name, cvd_version, 0);
    else
        result = my_data->acdb_init();

cleanup:
    if (NULL != my_data) {
        if (my_data->acdb_handle)
            dlclose(my_data->acdb_handle);

        struct listnode *node;
        struct meta_key_list *key_info;
        list_for_each(node, &my_data->acdb_meta_key_list) {
            key_info = node_to_item(node, struct meta_key_list, list);
            free(key_info);
        }
        free(my_data);
    }

    mixer_close(mixer);
    free(cvd_version);
    free(snd_card_name);

    return result;
}

int acdb_set_parameters(void *platform, struct str_parms *parms)
{
    struct acdb_platform_data *my_data = (struct acdb_platform_data *)platform;
    char value[128];
    char *kv_pairs = str_parms_to_str(parms);
    int ret = 0;

    if (kv_pairs == NULL) {
        ret = -EINVAL;
        ALOGE("%s: key-value pair is NULL",__func__);
        goto done;
    }

    ret = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME,
                            value, sizeof(value));
    if (ret >= 0) {
        str_parms_del(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME);
        my_data->snd_card_name = strdup(value);
        ALOGV("%s: sound card name %s", __func__, my_data->snd_card_name);
    }

done:
    free(kv_pairs);

    return ret;
}