/*
* $Id: rendertarget.h 873 2009-11-10 12:14:21Z stezkmil $
*
* Description  : One line description of file.
* Author       : Milan Stezka
*
* Purpose      :
*     Long description of what the file is for.
*/


#ifndef __RENDERTARGET_H__
#define __RENDERTARGET_H__

#include <stdlib.h>
#ifndef VRUT_HPC
#include <GL/glew.h>
#else
#include "OGLconstants.h"
#endif
#include <vector>
#include <set>
#include <wx/thread.h>

#include "countable.h"

#include "VrutApi.h"

namespace VRUT
{
	class RenderModule;

	struct VRUT_CORE_API RenderTarget : public Countable
	{
		typedef std::vector<GLenum> AttachmentPointVector;

		class TextureAttachmentParams
		{
		public:
		private:
			GLsizei width, height;
			GLint internalFormat;
			AttachmentPointVector attachmentPoint;
			GLenum format, type;
			GLsizei samples;
			GLboolean fixedSampleLocations;

		public:
			TextureAttachmentParams(GLsizei width, GLsizei height, GLint internalFormat, const AttachmentPointVector& attachmentPoint, GLenum format, GLenum type,  GLsizei samplesCount = 0, GLboolean fixedSampleLocations = GL_FALSE) :
			  width(width), height(height), internalFormat(internalFormat), attachmentPoint(attachmentPoint), format(format), type(type), samples(samplesCount), fixedSampleLocations(fixedSampleLocations) { }

			TextureAttachmentParams(GLsizei width, GLsizei height, GLint internalFormat, GLenum attachmentPoint, GLenum format, GLenum type, GLsizei samplesCount = 0, GLboolean fixedSampleLocations = GL_FALSE) :
			  width(width), height(height), internalFormat(internalFormat), format(format), type(type), samples(samplesCount), fixedSampleLocations(fixedSampleLocations)
			{
				this->attachmentPoint.push_back(attachmentPoint);
			}

			const GLsizei& GetWidth() const { return width; }
			const GLsizei& GetHeight() const { return height; }
			const GLsizei& GetSamplesCount() const { return samples; }
			const GLint& GetInternalFormat() const { return internalFormat; }
			const AttachmentPointVector& GetAttachment() const { return attachmentPoint; }
			const GLenum& GetFormat() const { return format; }
			const GLenum& GetType() const { return type; }
			const GLboolean& GetFixedSampleLocations() const { return fixedSampleLocations; }
			void AddAttachment(GLenum attachmentPoint) { this->attachmentPoint.push_back(attachmentPoint); }
		};

		enum AntiAliasingType
		{
			NONE=0,
			MULTI_SAMPLING,
			COVERAGE_SAMPLING
		};

		class RenderBufferAttachmentParams
		{
		public:
			
		private:
			GLsizei width, height;
			GLint internalFormat;
			GLint samples, coverageSamples;
			AttachmentPointVector attachmentPoint;
			AntiAliasingType antiAliasingType;

		public:
			RenderBufferAttachmentParams(GLsizei width, GLsizei  height, GLint internalFormat, GLint samples, GLint coverageSamples, const AttachmentPointVector& attachmentPoint, AntiAliasingType antiAliasingType) :
			  width(width), height(height), internalFormat(internalFormat), samples(samples), coverageSamples(coverageSamples), attachmentPoint(attachmentPoint), antiAliasingType(antiAliasingType)
			  {
			  }

			  RenderBufferAttachmentParams(GLsizei width, GLsizei  height, GLint internalFormat, GLint samples, GLint coverageSamples, GLenum attachmentPoint, AntiAliasingType antiAliasingType) :
			  width(width), height(height), internalFormat(internalFormat), samples(samples), coverageSamples(coverageSamples), antiAliasingType(antiAliasingType)
			  {
				  this->attachmentPoint.push_back(attachmentPoint);
			  }

