// Copyright (C) 2020 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.

#include "host-common/MediaTexturePool.h"

#include <cstdint>
#include <string>
#include <vector>

#include <stdio.h>
#include <string.h>

#define MEDIA_H264_DEBUG 0

#if MEDIA_H264_DEBUG
#define H264_DPRINT(fmt, ...)                                                 \
    fprintf(stderr, "media-texture-pool: %s:%d myid: %d " fmt "\n", __func__, \
            __LINE__, m_id, ##__VA_ARGS__);
#else
#define H264_DPRINT(fmt, ...)
#endif

namespace android {
namespace emulation {

static int s_texturePoolId = 0;
MediaTexturePool::MediaTexturePool() {
    mVirtioGpuOps = android_getVirtioGpuOps();
    if (mVirtioGpuOps == nullptr) {
        H264_DPRINT("Error, cannot get mVirtioGpuOps");
    }
    m_id = s_texturePoolId++;
    H264_DPRINT("created texturepool");
}

MediaTexturePool::~MediaTexturePool() {
    H264_DPRINT("destroyed texturepool");
    cleanUpTextures();
}

const uint32_t kGlUnsignedByte = 0x1401;

constexpr uint32_t kGL_RGBA8 = 0x8058;
constexpr uint32_t kGL_RGBA = 0x1908;
constexpr uint32_t kFRAME_POOL_SIZE = 8;
constexpr uint32_t kFRAMEWORK_FORMAT_NV12 = 3;

MediaTexturePool::TextureFrame MediaTexturePool::getTextureFrame(int w, int h) {
    H264_DPRINT("calling %s %d for tex of w %d h %d\n", __func__, __LINE__, w,
                h);
    PoolHandle ph = m_WH_to_PoolHandle[TexSizes{w, h}];
    if (ph == nullptr) {
        ph = new Pool;
        m_WH_to_PoolHandle[TexSizes{w, h}] = ph;
    }
    if (ph->empty()) {
        std::vector<uint32_t> textures(2 * kFRAME_POOL_SIZE);
        mVirtioGpuOps->create_yuv_textures(kFRAMEWORK_FORMAT_NV12,
                                           kFRAME_POOL_SIZE, w, h,
                                           textures.data());
        for (uint32_t i = 0; i < kFRAME_POOL_SIZE; ++i) {
            TextureFrame frame{textures[2 * i], textures[2 * i + 1]};
            H264_DPRINT("allocated Y %d UV %d", frame.Ytex, frame.UVtex);
            m_Frame_to_PoolHandle[TexFrame{frame.Ytex, frame.UVtex}] = ph;
            ph->push_back(frame);
        }
    }
    TextureFrame frame = ph->front();
    ph->pop_front();
    H264_DPRINT("done %s %d ret Y %d UV %d", __func__, __LINE__, frame.Ytex,
                frame.UVtex);
    return frame;
}

void MediaTexturePool::saveDecodedFrameToTexture(TextureFrame frame,
                                                 void* privData,
                                                 void* func) {
    H264_DPRINT("calling %s %d for tex of  %d  %d\n", __func__, __LINE__,
                (int)frame.Ytex, (int)frame.UVtex);
    if (mVirtioGpuOps) {
        uint32_t textures[2] = {frame.Ytex, frame.UVtex};
        mVirtioGpuOps->update_yuv_textures(kFRAMEWORK_FORMAT_NV12, textures,
                                           privData, func);
    }
}

void MediaTexturePool::putTextureFrame(TextureFrame frame) {
    H264_DPRINT("try recycle textures %d %d", (int)frame.Ytex,
                (int)frame.UVtex);
    if (frame.Ytex > 0 && frame.UVtex > 0) {
        TexFrame tframe{frame.Ytex, frame.UVtex};
        auto iter = m_Frame_to_PoolHandle.find(tframe);
        if (iter != m_Frame_to_PoolHandle.end()) {
            PoolHandle phandle = iter->second;
            H264_DPRINT("recycle registered textures %d %d", (int)frame.Ytex,
                        (int)frame.UVtex);
            phandle->push_back(std::move(frame));
        } else {
            H264_DPRINT("recycle un-registered textures %d %d", (int)frame.Ytex,
                        (int)frame.UVtex);
            deleteTextures(frame);
        }
    }
}

void MediaTexturePool::deleteTextures(TextureFrame frame) {
    if (mVirtioGpuOps && frame.Ytex > 0 && frame.UVtex > 0) {
        std::vector<uint32_t> textures;
        textures.push_back(frame.Ytex);
        textures.push_back(frame.UVtex);
        mVirtioGpuOps->destroy_yuv_textures(kFRAMEWORK_FORMAT_NV12, 1,
                                            textures.data());
    }
}

void MediaTexturePool::cleanUpTextures() {
    if (m_WH_to_PoolHandle.empty()) {
        return;
    }
    for (auto iter : m_WH_to_PoolHandle) {
        auto& myFramePool = *(iter.second);
        std::vector<uint32_t> textures;
        for (auto& frame : myFramePool) {
            textures.push_back(frame.Ytex);
            textures.push_back(frame.UVtex);
            H264_DPRINT("delete Y %d UV %d", frame.Ytex, frame.UVtex);
        }
        mVirtioGpuOps->destroy_yuv_textures(
                kFRAMEWORK_FORMAT_NV12, myFramePool.size(), textures.data());
        myFramePool.clear();
    }
}

}  // namespace emulation
}  // namespace android