#ifndef _GLUSHADERPROGRAM_HPP
#define _GLUSHADERPROGRAM_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES Utilities
 * ------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Shader and Program helpers.
 *//*--------------------------------------------------------------------*/

#include "gluDefs.hpp"
#include "gluShaderUtil.hpp"
#include "qpTestLog.h"

#include <string>
#include <vector>

namespace tcu
{
class TestLog;
}

namespace glu
{

class RenderContext;

/*--------------------------------------------------------------------*//*!
 * \brief Shader information (compile status, log, etc.).
 *//*--------------------------------------------------------------------*/
struct ShaderInfo
{
	ShaderType				type;			//!< Shader type.
	std::string				source;			//!< Shader source.
	std::string				infoLog;		//!< Compile info log.
	bool					compileOk;		//!< Did compilation succeed?
	deUint64				compileTimeUs;	//!< Compile time in microseconds (us).

	ShaderInfo (void) : compileOk(false), compileTimeUs(0) {}
};

/*--------------------------------------------------------------------*//*!
 * \brief Program information (link status, log).
 *//*--------------------------------------------------------------------*/
struct ProgramInfo
{
	std::string				infoLog;		//!< Link info log.
	bool					linkOk;			//!< Did link succeed?
	deUint64				linkTimeUs;		//!< Link time in microseconds (us).

	ProgramInfo (void) : linkOk(false), linkTimeUs(0) {}
};

/*--------------------------------------------------------------------*//*!
 * \brief Combined shader compilation and program linking info.
 *//*--------------------------------------------------------------------*/
struct ShaderProgramInfo
{
	glu::ProgramInfo				program;
	std::vector<glu::ShaderInfo>	shaders;
};

/*--------------------------------------------------------------------*//*!
 * \brief Shader object.
 *//*--------------------------------------------------------------------*/
class Shader
{
public:
							Shader				(const glw::Functions& gl, ShaderType shaderType);
							Shader				(const RenderContext& renderCtx, ShaderType shaderType);
							~Shader				(void);

	void					setSources			(int numSourceStrings, const char* const* sourceStrings, const int* lengths);
	void					compile				(void);

	deUint32				getShader			(void) const { return m_shader;				}
	const ShaderInfo&		getInfo				(void) const { return m_info;				}

	glu::ShaderType			getType				(void) const { return getInfo().type;		}
	bool					getCompileStatus	(void) const { return getInfo().compileOk;	}
	const std::string&		getSource			(void) const { return getInfo().source;		}
	const std::string&		getInfoLog			(void) const { return getInfo().infoLog;	}

	deUint32				operator*			(void) const { return getShader();			}

private:
							Shader				(const Shader& other);
	Shader&					operator=			(const Shader& other);

	const glw::Functions&	m_gl;
	deUint32				m_shader;	//!< Shader handle.
	ShaderInfo				m_info;		//!< Client-side clone of state for debug / perf reasons.
};

/*--------------------------------------------------------------------*//*!
 * \brief Program object.
 *//*--------------------------------------------------------------------*/
class Program
{
public:
							Program						(const glw::Functions& gl);
							Program						(const RenderContext& renderCtx);
							Program						(const RenderContext& renderCtx, deUint32 program);
							~Program					(void);

	void					attachShader				(deUint32 shader);
	void					detachShader				(deUint32 shader);

	void					bindAttribLocation			(deUint32 location, const char* name);
	void					transformFeedbackVaryings	(int count, const char* const* varyings, deUint32 bufferMode);

	void					link						(void);

	deUint32				getProgram					(void) const { return m_program;			}
	const ProgramInfo&		getInfo						(void) const { return m_info;				}

	bool					getLinkStatus				(void) const { return getInfo().linkOk;		}
	const std::string&		getInfoLog					(void) const { return getInfo().infoLog;	}

	bool					isSeparable					(void) const;
	void					setSeparable				(bool separable);

	int						getUniformLocation			(const std::string& name);

