// Copyright 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/MediaSnapshotState.h"

#include <stdio.h>
#include <cassert>

#define MEDIA_SNAPSTATE_DEBUG 0

#if MEDIA_SNAPSTATE_DEBUG
#define SNAPSTATE_DPRINT(fmt, ...)                                     \
    fprintf(stderr, "media-snapshot-state: %s:%d " fmt "\n", __func__, \
            __LINE__, ##__VA_ARGS__);
#else
#define SNAPSTATE_DPRINT(fmt, ...)
#endif

#include <string.h>

namespace android {
namespace emulation {

bool MediaSnapshotState::savePacket(std::vector<uint8_t> data, uint64_t pts) {
    if (pts > 0 && savedPackets.size() > 0 && pts == savedPackets.back().pts) {
        return false;
    }
    PacketInfo pkt{data, pts};
    savedPackets.push_back(std::move(pkt));
    return true;
}
bool MediaSnapshotState::savePacket(const uint8_t* frame,
                                    size_t size,
                                    uint64_t pts) {
    if (pts > 0 && savedPackets.size() > 0 && pts == savedPackets.back().pts) {
        return false;
    }
    std::vector<uint8_t> vec;
    vec.assign(frame, frame + size);
    PacketInfo pkt{vec, pts};
    savedPackets.push_back(std::move(pkt));
    return true;
}

void MediaSnapshotState::saveDecodedFrame(std::vector<uint8_t> data,
                                          int width,
                                          int height,
                                          uint64_t pts,
                                          ColorAspects xcolor) {
    SNAPSTATE_DPRINT("save decoded byte data");
    FrameInfo frame{
            std::move(data), std::vector<uint32_t>{}, width, height, pts,
            xcolor};
    savedFrames.push_back(std::move(frame));
}

void MediaSnapshotState::saveDecodedFrame(std::vector<uint32_t> texture,
                                          int width,
                                          int height,
                                          uint64_t pts,
                                          ColorAspects xcolor) {
    SNAPSTATE_DPRINT("save decoded texture");
    FrameInfo frame{std::vector<uint8_t>{},
                    std::move(texture),
                    width,
                    height,
                    pts,
                    xcolor};
    savedFrames.push_back(std::move(frame));
}

namespace {
template <class T>
void saveVec(base::Stream* stream, const std::vector<T>& vec) {
    stream->putBe32(vec.size());
    if (!vec.empty()) {
        stream->write(vec.data(), vec.size() * sizeof(vec[0]));
    }
}

template <class T>
void loadVec(base::Stream* stream, std::vector<T>& vec) {
    int size = stream->getBe32();
    vec.resize(size);
    if (size > 0) {
        stream->read(vec.data(), size * sizeof(vec[0]));
    }
}
}  // namespace

void MediaSnapshotState::saveFrameInfo(base::Stream* stream,
                                       const FrameInfo& frame) const {
    SNAPSTATE_DPRINT("save bytedata");
    saveVec(stream, frame.data);
    SNAPSTATE_DPRINT("save texture");
    saveVec(stream, frame.texture);
    stream->putBe32(frame.width);
    stream->putBe32(frame.height);
    saveColor(stream, frame.color);
    stream->putBe64(frame.pts);
}

void MediaSnapshotState::loadFrameInfo(base::Stream* stream, FrameInfo& frame) {
    SNAPSTATE_DPRINT("load bytedata");
    loadVec(stream, frame.data);
    SNAPSTATE_DPRINT("load texture");
    loadVec(stream, frame.texture);
    // set all to zero, as we dont really save texture
    std::fill(frame.texture.begin(), frame.texture.end(), 0);
    frame.width = stream->getBe32();
    frame.height = stream->getBe32();
    loadColor(stream, frame.color);
    frame.pts = stream->getBe64();
}

void MediaSnapshotState::savePacketInfo(base::Stream* stream,
                                        const PacketInfo& pkt) const {
    saveVec(stream, pkt.data);
    stream->putBe64(pkt.pts);
}

void MediaSnapshotState::loadPacketInfo(base::Stream* stream, PacketInfo& pkt) {
    loadVec(stream, pkt.data);
    pkt.pts = stream->getBe64();
}

void MediaSnapshotState::saveColor(base::Stream* stream,
                                   const ColorAspects& color) const {
    stream->putBe32(color.primaries);
    stream->putBe32(color.range);
    stream->putBe32(color.transfer);
    stream->putBe32(color.space);
}

void MediaSnapshotState::loadColor(base::Stream* stream,
                                   ColorAspects& color) const {
    color.primaries = stream->getBe32();
    color.range = stream->getBe32();
    color.transfer = stream->getBe32();
    color.space = stream->getBe32();
}

void MediaSnapshotState::save(base::Stream* stream) const {
    saveVec(stream, sps);
    saveVec(stream, pps);
    stream->putBe32(savedPackets.size());
    for (size_t i = 0; i < savedPackets.size(); ++i) {
        savePacketInfo(stream, savedPackets[i]);
    }
    stream->putBe32(savedFrames.size());
    SNAPSTATE_DPRINT("save now ");
    for (auto iter = savedFrames.begin(); iter != savedFrames.end(); ++iter) {
        SNAPSTATE_DPRINT("save now ");
        saveFrameInfo(stream, *iter);
    }
    // saveFrameInfo(stream, savedDecodedFrame);
}

void MediaSnapshotState::load(base::Stream* stream) {
    loadVec(stream, sps);
    loadVec(stream, pps);
    int count = stream->getBe32();
    savedPackets.resize(count);
    for (int i = 0; i < count; ++i) {
        loadPacketInfo(stream, savedPackets[i]);
    }
    int fcount = stream->getBe32();
    SNAPSTATE_DPRINT("load now ");
    for (int i = 0; i < fcount; ++i) {
        SNAPSTATE_DPRINT("load now ");
        loadFrameInfo(stream, savedDecodedFrame);
        savedFrames.push_back(std::move(savedDecodedFrame));
    }
}

}  // namespace emulation
}  // namespace android
