/// Copyright (c) 2010 Ruben Penalva. All rights reserved.
/// Redistribution and use in source and binary forms, with or without modification, 
/// are permitted provided that the following conditions are met:
///    1. Redistributions of source code must retain the above copyright notice, this 
///    list of conditions and the following disclaimer.
///    2. Redistributions in binary form must reproduce the above copyright notice,
///    this list of conditions and the following disclaimer in the documentation and/or
///    other materials provided with the distribution.
///    3. The name of the author may not be used to endorse or promote products derived
///    from this software without specific prior written permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
/// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
/// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 
/// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
/// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
/// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
/// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
/// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef _OPENGL_DRIVER_H_
#define _OPENGL_DRIVER_H_

#include <boost\utility.hpp>

#include <windows.h>

#include "OpenglContext.h"
#include "OpenglVertexBuffer.h"
#include "OpenglIndexBuffer.h"
#include "OpenglProgram.h"
#include "OpenglTexture.h"

#include <vector>
#include <map>

/// TODO
/// - Draw screen space text

namespace TestFrameWork
{

enum OpenglPrimitive
{
	Triangles
};

enum OpenglVersion
{
	OpenglVersion_2_0,
	OpenglVersion_2_1,
	OpenglVersion_3_0,
	OpenglVersion_3_1,
	OpenglVersion_3_2,
};

class OpenglDriver :
	boost::noncopyable
{
public:
	OpenglDriver(OpenglVersion version, HDC canvasContext, size_t width, size_t height, int depth, int samples);

	~OpenglDriver(void);

	void BeginFrame(void);

	void EndFrame(void);

	size_t CreateVertexBuffer(Vertices vertices, size_t verticesCount);
	
	size_t CreateIndexBuffer(Indices32 indices, size_t indicesCount);

	OpenglProgramWeakPtr CreateProgram(const std::vector<ShaderFileDesc>& shaders);
	
	OpenglTextureWeakPtr CreateTexture(TextureData data, OpenglTextureFormat format, 
									   size_t width, OpenglTextureFilter filter, 
									   OpenglTextureEdgeValue edge);

	OpenglTextureWeakPtr CreateTexture(TextureData data, OpenglTextureFormat format, 
									   size_t width, size_t height, OpenglTextureFilter filter, 
									   OpenglTextureEdgeValue edge);

	OpenglTextureWeakPtr CreateTexture(TextureData data, OpenglTextureFormat format, 
									   size_t width, size_t height, size_t depth, 
									   OpenglTextureFilter filter, OpenglTextureEdgeValue edge);

	void SetProgram(OpenglProgramWeakPtr program);

	void SetTexture(OpenglTextureWeakPtr texture, int textureUnit);

	void DrawIndexedPrimitive(OpenglPrimitive primitive, size_t vertexBufferId, size_t indexBufferId,
							  size_t primitivesCount = 1);

	void GetMaximumTextureSize(OpenglTextureFormat format, size_t& width, size_t& height);

	void Draw2DString(const std::string& s, size_t x, size_t y, const Vector3& color);

private:
	OpenglVersion m_version;

	HDC m_canvasContext;
	size_t m_width;
	size_t m_height;

	OpenglContext m_openglContext;

	std::vector<OpenglVertexBufferPtr> m_vertexBuffers;
	
	std::vector<OpenglIndexBuffer32Ptr> m_indexBuffers;

	std::vector<OpenglProgramPtr> m_programs;

	std::vector<OpenglTexturePtr> m_textures;

	OpenglProgramWeakPtr m_currentProgram;

	std::map<OpenglTextureWeakPtr, int> m_textureUnits;

	void InitializeGlew(void);
	
	void InitializeGlut(void);

	void CheckOpenglVersion(OpenglVersion version) const;

	void CheckMinRequirements(OpenglVersion version) const;

	GLenum Primitive2GlMode(OpenglPrimitive primitive) const;

	void SetCurrentProgram(void) const;

	void SetCurrentTextures(void) const;

	void SetBuffers(OpenglVertexBufferPtr vertexBuffer, OpenglIndexBuffer32Ptr indexBuffer) const;
};

typedef boost::shared_ptr<OpenglDriver> OpenglDriverPtr;
}

#endif