/*
 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
 * 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.
 */

#include "daudio_manager_callback.h"

#include <cstdint>
#include <hdf_base.h>
#include <securec.h>

#include "audio_types.h"

#include "daudio_constants.h"
#include "daudio_errorcode.h"
#include "daudio_log.h"

#undef DH_LOG_TAG
#define DH_LOG_TAG "DAudioManagerCallback"

using OHOS::HDI::DistributedAudio::Audioext::V3_0::AudioParameter;

namespace OHOS {
namespace DistributedHardware {
int32_t DAudioManagerCallback::CreateStream(int32_t streamId /* for multistream */)
{
    DHLOGI("Open device.");
    CHECK_NULL_RETURN(callback_, HDF_FAILURE);
    int32_t ret = callback_->CreateStream(streamId);
    if (ret != DH_SUCCESS) {
        DHLOGE("Call hdi callback failed.");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::DestroyStream(int32_t streamId)
{
    DHLOGI("Close device.");
    CHECK_NULL_RETURN(callback_, HDF_FAILURE);
    if (callback_->DestroyStream(streamId) != DH_SUCCESS) {
        DHLOGE("Rall hdi callback failed.");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::GetAudioParamHDF(const AudioParameter& param, AudioParamHDF& paramHDF)
{
    paramHDF.sampleRate = static_cast<AudioSampleRate>(param.sampleRate);
    paramHDF.channelMask = static_cast<AudioChannel>(param.channelCount);
    switch (static_cast<AudioFormat>(param.format)) {
        case AUDIO_FORMAT_TYPE_PCM_8_BIT:
            paramHDF.bitFormat = AudioSampleFormat::SAMPLE_U8;
            break;
        case AUDIO_FORMAT_TYPE_PCM_16_BIT:
            paramHDF.bitFormat = AudioSampleFormat::SAMPLE_S16LE;
            break;
        case AUDIO_FORMAT_TYPE_PCM_24_BIT:
            paramHDF.bitFormat = AudioSampleFormat::SAMPLE_S24LE;
            break;
        default:
            DHLOGE("Format [%{public}" PRIu32"] does not support conversion.", param.format);
            return HDF_FAILURE;
    }
    switch (static_cast<AudioCategory>(param.streamUsage)) {
        case AUDIO_IN_MEDIA:
            paramHDF.streamUsage = StreamUsage::STREAM_USAGE_MEDIA;
            break;
        case AUDIO_IN_COMMUNICATION:
        case AUDIO_MMAP_VOIP:
            paramHDF.streamUsage = StreamUsage::STREAM_USAGE_VOICE_COMMUNICATION;
            break;
        case AUDIO_IN_RINGTONE:
            paramHDF.streamUsage = StreamUsage::STREAM_USAGE_NOTIFICATION_RINGTONE;
            break;
        case AUDIO_MMAP_NOIRQ:
            paramHDF.streamUsage = StreamUsage::STREAM_USAGE_MEDIA;
            break;
        default:
            DHLOGE("Stream usage [%{public}" PRIu32"] does not support conversion.", param.streamUsage);
            return HDF_FAILURE;
    }
    paramHDF.frameSize = param.frameSize;
    paramHDF.period = param.period;
    paramHDF.ext = param.ext;
    paramHDF.renderFlags = static_cast<OHOS::DistributedHardware::PortOperationMode>(param.renderFlags);
    paramHDF.capturerFlags = static_cast<OHOS::DistributedHardware::PortOperationMode>(param.capturerFlags);
    DHLOGI("HDF Param: sample rate %{public}d, channel %{public}d, bit format %{public}d, stream "
        "usage %{public}d, frame size %{public}" PRIu32", period %{public}" PRIu32
        ", renderFlags %{public}d, capturerFlags %{public}d, ext {%{public}s}.", paramHDF.sampleRate,
        paramHDF.channelMask, paramHDF.bitFormat, paramHDF.streamUsage, paramHDF.frameSize, paramHDF.period,
        paramHDF.renderFlags, paramHDF.capturerFlags, paramHDF.ext.c_str());
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::SetParameters(int32_t streamId, const AudioParameter& param)
{
    DHLOGI("Set Parameters channelLayout:%{public}" PRIu64, param.channelLayout);
    CHECK_NULL_RETURN(callback_, HDF_FAILURE);
    AudioParamHDF paramHDF;
    int32_t ret = GetAudioParamHDF(param, paramHDF);
    if (ret != DH_SUCCESS) {
        DHLOGE("Get audio HDF param failed.");
        return HDF_FAILURE;
    }
    ret = callback_->SetParameters(streamId, paramHDF);
    if (ret != DH_SUCCESS) {
        DHLOGE("Call hdi callback failed.");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::NotifyEvent(int32_t streamId,
    const OHOS::HDI::DistributedAudio::Audioext::V3_0::DAudioEvent& event)
{
    DHLOGI("Notify event.");
    CHECK_NULL_RETURN(callback_, HDF_FAILURE);
    AudioEvent newEvent(AudioEventType::EVENT_UNKNOWN, event.content);
    switch (event.type) {
        case AudioEventHDF::AUDIO_EVENT_VOLUME_SET:
            newEvent.type = AudioEventType::VOLUME_SET;
            break;
        case AudioEventHDF::AUDIO_EVENT_MUTE_SET:
            newEvent.type = AudioEventType::VOLUME_MUTE_SET;
            break;
        case AudioEventHDF::AUDIO_EVENT_CHANGE_PLAY_STATUS:
            newEvent.type = AudioEventType::CHANGE_PLAY_STATUS;
            break;
        case AudioEventHDF::AUDIO_EVENT_MMAP_START_SPK:
            newEvent.type = AudioEventType::MMAP_SPK_START;
            break;
        case AudioEventHDF::AUDIO_EVENT_MMAP_STOP_SPK:
            newEvent.type = AudioEventType::MMAP_SPK_STOP;
            break;
        case AudioEventHDF::AUDIO_EVENT_MMAP_START_MIC:
            newEvent.type = AudioEventType::MMAP_MIC_START;
            break;
        case AudioEventHDF::AUDIO_EVENT_MMAP_STOP_MIC:
            newEvent.type = AudioEventType::MMAP_MIC_STOP;
            break;
        case AudioEventHDF::AUDIO_EVENT_START:
            newEvent.type = AudioEventType::AUDIO_START;
            break;
        case AudioEventHDF::AUDIO_EVENT_STOP:
            newEvent.type = AudioEventType::AUDIO_STOP;
            break;
        case AudioEventHDF::AUDIO_EVENT_ENHANC:
            newEvent.type = AudioEventType::ENHANCE_PARAM_CHANGE;
            break;
        default:
            DHLOGE("Unsupport event tpye.");
            break;
    }

    int32_t ret = callback_->NotifyEvent(streamId, newEvent);
    if (ret != DH_SUCCESS) {
        DHLOGE("Call hdi callback failed.");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::WriteStreamData(int32_t streamId,
    const OHOS::HDI::DistributedAudio::Audioext::V3_0::AudioData &data)
{
    DHLOGD("Write Stream Data, audio data param frameSize is %{public}d.", data.param.frameSize);
    if (data.param.frameSize == 0 || data.param.frameSize > DEFAULT_AUDIO_DATA_SIZE) {
        DHLOGE("Audio data param frameSize is 0. or > 4096");
        return HDF_FAILURE;
    }

    std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>(data.param.frameSize);
    int32_t ret = memcpy_s(audioData->Data(), audioData->Capacity(), data.data.data(), data.data.size());
    if (ret != EOK) {
        DHLOGE("Copy audio data failed, error code %{public}d.", ret);
        return HDF_FAILURE;
    }

    CHECK_NULL_RETURN(callback_, HDF_FAILURE);
    if (callback_->WriteStreamData(streamId, audioData) != DH_SUCCESS) {
        DHLOGE("WriteStreamData failed.");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::ReadStreamData(int32_t streamId,
    OHOS::HDI::DistributedAudio::Audioext::V3_0::AudioData &data)
{
    DHLOGD("Read stream data.");
    std::shared_ptr<AudioData> audioData = nullptr;
    CHECK_NULL_RETURN(callback_, HDF_FAILURE);
    if (callback_->ReadStreamData(streamId, audioData) != DH_SUCCESS) {
        DHLOGE("Read stream data failed.");
        return HDF_FAILURE;
    }

    CHECK_NULL_RETURN(audioData, HDF_FAILURE);
    data.data.assign(audioData->Data(), audioData->Data()+audioData->Capacity());
    DHLOGD("Read stream data success.");
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::ReadMmapPosition(int32_t streamId,
    uint64_t &frames, OHOS::HDI::DistributedAudio::Audioext::V3_0::CurrentTime &time)
{
    DHLOGD("Read mmap position");
    CurrentTimeHDF timeHdf;
    CHECK_NULL_RETURN(callback_, HDF_FAILURE);
    if (callback_->ReadMmapPosition(streamId, frames, timeHdf) != DH_SUCCESS) {
        DHLOGE("Read mmap position failed.");
        return HDF_FAILURE;
    }
    time.tvSec = timeHdf.tvSec;
    time.tvNSec = timeHdf.tvNSec;
    DHLOGD("Read mmap position success.");
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::RefreshAshmemInfo(int32_t streamId, int fd, int32_t ashmemLength,
    int32_t lengthPerTrans)
{
    DHLOGD("Refresh ashmem info.");
    CHECK_NULL_RETURN(callback_, HDF_FAILURE);
    if (callback_->RefreshAshmemInfo(streamId, fd, ashmemLength, lengthPerTrans) != DH_SUCCESS) {
        DHLOGE("Refresh ashmem info failed.");
        return HDF_FAILURE;
    }
    DHLOGD("Refresh ashmem info success.");
    return HDF_SUCCESS;
}

int32_t DAudioManagerCallback::GetLatency(int32_t streamId, uint32_t& ms)
{
    DHLOGD("Get latency not support yet.");
    (void)streamId;
    (void)ms;
    return HDF_SUCCESS;
}
 
int32_t DAudioManagerCallback::GetRenderPosition(int32_t streamId, uint64_t &frames,
    OHOS::HDI::DistributedAudio::Audioext::V3_0::CurrentTime &time)
{
    DHLOGD("Get render position not support yet.");
    (void)streamId;
    (void)frames;
    (void)time;
    return HDF_SUCCESS;
}
} // DistributedHardware
} // OHOS