#pragma once
#include "Gui/3D/Graphics.h"
#include "Texture.h"
#include "Buffer.h"
#include "Shader.h"

namespace gui {

	class GLSurface;
	class GLVertexShader;
	class GLPixelShader;
	class GLProgram;
	class GLBuffer;
	class GLTexture;

	class GLGraphics : public Graphics3D {
		STORM_CLASS;
	public:
		// Create.
		GLGraphics(GLSurface &surface, Nat id);

		// Before/after rendering.
		virtual void beforeRender();
		virtual Bool afterRender();

		// Fill the surface.
		virtual void STORM_FN fill(Color color);

		// Fill the depth buffer.
		virtual void STORM_FN clearDepth();

		/**
		 * Rendering parameters.
		 */
		class GLParams : public Graphics3D::RenderParams {
			STORM_CLASS;
		public:
			friend class GLGraphics;

			// Create.
			GLParams(GLGraphics *graphics);

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

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

			// Set primitive type.
			virtual void STORM_FN primitive(DrawPrimitive p);

			// Render.
			void STORM_FN done();

		private:
			// Current graphics object.
			GLGraphics *graphics;

			// Created program. Contains all shaders.
			GLProgram *program;

			// Bound vertex buffers.
			GcArray<GLBuffer *> *buffers;

			// Bound texture units.
			GcArray<GLTexture *> *textures;

			// Use indexed rendering.
			GLBuffer *indexBuffer;

			// Number of vertices to draw.
			Nat numVertices;

			// Primitive to draw.
			DrawPrimitive topology;

			// Clear the state here.
			void clear();
		};

		// Resource management.
		virtual void createResource(Resource3D *resource, void *&data, Resource3D::Cleanup &cleanup);
		virtual void updateResource(Resource3D *resource, void *data, const DirtyRange &dirty);

	protected:
		// Render a pipeline.
		virtual Graphics3D::RenderParams *STORM_FN beginRender(Pipeline *pipeline);

	private:
		// The surface itself.
		GLSurface &surface;

		// Cached instance of rendering parameters, so that we can avoid allocations.
		GLParams *renderParams;

		// Make the current context and framebuffer current.
		void makeCurrent();
	};

}
