/****************************************************************************
**
** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of Qt 3D Studio.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef DRAGONRENDERER_P_H
#define DRAGONRENDERER_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists for the convenience
// of other Qt classes.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

// Mostly needed for the types like LoadedBuffers
#include <private/dragonbufferjobs_p.h>
#include <private/dragonrenderviewjobs_p.h>
#include <private/dragontexturejobs_p.h>

//
#include <private/dragonoffscreensurfacehelper_p.h>
#include <private/dragonopenglvertexarrayobject_p.h>

#include <QAtomicInt>
#include <QElapsedTimer>
#include <QMutex>
#include <QQueue>
#include <QScopedPointer>
#include <QSemaphore>

QT_BEGIN_NAMESPACE

namespace Qt3DRender {

namespace Dragon {

// renderer.h
struct Scene2DState;
struct GLBuffer;
struct GLShader;
struct GLTexture;
class Renderer;
class GraphicsHelperInterface;

class RenderThread : public QThread
{
    Q_OBJECT
public:
    explicit RenderThread(Renderer *renderer);
    void waitForStart(Priority priority = InheritPriority);

protected:
    void run() override;

private:
    Renderer *m_renderer = nullptr;
    QSemaphore m_semaphore;
};

template<typename T>
struct ExplicitChangeWrapper
{
    std::unique_ptr<T> operator->() {}
    std::unique_ptr<T> m_data;
};

struct FrameInput
{
    RenderViews renderViews;
    ValueContainer<CameraMatrices> renderViewCameraMatrices;
    RenderCommands renderCommands;
    LoadedTextures loadedTextures;
    LoadedBuffers loadedBuffers;
    ValueContainer<Attribute> attributes;
    ValueContainer<Parameter> parameters;
    ValueContainer<Matrix4x4> worldTransforms;
    ValueContainer<Shader> shaders;
    ValueContainer<Scene2DState> scene2ds;
    TreeInfo frameGraph;
};

// TODO move to separate file
struct GLRenderTarget
{
    GLuint frameBufferObjectId = 0;
    QSize size;
};

class Renderer
{
public:
    /*!
     * \brief The Frame struct holds data that is shared across frames,
     * such as already uploaded textures and active textures.
     */
    struct Frame
    {
        int maxTextureImageUnits;
        // TODO consider adding OpenGLState here
        MutableContainer<GLTexture> uploadedTextures;
        MutableContainer<GLShader> uploadedShaders;
        MutableContainer<GLBuffer> uploadedBuffers;
        QHash<VAOIdentifier, Immutable<GLVertexArrayObject>> uploadedVaos;
        // TODO could be ValueContainer?
        QHash<Qt3DCore::QNodeId, GLRenderTarget> createdRenderTargets;
        int number = 0;
    };

    // TODO a cleaner option is to move all thread-specific code into a separate class
    // instead of letting one class handle both cases
    enum RenderType {
        Synchronous,
        Threaded
    };

    Renderer(RenderType renderType);
    ~Renderer();

    void render();
    Frame doRender(Frame previousFrame);

    // Data from the aspect jobs are set and gotten with these
    void addLatestData(FrameInput data);
    GraphicsApiFilterData contextInfo(RenderViews views);

    void initialize(QOpenGLContext *context = nullptr);

    QSemaphore nextFrameSemaphore;

    QOpenGLContext *openGLContext() const;
    QOpenGLContext *shareContext() const;

    void beginShutdown();
    void endShutdown();

    const QHash<Qt3DCore::QNodeId, QSharedPointer<QOpenGLTexture>> &textures() const
    {
        return m_snaggedTextures;
    }
    void clear()
    {
        m_snaggedTextures.clear();
    }

private:

    void checkContextInfoRequested();

    // Render thread
    QScopedPointer<RenderThread> m_renderThread;
    QAtomicInt m_running;

    // Render views
    QMutex m_latestDataMutex;

    // Context
    QMutex m_shareContextMutex;
    bool m_ownedContext = false;
    bool m_ownedShareContext = false;

    // TODO Protect behind a read-only barrier with a mutex, similar to RenderQueue
    QQueue<FrameInput> m_latestData;

    // TODO any way to reduce the number of members on this class?
    QHash<QSurface *, QSharedPointer<Dragon::GraphicsHelperInterface>> m_glHelpers;

    // Context information
    QOpenGLContext *m_glContext = nullptr;
    QOpenGLContext *m_shareContext = nullptr;

    bool m_contextInfoRequested = false;
    RenderViews m_requestedContextInfoViews;
    QSemaphore m_writeContextInfoSemaphore;
    QSemaphore m_readContextInfoSemaphore;
    GraphicsApiFilterData m_contextInfo;

    QSemaphore m_waitForInitializationToBeCompleted;

    QElapsedTimer m_elapsedTimer;

    OffscreenSurfaceHelper *m_offscreenHelper;

    // List of textures and their ids after they have been uploaded
    QHash<Qt3DCore::QNodeId, QSharedPointer<QOpenGLTexture>> m_snaggedTextures;

    RenderType m_renderType;
};

} // namespace Dragon
} // namespace Qt3DRender
QT_END_NAMESPACE

#endif // DRAGONRENDERER_P_H
