/// 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.
#include "OpenglShader.h"

#include <boost\scoped_array.hpp>

#include "OpenglException.h"
#include "OpenglLastError.h"
#include "UnicodeString.h"

#include <fstream>

using namespace TestFrameWork;

OpenglShader::OpenglShader(const std::wstring& shaderFile, OpenglShaderStage stage)	:	m_id(CreateShaderFromFile(shaderFile, stage)),
																						m_stage(stage)
{
}

OpenglShader::~OpenglShader(void)
{
	  glDeleteShader(m_id);
}

GLuint OpenglShader::GetOpenglId(void) const
{
	return m_id;
}

OpenglShaderStage OpenglShader::GetStage(void) const
{
	return m_stage;
}

GLuint OpenglShader::CreateShaderFromFile(const std::wstring& shaderFile, OpenglShaderStage stage)
{
	std::string source = ReadSourceFile(shaderFile);

	return CreateShader(source, stage);
}

GLuint OpenglShader::CreateShader(const std::string& source, OpenglShaderStage stage)
{ 
	assert(stage == FragmentStage || stage == VertexStage);

	GLenum shaderStage = GL_VERTEX_SHADER;
	if (stage == FragmentStage)
	{
		shaderStage = GL_FRAGMENT_SHADER;
	}

	GLuint shaderId = glCreateShader(shaderStage);
	TestFrameWork::OnGLErrorThrowException();
	if (!shaderId)
	{
		throw Exception();
	}

	int size = (int)source.size();
	const char* sourceChar = source.c_str();

	glShaderSource(shaderId, 1, (const GLchar**)&sourceChar, (GLint*)&size);
	TestFrameWork::OnGLErrorThrowException();

	glCompileShader(shaderId);
	TestFrameWork::OnGLErrorThrowException();

	// Check if the compilation was ok
	int compileStatus;
	glGetShaderiv(shaderId, GL_COMPILE_STATUS, &compileStatus);
	TestFrameWork::OnGLErrorThrowException();

	if (!compileStatus)
	{
		int length = 0;
		glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &length);
		TestFrameWork::OnGLErrorThrowException();

		boost::scoped_array<char> infoLog(new char[length]);

		glGetShaderInfoLog(shaderId, length, &length, infoLog.get());
		TestFrameWork::OnGLErrorThrowException();

		std::wstring exceptionError(ConvertUTF8ToUTF16(infoLog.get()));
		exceptionError = L"Compiling shader error: " + exceptionError;

		throw Exception(exceptionError);
	}

	return shaderId;
}

std::string OpenglShader::ReadSourceFile(const std::wstring& shaderFile)
{
	/// Load the file
	std::ifstream fileStream(shaderFile.c_str());
	if (!fileStream)
	{
		throw Exception(L"Shader file couldn't be loaded.");
	}

	std::string source;
	std::string line;
	while (std::getline(fileStream, line))
	{
		source += "\n" + line;
	}

	return source;
}