// Copyright (C) 2024 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 <gmock/gmock.h>
#include <gtest/gtest.h>

#include "KumquatInstance.h"

#include <unistd.h>

#include <filesystem>

#include "aemu/base/Path.h"

using testing::Eq;

namespace gfxstream {
namespace tests {

KumquatInstance::KumquatInstance() {}

void KumquatInstance::SetUp(bool withGl, bool withVk, std::string features) {
    const std::filesystem::path testDirectory = gfxstream::guest::getProgramDirectory();
    const std::string kumquatCommand = (testDirectory / "kumquat").string();
    const std::string renderer_features = "--renderer-features=" + features;

    std::string capset_names = "--capset-names=";
    if (withGl) {
        capset_names.append("gfxstream-gles:");
    }
    if (withVk) {
        capset_names.append("gfxstream-vulkan:");
    }

    int fds[2];
    ASSERT_THAT(pipe(fds), Eq(0));

    // VirtGpuKumquatDevice.cpp by default connects to "/tmp/kumquat-gpu-0".  If this changes,
    // the correct socket path must be plumbed through to VirtGpuKumquatDevice.cpp
    const std::string gpu_socket_path = "/tmp/kumquat-gpu-" + std::to_string(0);

    const std::string gpu_socket_cmd = "--gpu-socket-path=" + gpu_socket_path;

    const std::string pipe_descriptor = "--pipe-descriptor=" + std::to_string(fds[1]);

    pid_t pid = fork();
    if (pid == 0) {
        close(fds[0]);
        execl(kumquatCommand.c_str(), kumquatCommand.c_str(), gpu_socket_cmd.c_str(),
              capset_names.c_str(), renderer_features.c_str(), pipe_descriptor.c_str(), nullptr);
        exit(0);
    } else {
        close(fds[1]);
        uint64_t count = 0;
        ssize_t bytes_read = read(fds[0], &count, sizeof(count));
        // Kumquat writes [uint64_t](1) to the write end of the pipe..
        ASSERT_THAT(bytes_read, Eq(8));
        close(fds[0]);
        ASSERT_THAT(virtgpu_kumquat_init(&mVirtGpu, gpu_socket_path.c_str()), Eq(0));
        mKumquatPid = pid;
    }
}

KumquatInstance::~KumquatInstance() {
    virtgpu_kumquat_finish(&mVirtGpu);
    kill(mKumquatPid, SIGKILL);
    int status = 0;
    pid_t pid = waitpid(mKumquatPid, &status, WNOHANG);
    while (!pid) {
        pid = waitpid(mKumquatPid, &status, WNOHANG);
    }
}

void KumquatInstance::Snapshot() { ASSERT_THAT(virtgpu_kumquat_snapshot_save(mVirtGpu), Eq(0)); }

void KumquatInstance::Restore() { ASSERT_THAT(virtgpu_kumquat_snapshot_restore(mVirtGpu), Eq(0)); }

}  // namespace tests
}  // namespace gfxstream