	deUint32				operator*					(void) const { return getProgram();			}

private:
							Program						(const Program& other);
	Program&				operator=					(const Program& other);

	const glw::Functions&	m_gl;
	deUint32				m_program;
	ProgramInfo				m_info;
};


/*--------------------------------------------------------------------*//*!
 * \brief Program pipeline object.
 *//*--------------------------------------------------------------------*/
class ProgramPipeline
{
public:
							ProgramPipeline				(const RenderContext& renderCtx);
							ProgramPipeline				(const glw::Functions& gl);
							~ProgramPipeline			(void);

	deUint32				getPipeline					(void) const { return m_pipeline; }
	void					useProgramStages			(deUint32 stages, deUint32 program);
	void					activeShaderProgram			(deUint32 program);
	bool					isValid						(void);

private:
							ProgramPipeline				(const ProgramPipeline& other);
	ProgramPipeline&		operator=					(const ProgramPipeline& other);

	const glw::Functions&	m_gl;
	deUint32				m_pipeline;
};

struct ProgramSources;

/*--------------------------------------------------------------------*//*!
 * \brief Shader program manager.
 *
 * ShaderProgram manages both Shader and Program objects, and provides
 * convenient API for constructing such programs.
 *//*--------------------------------------------------------------------*/
class ShaderProgram
{
public:
							ShaderProgram				(const glw::Functions& gl, const ProgramSources& sources);
							ShaderProgram				(const RenderContext& renderCtx, const ProgramSources& sources);
							~ShaderProgram				(void);

	bool					isOk						(void) const											{ return m_program.getLinkStatus();						}
	deUint32				getProgram					(void) const											{ return m_program.getProgram();						}

	bool					hasShader					(glu::ShaderType shaderType) const						{ return !m_shaders[shaderType].empty();				}
	int						getNumShaders				(glu::ShaderType shaderType) const						{ return (int)m_shaders[shaderType].size();				}
	const ShaderInfo&		getShaderInfo				(glu::ShaderType shaderType, int shaderNdx = 0) const	{ return m_shaders[shaderType][shaderNdx]->getInfo();	}
	const ProgramInfo&		getProgramInfo				(void) const											{ return m_program.getInfo();							}

private:
							ShaderProgram				(const ShaderProgram& other);
	ShaderProgram&			operator=					(const ShaderProgram& other);
	void					init						(const glw::Functions& gl, const ProgramSources& sources);

	std::vector<Shader*>	m_shaders[SHADERTYPE_LAST];
	Program					m_program;
};

// Utilities.

deUint32		getGLShaderType		(ShaderType shaderType);
deUint32		getGLShaderTypeBit	(ShaderType shaderType);
qpShaderType	getLogShaderType	(ShaderType shaderType);

tcu::TestLog&	operator<<			(tcu::TestLog& log, const ShaderInfo& shaderInfo);
tcu::TestLog&	operator<<			(tcu::TestLog& log, const ShaderProgramInfo& shaderProgramInfo);
tcu::TestLog&	operator<<			(tcu::TestLog& log, const ProgramSources& sources);
tcu::TestLog&	operator<<			(tcu::TestLog& log, const Shader& shader);
tcu::TestLog&	operator<<			(tcu::TestLog& log, const ShaderProgram& program);

// ProgramSources utilities and implementation.

struct AttribLocationBinding
{
	std::string			name;
	deUint32			location;

	AttribLocationBinding (void) : location(0) {}
	AttribLocationBinding (const std::string& name_, deUint32 location_) : name(name_), location(location_) {}
};

struct TransformFeedbackMode
{
	deUint32			mode;

	TransformFeedbackMode (void) : mode(0) {}
	TransformFeedbackMode (deUint32 mode_) : mode(mode_) {}
};

struct TransformFeedbackVarying
{
	std::string			name;

