/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * Licensed under Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with License.
 * You may obtain a copy of License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under 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 "audio_buffer_queue.h"
#include <cstring>
#include <hilog/log.h>
#include <unistd.h>
#include "securec.h"

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x3200
#define LOG_TAG "AUDIO_BUFFER_QUEUE"

AudioBufferQueue::AudioBufferQueue(size_t size) {
    bufferSize = size;
    buffer.resize(size);
    writePos = 0;
    readPos = 0;
    availableData = 0;
}

AudioBufferQueue::~AudioBufferQueue() {
    buffer.clear();
}

size_t AudioBufferQueue::Write(int fd, size_t bytesToWrite) {
    std::lock_guard<std::mutex> lock(mutex);
    
    size_t availableSpace = bufferSize - availableData;
    size_t actualWrite = std::min(bytesToWrite, availableSpace);
    
    if (actualWrite == 0) {
        return 0;
    }
    
    if (writePos + actualWrite > bufferSize) {
        size_t firstPart = bufferSize - writePos;
        size_t secondPart = actualWrite - firstPart;
        
        ssize_t bytesRead = read(fd, buffer.data() + writePos, firstPart);
        if (bytesRead != firstPart) {
            OH_LOG_ERROR(LOG_APP, "Read audio data error.");
            return 0;
        }
        bytesRead = read(fd, buffer.data(), secondPart);
        if (bytesRead != secondPart) {
            OH_LOG_ERROR(LOG_APP, "Read audio data error.");
            return 0;
        }
        
        writePos = secondPart;
    } else {
        ssize_t bytesRead = read(fd, buffer.data() + writePos, actualWrite);
        if (bytesRead != actualWrite) {
            OH_LOG_ERROR(LOG_APP, "Read audio data error.");
            return 0;
        }
        writePos += actualWrite;
    }
    
    availableData += actualWrite;
    
    return actualWrite;
}

size_t AudioBufferQueue::Read(void* dest, size_t bytesToRead) {
    if (dest == nullptr) {
        return 0;
    }
    std::lock_guard<std::mutex> lock(mutex);
    size_t actualRead = std::min(bytesToRead, availableData);
    
    if (actualRead == 0) {
        return 0;
    }
    
    if (readPos + actualRead > bufferSize) {
        size_t firstPart = bufferSize - readPos;
        size_t secondPart = actualRead - firstPart;
        errno_t ret = memcpy_s(static_cast<uint8_t*>(dest), buffer.size(), buffer.data() + readPos, firstPart);
        if (ret != EOK) {
            return -1;
        }
        ret = memcpy_s(static_cast<uint8_t*>(dest) + firstPart, buffer.size(), buffer.data(), secondPart);
        if (ret != EOK) {
            return -1;
        }
        readPos = secondPart;
    } else {
        errno_t ret = memcpy_s(static_cast<uint8_t*>(dest), buffer.size(), buffer.data() + readPos, actualRead);
        if (ret != EOK) {
            return -1;
        }
        readPos += actualRead;
    }
    availableData -= actualRead;
    return actualRead;
}

size_t AudioBufferQueue::GetAvailableSpace() const {
    return bufferSize - availableData;
}

void AudioBufferQueue::Clear() {
    std::lock_guard<std::mutex> lock(mutex);
    writePos = 0;
    readPos = 0;
    availableData = 0;
}

bool AudioBufferQueue::IsFull() const {
    return availableData == bufferSize;
}