/****************************************************************************
**
** 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 QDRAGONRENDERASPECT_H
#define QDRAGONRENDERASPECT_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.
//

// Backend types
#include <private/dragonbackendnode_p.h>
#include <private/dragonbuffer_p.h>
#include <private/dragoncameralens_p.h>
#include <private/dragoneffect_p.h>
#include <private/dragonentity_p.h>
#include <private/dragonfilterkey_p.h>
#include <private/dragonframegraphnode_p.h>
#include <private/dragonmanager_p.h>
#include <private/dragonmapper_p.h>
#include <private/dragonmaterial_p.h>
#include <private/dragonparameter_p.h>
#include <private/dragonrenderpass_p.h>
#include <private/dragonrendersettings_p.h>
#include <private/dragonrenderstatenode_p.h>
#include <private/dragonrendertarget_p.h>
#include <private/dragonrendertargetoutput_p.h>
#include <private/dragonrenderview_p.h>
#include <private/dragonshader_p.h>
#include <private/dragonshaderdata_p.h>
#include <private/dragontask_p.h>
#include <private/dragontechnique_p.h>
#include <private/dragontexture_p.h>
#include <private/dragontrackingchangescontainer_p.h>
#include <private/dragontransform_p.h>

// Helpers
#include <private/dragonimmutable_p.h>
#include <private/dragonvaluecontainer_p.h>

#include <private/dragonbufferjobs_p.h>
#include <private/dragonmaterialjobs_p.h>
#include <private/dragonrenderer_p.h>
#include <private/dragonrenderviewjobs_p.h>
#include <private/dragontexturejobs_p.h>

#include <Qt3DCore/QAbstractAspect>
#include <Qt3DCore/QAspectJob>
#include <Qt3DCore/QBackendNodeMapper>
#include <Qt3DCore/QComponentAddedChangePtr>
#include <Qt3DCore/QNodeCreatedChangeBasePtr>
#include <Qt3DCore/QNodeId>
#include <Qt3DCore/QPropertyUpdatedChangePtr>

#include <QMatrix4x4>
#include <QQueue>
#include <QSemaphore>
#include <QThread>

#include <unordered_map>

QT_BEGIN_NAMESPACE
namespace Qt3DRender {
namespace Dragon {

struct BoundingVolume;
//struct BoundingVolumes;
struct BoundingVolumeResult;
using BoundingVolumes = ValueContainer<BoundingVolumeResult>;

// TODO Add typename DirtyFlags which can differ depending on container, often just T::DirtyFlags

template<typename T, typename U>
using CacheValueContainer = CacheContainer<Immutable<T>, U>;

template<typename T>
using BasicContainer = TrackingChangesContainer<T>;

// aspect.h
class Q3DSV_EXPORT QDragonRenderAspect : public Qt3DCore::QAbstractAspect
{
    Q_OBJECT
public:
    QDragonRenderAspect(Renderer::RenderType renderType);
    Renderer::Frame renderSynchronous(Renderer::Frame frame);
    void initialize(QOpenGLContext *context);

    void endRenderShutdown();
    void beginRenderShutdown();
private:
    QVector<Qt3DCore::QAspectJobPtr> jobsToExecute(qint64 time) override;

    QVector<Qt3DCore::QAspectJobPtr> m_jobs;

    // Backend nodes
    NodeFunctorPtr<Entity> m_entities = NodeFunctorPtr<Entity>::create();
    NodeFunctorPtr<Transform> m_transforms = NodeFunctorPtr<Transform>::create();
    NodeFunctorPtr<Texture> m_textures = NodeFunctorPtr<Texture>::create();
    NodeFunctorPtr<TextureImage> m_textureImages = NodeFunctorPtr<TextureImage>::create();
    NodeFunctorPtr<Buffer> m_buffers = NodeFunctorPtr<Buffer>::create();
    NodeFunctorPtr<Parameter> m_parameters = NodeFunctorPtr<Parameter>::create();
    NodeFunctorPtr<Material> m_materials = NodeFunctorPtr<Material>::create();
    NodeFunctorPtr<Effect> m_effects = NodeFunctorPtr<Effect>::create();
    NodeFunctorPtr<Technique> m_techniques = NodeFunctorPtr<Technique>::create();
    NodeFunctorPtr<FilterKey> m_filterKeys = NodeFunctorPtr<FilterKey>::create();
    NodeFunctorPtr<Geometry> m_geometries = NodeFunctorPtr<Geometry>::create();
    NodeFunctorPtr<GeometryRenderer> m_geometryRenderers = NodeFunctorPtr<GeometryRenderer>::create();
    NodeFunctorPtr<Attribute> m_attributes = NodeFunctorPtr<Attribute>::create();
    NodeFunctorPtr<CameraLens> m_cameraLenses = NodeFunctorPtr<CameraLens>::create();
    NodeFunctorPtr<RenderTarget> m_renderTargets = NodeFunctorPtr<RenderTarget>::create();
    NodeFunctorPtr<RenderPass> m_renderPasses = NodeFunctorPtr<RenderPass>::create();
    NodeFunctorPtr<RenderTargetOutput> m_renderTargetOutputs
        = NodeFunctorPtr<RenderTargetOutput>::create();
    NodeFunctorPtr<Shader> m_shaders = NodeFunctorPtr<Shader>::create();
    NodeFunctorPtr<ShaderData> m_shaderDatas = NodeFunctorPtr<ShaderData>::create();
    NodeFunctorPtr<RenderStateNode> m_renderStates = NodeFunctorPtr<RenderStateNode>::create();

    // Frame graph
    ValueContainer<FrameGraphNode> m_frameGraphNodesContainer;
    NodeFunctorPtr<FrameGraphNode> m_frameGraphNodes = NodeFunctorPtr<FrameGraphNode>::create(
        &m_frameGraphNodesContainer);

    RenderSettings *m_renderSettings = nullptr;
    //    Qt3DCore::ResourceManager<Buffer> m_bufferManager;

    // TODO boilerplate, consider generating
    SourceTaskPtr<Qt3DCore::QNodeId> m_rootEntitySource = SourceTaskPtr<Qt3DCore::QNodeId>::create();
    SourceTaskPtr<Qt3DCore::QNodeId> m_rootFrameGraphNodeSource
        = SourceTaskPtr<Qt3DCore::QNodeId>::create();

    // TODO look into ways of declaring templates based on function spec
    // TODO or look for a way to store multiple tasks in a simple container
    TaskPtr<bool> m_resetJobs;
    TaskPtr<bool> m_uploadRenderViews;

    // General jobs
    TaskPtr<TreeInfo> m_generateFrameGraph;
    TaskPtr<ValueContainer<Matrix4x4>> m_calculateWorldTransforms;
    TaskPtr<BoundingVolumes> m_calculateBoundingVolumes;
    TaskPtr<int> m_printTransforms;
    TaskPtr<LoadedTextures> m_loadTextures;
    TaskPtr<LoadedTextureImages> m_loadTextureImages;
    //    TaskPtr<BasicContainer<QTextureImageDataPtr>> m_loadTextures;
    TaskPtr<LoadedBuffers> m_loadBuffers;
    TaskPtr<ValueContainer<GLShader>> m_uploadedShaders;
    TaskPtr<GatheredParameters> m_gatherParameters;
    TaskPtr<GraphicsApiFilterData> m_requestContextInfo;
    TaskPtr<RenderViews> m_buildRenderViews;
    TaskPtr<RenderCommands> m_buildRenderCommands;
    TaskPtr<AppliedRenderCommandss> m_applyParameters;
    QScopedPointer<Renderer> m_renderer;

    friend class RenderSettingsFunctor;
};

} // namespace Dragon

} // namespace Qt3DRender

QT_END_NAMESPACE

#endif // QDRAGONRENDERASPECT_H
