/*
 * Copyright (c) 2023-2023 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 <memory>
#include <sstream>
#include <sys/ioctl.h>
#include <linux/dma-buf.h>
#include "securec.h"
#include "avcodec_log.h"
#include "avcodec_errors.h"
#include "fsurface_memory.h"
namespace {
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_FRAMEWORK, "AvCodec-FSurfaceMemory"};
#define DMA_BUF_SET_LEAK_TYPE _IOW(DMA_BUF_BASE, 5, const char *)
}
namespace OHOS {
namespace MediaAVCodec {
FSurfaceMemory::~FSurfaceMemory()
{
    ReleaseSurfaceBuffer();
}

int32_t FSurfaceMemory::AllocSurfaceBuffer(int32_t width, int32_t height)
{
    CHECK_AND_RETURN_RET_LOG(sInfo_->surface != nullptr, AVCS_ERR_UNKNOWN, "Surface is nullptr!");
    CHECK_AND_RETURN_RET_LOG(!isAttached, AVCS_ERR_UNKNOWN, "Only support when not attach!");
    CHECK_AND_RETURN_RET_LOG(surfaceBuffer_ == nullptr, AVCS_ERR_UNKNOWN, "Surface buffer is not nullptr!");
    surfaceBuffer_ = SurfaceBuffer::Create();
    CHECK_AND_RETURN_RET_LOG(surfaceBuffer_, AVCS_ERR_UNKNOWN, "Create surface buffer failed!");
    GSError err = surfaceBuffer_->Alloc(sInfo_->requestConfig);
    CHECK_AND_RETURN_RET_LOG(err == GSERROR_OK, err, "Alloc surface buffer failed, GSERROR=%{public}d", err);
    owner = Owner::OWNED_BY_CODEC;
    isAttached = false;
    seqNum_ = surfaceBuffer_->GetSeqNum();
    SetCallerToBuffer(width, height);
    AVCODEC_LOGI("Alloc surface buffer success seq=%{public}u", seqNum_);
    return AVCS_ERR_OK;
}

int32_t FSurfaceMemory::RequestSurfaceBuffer()
{
    CHECK_AND_RETURN_RET_LOG(sInfo_->surface != nullptr, AVCS_ERR_UNKNOWN, "Surface is nullptr");
    CHECK_AND_RETURN_RET_LOG(owner == Owner::OWNED_BY_SURFACE, AVCS_ERR_UNKNOWN, "Only support when owned by surface!");
    sptr<SurfaceBuffer> surfaceBuffer = nullptr;
    auto ret = sInfo_->surface->RequestBuffer(surfaceBuffer, fence_, sInfo_->requestConfig);
    if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK || surfaceBuffer == nullptr) {
        if (ret != OHOS::SurfaceError::SURFACE_ERROR_NO_BUFFER) {
            AVCODEC_LOGE("Request surface buffer fail, ret=%{public}" PRIu64, static_cast<uint64_t>(ret));
        }
        return ret;
    }
    SetSurfaceBuffer(surfaceBuffer, Owner::OWNED_BY_CODEC);
    return AVCS_ERR_OK;
}

void FSurfaceMemory::ReleaseSurfaceBuffer()
{
    surfaceBuffer_ = nullptr;
}

sptr<SurfaceBuffer> FSurfaceMemory::GetSurfaceBuffer()
{
    if (isAttached && owner == Owner::OWNED_BY_SURFACE) {
        CHECK_AND_RETURN_RET_LOG(RequestSurfaceBuffer() == AVCS_ERR_OK, nullptr, "Get surface buffer failed!");
    }
    return surfaceBuffer_;
}

void FSurfaceMemory::SetSurfaceBuffer(sptr<SurfaceBuffer> surfaceBuffer, Owner toChangeOwner, sptr<SyncFence> fence)
{
    CHECK_AND_RETURN_LOG(surfaceBuffer != nullptr, "Surface buffer is nullptr!");
    surfaceBuffer_ = surfaceBuffer;
    owner = toChangeOwner;
    seqNum_ = surfaceBuffer_->GetSeqNum();
    if (fence != nullptr) {
        fence_ = fence;
    }
}

int32_t FSurfaceMemory::GetSurfaceBufferStride()
{
    CHECK_AND_RETURN_RET_LOG(surfaceBuffer_ != nullptr, 0, "Surface buffer is nullptr!");
    auto bufferHandle = surfaceBuffer_->GetBufferHandle();
    CHECK_AND_RETURN_RET_LOG(bufferHandle != nullptr, AVCS_ERR_UNKNOWN, "Failed to get bufferHandle!");
    stride_ = bufferHandle->stride;
    return stride_;
}

sptr<SyncFence> FSurfaceMemory::GetFence()
{
    return fence_;
}

uint8_t *FSurfaceMemory::GetBase() const
{
    CHECK_AND_RETURN_RET_LOG(surfaceBuffer_ != nullptr, nullptr, "Surface buffer is nullptr!");
    return static_cast<uint8_t *>(surfaceBuffer_->GetVirAddr());
}

int32_t FSurfaceMemory::GetSize() const
{
    CHECK_AND_RETURN_RET_LOG(surfaceBuffer_ != nullptr, -1, "Surface buffer is nullptr!");
    uint32_t size = surfaceBuffer_->GetSize();
    return static_cast<int32_t>(size);
}

uint32_t FSurfaceMemory::GetSurfaceBufferSeqNum() const
{
    return seqNum_;
}

void FSurfaceMemory::SetCallerToBuffer(int32_t w, int32_t h)
{
    CHECK_AND_RETURN_LOG(surfaceBuffer_ != nullptr, "Surface buffer is nullptr!");
    int32_t fd = surfaceBuffer_->GetFileDescriptor();
    CHECK_AND_RETURN_LOG(fd > 0, "Invalid fd %{public}d, surfacebuf(%{public}u)", fd, surfaceBuffer_->GetSeqNum());
    std::string type = "sw-video-decoder";
    std::string mime(decInfo_.mimeType);
    std::vector<std::string> splitMime;
    std::string token;
    std::istringstream iss(mime);
    while (std::getline(iss, token, '/')) {
        splitMime.push_back(token);
    }
    if (!splitMime.empty()) {
        mime = splitMime.back();
    }
    std::string name = decInfo_.processName + "-" + std::to_string(w) + "x" + std::to_string(h)
                       + "-" + mime + "-" + decInfo_.instanceId;
    ioctl(fd, DMA_BUF_SET_LEAK_TYPE, type.c_str());
    std::string pid = std::to_string(decInfo_.pid);
    ioctl(fd, DMA_BUF_SET_NAME_A, pid.c_str());
    ioctl(fd, DMA_BUF_SET_NAME_A, name.c_str());
}
} // namespace MediaAVCodec
} // namespace OHOS