	explicit TransformFeedbackVarying (const std::string& name_) : name(name_) {}
};

struct ProgramSeparable
{
	bool				separable;
	explicit ProgramSeparable (bool separable_) : separable(separable_) {}
};

template<typename Iterator>
struct TransformFeedbackVaryings
{
	Iterator			begin;
	Iterator			end;

	TransformFeedbackVaryings (Iterator begin_, Iterator end_) : begin(begin_), end(end_) {}
};

struct ShaderSource
{
	ShaderType			shaderType;
	std::string			source;

	ShaderSource (void) : shaderType(SHADERTYPE_LAST) {}
	ShaderSource (glu::ShaderType shaderType_, const std::string& source_) : shaderType(shaderType_), source(source_) { DE_ASSERT(!source_.empty()); }
};

struct VertexSource : public ShaderSource
{
	VertexSource (const std::string& source_) : ShaderSource(glu::SHADERTYPE_VERTEX, source_) {}
};

struct FragmentSource : public ShaderSource
{
	FragmentSource (const std::string& source_) : ShaderSource(glu::SHADERTYPE_FRAGMENT, source_) {}
};

struct GeometrySource : public ShaderSource
{
	GeometrySource (const std::string& source_) : ShaderSource(glu::SHADERTYPE_GEOMETRY, source_) {}
};

struct ComputeSource : public ShaderSource
{
	ComputeSource (const std::string& source_) : ShaderSource(glu::SHADERTYPE_COMPUTE, source_) {}
};

struct TessellationControlSource : public ShaderSource
{
	TessellationControlSource (const std::string& source_) : ShaderSource(glu::SHADERTYPE_TESSELLATION_CONTROL, source_) {}
};

struct TessellationEvaluationSource : public ShaderSource
{
	TessellationEvaluationSource (const std::string& source_) : ShaderSource(glu::SHADERTYPE_TESSELLATION_EVALUATION, source_) {}
};

struct ProgramSources
{
	std::vector<std::string>			sources[SHADERTYPE_LAST];
	std::vector<AttribLocationBinding>	attribLocationBindings;

	deUint32							transformFeedbackBufferMode;		//!< TF buffer mode, or GL_NONE.
	std::vector<std::string>			transformFeedbackVaryings;
	bool								separable;

	ProgramSources (void) : transformFeedbackBufferMode(0), separable(false) {}

	ProgramSources&						operator<<			(const AttribLocationBinding& binding)		{ attribLocationBindings.push_back(binding);						return *this;	}
	ProgramSources&						operator<<			(const TransformFeedbackMode& mode)			{ transformFeedbackBufferMode = mode.mode;							return *this;	}
	ProgramSources&						operator<<			(const TransformFeedbackVarying& varying)	{ transformFeedbackVaryings.push_back(varying.name);				return *this;	}
	ProgramSources&						operator<<			(const ShaderSource& shaderSource)			{ sources[shaderSource.shaderType].push_back(shaderSource.source);	return *this;	}
	ProgramSources&						operator<<			(const ProgramSeparable& progSeparable)		{ separable = progSeparable.separable;								return *this;	}

	template<typename Iterator>
	ProgramSources&						operator<<			(const TransformFeedbackVaryings<Iterator>& varyings);
};

template<typename Iterator>
inline ProgramSources& ProgramSources::operator<< (const TransformFeedbackVaryings<Iterator>& varyings)
{
	for (Iterator cur = varyings.begin; cur != varyings.end; ++cur)
		transformFeedbackVaryings.push_back(*cur);
	return *this;
}

//! Helper for constructing vertex-fragment source pair.
inline ProgramSources makeVtxFragSources (const std::string& vertexSrc, const std::string& fragmentSrc)
{
	ProgramSources sources;
	sources.sources[SHADERTYPE_VERTEX].push_back(vertexSrc);
	sources.sources[SHADERTYPE_FRAGMENT].push_back(fragmentSrc);
	return sources;
}

} // glu

#endif // _GLUSHADERPROGRAM_HPP