			  const GLsizei& GetWidth() const { return width; }
			  const GLsizei& GetHeight() const { return height; }
			  const GLint& GetInternalFormat() const { return internalFormat; }
			  const GLint& GetSamples() const { return samples; }
			  const GLint& GetCoverageSamples() const { return coverageSamples; }
			  const AttachmentPointVector& GetAttachment() const { return attachmentPoint; }
			  const AntiAliasingType& GetAntiAliasingType() const { return antiAliasingType; }
			  void AddAttachment(GLenum attachmentPoint) { this->attachmentPoint.push_back(attachmentPoint); }
		};

		struct TextureAttachment
		{
			TextureAttachmentParams params;
			GLuint object;
			typedef std::set<RenderTarget*> RenderTargetSet;
			RenderTargetSet usedBy;
			
			TextureAttachment(const TextureAttachmentParams &params, GLuint object, RenderTarget *creator) : params(params), object(object)
			{
				usedBy.insert(creator);
			}
		};

		struct RenderBufferAttachment
		{
			RenderBufferAttachmentParams params;
			GLuint object;
			typedef std::set<RenderTarget*> RenderTargetSet;
			RenderTargetSet usedBy;
			
			RenderBufferAttachment(const RenderBufferAttachmentParams &params, GLuint object, RenderTarget *creator) :
			params(params),
				object(object)
			{
				usedBy.insert(creator);
			}
		};

	protected:

		typedef std::vector<TextureAttachment*> TextureAttachmentVector;
		typedef std::vector<RenderBufferAttachment*> RenderBufferAttachmentVector;
		///Multiple modules can simultaneously write data to attachments through AddTextureAttachment, RemoveAttachment and maybe other methods, so an access control is needed.
		wxCriticalSection attachmentMutex;
		TextureAttachmentVector textureAttachment;
		RenderBufferAttachmentVector renderBufferAttachment;
		GLuint frameBufferObject;
		GLint textureUnitUniformLocation;

	public:

		RenderTarget() : frameBufferObject(0), textureUnitUniformLocation(-1) {}

		RenderTarget(const RenderTarget& renderTarget) : Countable(renderTarget), textureAttachment(renderTarget.textureAttachment),
				renderBufferAttachment(renderTarget.renderBufferAttachment), frameBufferObject(renderTarget.frameBufferObject),
				textureUnitUniformLocation(renderTarget.textureUnitUniformLocation) {}

		~RenderTarget()
		{
#ifndef VRUT_HPC
			if (frameBufferObject)
				DeleteFrameBufferObject();
#endif
		}

		void Bind();
		static void Unbind();
		GLenum CheckFrameBufferObjectStatus();
		void SetTextureUnitUniformLocation(GLint textureUnitUniformLocation) { this->textureUnitUniformLocation = textureUnitUniformLocation; }
		void AddCubeTextureAttachment(const TextureAttachmentParams& params, size_t position = (size_t)-1);
		GLenum AddTextureAttachment(const TextureAttachmentParams& params, size_t position = (size_t)-1);
		GLenum AddTextureAttachment(TextureAttachment& existingAttachment, size_t position = (size_t)-1);
		GLenum AddExistingTextureAttachment(GLuint texture, const TextureAttachmentParams& params, size_t position = (size_t)-1);
		GLenum AddRenderBufferAttachment(const RenderBufferAttachmentParams& params, size_t position = (size_t)-1);
		GLenum AddRenderBufferAttachment(RenderBufferAttachment& existingAttachment, size_t position = (size_t)-1);
		void ResizeAttachments(GLsizei width, GLsizei height);
		void RemoveTextureAttachment(TextureAttachment *existingAttachment);
		void RemoveRenderBufferAttachment(RenderBufferAttachment *existingAttachment);
		void DeleteAllTextureAttachments();
		void DeleteAllRenderBufferAttachments();
		TextureAttachment * GetTextureAttachmentOfType(GLenum type) const;
		const RenderBufferAttachment * GetRenderBufferAttachmentOfType(GLenum type) const;
		GLint GetTextureUnitUniformLocation() const { return textureUnitUniformLocation; }
		GLuint GetFrameBufferObject() const { return frameBufferObject; }
		void DeleteFrameBufferObject();
	};

}

#endif
