// Copyright 2014-2015 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.

#ifndef ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H
#define ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H

#include "render-utils/render_api.h"

#include "aemu/base/synchronization/MessageChannel.h"
#include "aemu/base/threads/FunctorThread.h"
#include "aemu/base/threads/Thread.h"

namespace gfxstream {

class RenderWindowChannel;
struct RenderWindowMessage;

// Helper class used to manage the sub-window that displays the emulated GPU
// output. To use it, do the following:
//
//  1) Create a new instance, passing the size of the emulated accelerated
//     framebuffer in pixels you need.
//
//  2) Check isValid() after construction. If false, the library could not
//     initialize the class properly, and one should abort.
//
//  3) Optional: call setPostCallback() to specify a callback function which
//     will be called everytime a new frame is drawn.
//
//  4) Call setupSubWindow() to setup a new sub-window within the UI window.
//     One can call removeSubWindow() to remove it, and one can call
//     setupSubWindow() + removeSubWindow() any number of time (e.g. for
//     changing the position / rotation of the subwindow).
//
//  5) Optional: call setRotation() to only change the display rotation of
//     the sub-window content.
//
//  6) Call repaint() to force a repaint().
//
class RenderWindow {
public:
    // Create new instance. |width| and |height| are the dimensions of the
    // emulated accelerated framebuffer. |use_thread| can be true to force
    // the use of a separate thread, which might be required on some platforms
    // to avoid GL-realted corruption issues in the main window. Call
    // isValid() after construction to verify that it worked properly.
    //
    // |use_sub_window| is true if the client will call setupSubWindow(),
    // and false if it will call setPostCallback().
    //
    // Note that this call doesn't display anything, it just initializes
    // the library, use setupSubWindow() to display something.
    RenderWindow(int width, int height, bool use_thread, bool use_sub_window,
            bool egl2egl);

    // Destructor. This will automatically call removeSubWindow() is needed.
    ~RenderWindow();

    // Returns true if the RenderWindow instance is valid, which really
    // means that the constructor succeeded.
    bool isValid() const { return mValid; }

    // Return misc. GL strings to the caller. On success, return true and sets
    // |*vendor| to the GL vendor string, |*renderer| to the GL renderer one,
    // and |*version| to the GL version one. On failure, return false.
    bool getHardwareStrings(const char** vendor,
                            const char** renderer,
                            const char** version);

    // Specify a function that will be called everytime a new frame is
    // displayed. This is relatively slow but allows one to capture the
    // output.
    void setPostCallback(Renderer::OnPostCallback onPost, void* onPostContext, uint32_t displayId,
                         bool useBgraReadback = false);

    bool asyncReadbackSupported();
    Renderer::ReadPixelsCallback getReadPixelsCallback();
    Renderer::FlushReadPixelPipeline getFlushReadPixelPipeline();

    // Start displaying the emulated framebuffer using a sub-window of a
    // parent |window| id. |wx|, |wy|, |ww| and |wh| are the position
    // and dimension of the sub-window, relative to its parent.
    // |fbw| and |fbh| are the dimensions of the underlying guest framebuffer.
    // |dpr| is the device pixel ratio for the monitor, which is required for
    // higher-density displays (such as retina).
    // |rotation| is a clockwise-rotation for the content. Only multiples of
    // 90. are accepted. Returns true on success, false otherwise.
    //
    // If the subwindow already exists, this function will update
    // the dimensions of the subwindow, backing framebuffer, and rendering
    // pipeline to reflect the new values.
    //
    // One can call removeSubWindow() to remove the sub-window.
    bool setupSubWindow(FBNativeWindowType window,
                        int wx,
                        int wy,
                        int ww,
                        int wh,
                        int fbw,
                        int fbh,
                        float dpr,
                        float rotation,
                        bool deleteExisting,
                        bool hideWindow);

    // Remove the sub-window created by calling setupSubWindow().
    // Note that this doesn't discard the content of the emulated framebuffer,
    // it just hides it from the main window. Returns true on success, false
    // otherwise.
    bool removeSubWindow();

    // Change the display rotation on the fly. |zRot| is a clockwise rotation
    // angle in degrees. Only multiples of 90. are accepted.
    void setRotation(float zRot);

    // Change the display translation. |px|,|py| are numbers between 0 and 1,
    // with (0,0) indicating "align the bottom left of the framebuffer with the
    // bottom left of the subwindow", and (1,1) indicating "align the top right of
    // the framebuffer with the top right of the subwindow."
    void setTranslation(float px, float py);

    // Receive a screen mask and pass it to TextureDraw
    void setScreenMask(int width, int height, const unsigned char* rgbaData);

    // Force a repaint of the whole content into the sub-window.
    void repaint();

    // Returns whether or not the guest posted a frame. For checking emulator
    // liveness.
    bool hasGuestPostedAFrame();
    // Resets whether the guest has posted a frame.
    void resetGuestPostedAFrame();

    void setPaused(bool paused);

    void addListener(Renderer::FrameBufferChangeEventListener* listener);
    void removeListener(Renderer::FrameBufferChangeEventListener* listener);

    void setVsyncHz(int vsyncHz);
    void setDisplayConfigs(int configId, int w, int h, int dpiX, int dpiY);
    void setDisplayActiveConfig(int configId);
private:
    bool processMessage(const RenderWindowMessage& msg);
    bool useThread() const { return mThread != nullptr; }

    bool mValid = false;
    bool mHasSubWindow = false;
    android::base::Thread* mThread = nullptr;
    RenderWindowChannel* mChannel = nullptr;

    // A worker thread to run repost() commands asynchronously.
    enum class RepostCommand : char {
        Repost, Sync
    };
    android::base::MessageChannel<RepostCommand, 10> mRepostCommands;
    android::base::FunctorThread mRepostThread;

    bool mPaused = false;
};

}  // namespace gfxstream

#endif  // ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H
