// Copyright (C) 2016 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.
#pragma once

#include "aemu/base/EnumFlags.h"
#include "aemu/base/containers/SmallVector.h"
#include "aemu/base/files/Stream.h"
#include "aemu/base/containers/BufferQueue.h"

#include <functional>
#include <memory>

namespace gfxstream {

// Turn the RenderChannel::State enum into flags.
using namespace ::android::base::EnumFlags;

// RenderChannel - For each guest-to-host renderer connection, this provides
// an interface for the guest side to interact with the corresponding renderer
// thread on the host. Its main purpose is to send and receive wire protocol
// bytes in an asynchronous way (compatible with Android pipes).
//
// Usage is the following:
//    1) Get an instance pointer through a dedicated Renderer function
//       (e.g. RendererImpl::createRenderChannel()).
//
//    2) Call setEventCallback() to indicate which callback should be called
//       when the channel's state has changed due to a host thread event.
//
class RenderChannel {
public:
    // A type used to pass byte packets between the guest and the
    // RenderChannel instance. Experience has shown that using a
    // SmallFixedVector<char, N> instance instead of a std::vector<char>
    // avoids a lot of un-necessary heap allocations. The current size
    // of 512 was selected after profiling existing traffic, including
    // the one used in protocol-heavy benchmark like Antutu3D.
    using Buffer = android::base::SmallFixedVector<char, 512>;

    // Bit-flags for the channel state.
    // |CanRead| means there is data from the host to read.
    // |CanWrite| means there is room to send data to the host.
    // |Stopped| means the channel was stopped.
    enum class State {
        // Can't use None here, some system header declares it as a macro.
        Empty = 0,
        CanRead = 1 << 0,
        CanWrite = 1 << 1,
        Stopped = 1 << 2,
    };

    using IoResult = android::base::BufferQueueResult;
    using Duration = uint64_t;

    // Type of a callback used to tell the guest when the RenderChannel
    // state changes. Used by setEventCallback(). The parameter contains
    // the State bits matching the event, i.e. it is the logical AND of
    // the last value passed to setWantedEvents() and the current
    // RenderChannel state.
    using EventCallback = std::function<void(State)>;

    // Sets a single (!) callback that is called when the channel state's
    // changes due to an event *from* *the* *host* only. |callback| is a
    // guest-provided callback that will be called from the host renderer
    // thread, not the guest one.
    virtual void setEventCallback(EventCallback&& callback) = 0;

    // Used to indicate which i/o events the guest wants to be notified
    // through its StateChangeCallback. |state| must be a combination of
    // State::CanRead or State::CanWrite only. This will *not* call the
    // callback directly since this happens in the guest thread.
    virtual void setWantedEvents(State state) = 0;

    // Get the current state flags.
    virtual State state() const = 0;

    // Try to writes the data in |buffer| into the channel. On success,
    // return IoResult::Ok and moves |buffer|. On failure, return
    // IoResult::TryAgain if the channel was full, or IoResult::Error
    // if it is stopped.
    virtual IoResult tryWrite(Buffer&& buffer) = 0;

    // Blocking call that waits until able to write into the channel.
    virtual void waitUntilWritable() = 0;

    // Try to read data from the channel. On success, return IoResult::Ok and
    // sets |*buffer| to contain the data. On failure, return
    // IoResult::TryAgain if the channel was empty, or IoResult::Error if
    // it was stopped.
    virtual IoResult tryRead(Buffer* buffer) = 0;

    // Try to read data from the channel. On success, return IoResult::Ok and
    // sets |*buffer| to contain the data. On failure, return IoResult::Error
    // if it was stopped. Returns IoResult::Timeout if we waited passed
    // waitUntilUs.
    virtual IoResult readBefore(Buffer* buffer, Duration waitUntilUs) = 0;

    // Blocking call that waits until data is available to read from the channel.
    virtual void waitUntilReadable() = 0;

    // Abort all pending operations. Any following operation is a noop.
    // Once a channel is stopped, it cannot be re-started.
    virtual void stop() = 0;

    // Callback function when snapshotting the virtual machine.
    virtual void onSave(android::base::Stream* stream) = 0;

protected:
    ~RenderChannel() = default;
};

// Shared pointer to RenderChannel instance.
using RenderChannelPtr = std::shared_ptr<RenderChannel>;

}  // namespace gfxstream
