#pragma once
#include "Core/Graphics/Color.h"
#include "Core/WeakSet.h"
#include "Resource.h"
#include "Pipeline.h"
#include "Shader.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"

namespace gui {

	/**
	 * A 3D graphics object. Used together with Painter3D.
	 */
	class Graphics3D : public ObjectOn<Render> {
		STORM_ABSTRACT_CLASS;
	public:
		// Create.
		STORM_CTOR Graphics3D(Nat id);

		// Destroy.
		~Graphics3D();

		// Destroy resources.
		virtual void STORM_FN destroy();

		// Fill the entire surface with a solid color. Useful to clear the background color before
		// rendering. Not done automatically, since it is not needed if we know that we will fill
		// all pixels with other content anyway.
		virtual void STORM_FN fill(Color color) ABSTRACT;

		// Clear the depth buffer. The depth buffer is automatically cleared to the far-plane for
		// each frame.
		virtual void STORM_FN clearDepth() ABSTRACT;


		/**
		 * Object that allows setting up parameters when rendering a pipeline.
		 *
		 * Consider this object to expose more of the rendering API rather than allowing multiple
		 * render stages to be set-up at the same time. The underlying implementation is likely to
		 * re-use the same object for each call to `beginRender`.
		 */
		class RenderParams : public ObjectOn<Render> {
			STORM_ABSTRACT_CLASS;
		public:
			// Create.
			STORM_CTOR RenderParams() {}

			// Bind buffers.
			virtual void STORM_FN bind(Nat id, VertexBufferBase *vbuf) ABSTRACT;

			// Bind index buffer.
			virtual void STORM_FN bind(IndexBufferBase *index) ABSTRACT;

			// Set primitive type. Only useful if an index buffer was *not* bound.
			virtual void STORM_FN primitive(DrawPrimitive p) ABSTRACT;

			// Render using the pipeline.
			virtual void STORM_FN done() ABSTRACT;
		};


		// Called before rendering starts.
		virtual void beforeRender();

		// Called after rendering is complete.
		virtual Bool afterRender();

		// Called when the surface is about to be destroyed.
		virtual void surfaceDestroyed();

		// Get the ID of this Graphics class.
		Nat id() const { return identifier; }

		// Attach a resource to this graphics object.
		Bool STORM_FN attach(Resource3D *resource);

	public:
		// Resource management. Called by Resource3D to create resources specific to this graphics
		// backend.
		virtual void createResource(Resource3D *resource, void *&data, Resource3D::Cleanup &cleanup);
		virtual void updateResource(Resource3D *resource, void *data, const DirtyRange &dirty);

	protected:
		// Called by the framework to render a pipeline. Prefer to use `<pipeline>.render()` when
		// possible.
		//
		// Returns a RenderParams object that allows binding buffers etc. Note, even though a
		// separate object is returned, this object should only be considered to expose more of the
		// API. It does not allow multiple render setup-calls to be in-flight at the same time.
		virtual RenderParams *STORM_FN beginRender(Pipeline *pipeline) ABSTRACT;

	private:
		// Identifier of the backend.
		Nat identifier;

		// Resources attached to this graphics object.
		WeakSet<Resource3D> *resources;
	};

}


// Macro to define all members of a Graphics subclass as no-ops. Used to compile the Storm classes for
// an OS-specific backend where that backend is not available.
#define GRAPHICS3D_DEFINES(graphics, params, paramsMember)				\
	void graphics::beforeRender() {}									\
	Bool graphics::afterRender() { return false; }						\
	void graphics::fill(Color) {}										\
	void graphics::clearDepth() {}										\
	Graphics3D::RenderParams *graphics::beginRender(Pipeline *) {		\
		return paramsMember;											\
	}																	\
	void graphics::createResource(Resource3D *, void *&, Resource3D::Cleanup &) {} \
	void graphics::updateResource(Resource3D *, void *, const DirtyRange &dirty) {}	\
	void params::bind(Nat, VertexBufferBase *) {}						\
	void params::bind(IndexBufferBase *) {}								\
	void params::primitive(DrawPrimitive) {}							\
	void params::done() {}
