//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#include "Shader.hpp"

#include "Forall.hpp"
#include "Information.hpp"

#include <algorithm>
#include <iostream>
#include <QFile>
#include <QSet>
#include <QString>
#include <QTextStream>

// This has to be defined somewhere in libmgx;
// Shader.cpp mostly makes sense
mgx::MGXOpenGLFunctions *mgx::glfuncs = NULL;

namespace mgx {
  using Information::err;
  
  Shader::Shader(int _verbosity) : verbosity(_verbosity), _program(0), _initialized(false) {}
  
  bool Shader::init()
  {
    has_shaders = true;
    return true;
  }
  
  void Shader::addVertexShader(const QString& filename)
  {
    std::vector<code_t>::iterator it_found
      = std::find(vertex_shaders_code.begin(), vertex_shaders_code.end(), std::make_pair(filename, false));
    if(it_found == vertex_shaders_code.end()) {
      vertex_shaders_code.push_back(std::make_pair(filename, false));
      invalidate();
    }
  }
  
  void Shader::removeVertexShader(const QString& filename)
  {
    std::vector<code_t>::iterator it_found
      = std::find(vertex_shaders_code.begin(), vertex_shaders_code.end(), std::make_pair(filename, false));
    if(it_found != vertex_shaders_code.end()) {
      vertex_shaders_code.erase(it_found);
      invalidate();
    }
  }
  
  void Shader::addFragmentShader(const QString& filename)
  {
    std::vector<code_t>::iterator it_found
      = std::find(fragment_shaders_code.begin(), fragment_shaders_code.end(), std::make_pair(filename, false));
    if(it_found == fragment_shaders_code.end()) {
      fragment_shaders_code.push_back(std::make_pair(filename, false));
      invalidate();
    }
  }
  
  bool Shader::changeFragmentShader(int pos, const QString& filename)
  {
    if(pos < 0) pos += fragment_shaders_code.size();
    if(pos < 0) return false;
    if(fragment_shaders_code.size() > (size_t)pos) {
      code_t c = std::make_pair(filename, false);
      if(c != fragment_shaders_code[pos]) {
        invalidate();
        fragment_shaders_code[pos] = std::make_pair(filename, false);
      }
      return true;
    }
    return false;
  }
  
  bool Shader::changeVertexShader(int pos, const QString& filename)
  {
    if(pos < 0) pos += vertex_shaders_code.size();
    if(pos < 0) return false;
    if(vertex_shaders_code.size() > (size_t)pos) {
      code_t c = std::make_pair(filename, false);
      if(c != vertex_shaders_code[pos]) {
        invalidate();
        vertex_shaders_code[pos] = std::make_pair(filename, false);
      }
      return true;
    }
    return false;
  }
  
  bool Shader::changeFragmentShaderCode(int pos, const QString& filename)
  {
    if(pos < 0) pos += fragment_shaders_code.size();
    if(pos < 0) return false;
    if(fragment_shaders_code.size() > (size_t)pos) {
      code_t c = std::make_pair(filename, true);
      if(c != fragment_shaders_code[pos]) {
        invalidate();
        fragment_shaders_code[pos] = std::make_pair(filename, true);
      }
      return true;
    }
    return false;
  }
  
  bool Shader::changeVertexShaderCode(int pos, const QString& filename)
  {
    if(pos < 0) pos += vertex_shaders_code.size();
    if(pos < 0) return false;
    if(vertex_shaders_code.size() > (size_t)pos) {
      code_t c = std::make_pair(filename, true);
      if(c != vertex_shaders_code[pos]) {
        invalidate();
        vertex_shaders_code[pos] = std::make_pair(filename, true);
      }
      return true;
    }
    return false;
  }
  
  void Shader::removeFragmentShader(const QString& filename)
  {
    std::vector<code_t>::iterator it_found
      = std::find(fragment_shaders_code.begin(), fragment_shaders_code.end(), std::make_pair(filename, false));
    if(it_found != fragment_shaders_code.end()) {
      fragment_shaders_code.erase(it_found);
      invalidate();
    }
  }
  
  void Shader::addVertexShaderCode(const QString& code)
  {
    std::vector<code_t>::iterator it_found
      = std::find(vertex_shaders_code.begin(), vertex_shaders_code.end(), std::make_pair(code, true));
    if(it_found == vertex_shaders_code.end()) {
      vertex_shaders_code.push_back(std::make_pair(code, true));
      invalidate();
    }
  }
  
  void Shader::removeVertexShaderCode(const QString& code)
  {
    std::vector<code_t>::iterator it_found
      = std::find(vertex_shaders_code.begin(), vertex_shaders_code.end(), std::make_pair(code, true));
    if(it_found != vertex_shaders_code.end()) {
      vertex_shaders_code.erase(it_found);
      invalidate();
    }
  }
  
  void Shader::addFragmentShaderCode(const QString& code)
  {
    std::vector<code_t>::iterator it_found
      = std::find(fragment_shaders_code.begin(), fragment_shaders_code.end(), std::make_pair(code, true));
    if(it_found == fragment_shaders_code.end()) {
      fragment_shaders_code.push_back(std::make_pair(code, true));
      invalidate();
    }
  }
  
  void Shader::removeFragmentShaderCode(const QString& code)
  {
    std::vector<code_t>::iterator it_found
      = std::find(fragment_shaders_code.begin(), fragment_shaders_code.end(), std::make_pair(code, true));
    if(it_found != fragment_shaders_code.end()) {
      fragment_shaders_code.erase(it_found);
      invalidate();
    }
  }
  
  void Shader::cleanShaders()
  {
    if(!hasShaders())
      return;
    forall(const GLuint& id, vertex_shaders) {
      if(_program)
        glfuncs->glDetachShader(_program, id);
      glfuncs->glDeleteShader(id);
    }
    forall(const GLuint& id, fragment_shaders) {
      if(_program)
        glfuncs->glDetachShader(_program, id);
      glfuncs->glDeleteShader(id);
    }
    vertex_shaders.clear();
    fragment_shaders.clear();
    if(_program)
      glfuncs->glDeleteProgram(_program);
    _program = 0;
  }
  
  bool Shader::setupShaders()
  {
    if(!hasShaders())
      return false;
    cleanShaders();
    // First, compile the shaders
    // For that, concatenate all the files, and add the version 120 to the beginning
    {
      QString content("#version 120\n\n");
      forall(const code_t& code, vertex_shaders_code) {
        if(code.second) {
          content += code.first;
          content += "\n";
        } else {
          const QString& filename = code.first;
          QFile f(filename);
          if(f.open(QIODevice::ReadOnly)) {
            QTextStream ts(&f);
            content += ts.readAll();
            content += "\n";
          } else {
            err << "Error, cannot open shader file " << filename << endl;
            cleanShaders();
            return false;
          }
        }
      }
      GLuint s = compileShader(GL_VERTEX_SHADER, content);
      if(verbosity > 3)
        err << "Compiling vertex shader" << endl;
      if(s == 0) {
        cleanShaders();
        return false;
      }
      vertex_shaders.push_back(s);
    }
  
    {
      QString content("#version 120\n\n");
      forall(const code_t& code, fragment_shaders_code) {
        if(code.second) {
          content += code.first;
          content += "\n";
        } else {
          const QString& filename = code.first;
          QFile f(filename);
          if(f.open(QIODevice::ReadOnly)) {
            QTextStream ts(&f);
            content += ts.readAll();
            content += "\n";
          } else {
            err << "Error, cannot open shader file " << filename << endl;
            cleanShaders();
            return false;
          }
        }
      }
      GLuint s = compileShader(GL_FRAGMENT_SHADER, content);
      if(verbosity > 3)
        err << "Compiling fragment shader" << endl;
      if(s == 0) {
        cleanShaders();
        return false;
      }
      fragment_shaders.push_back(s);
    }
  
    if(fragment_shaders.empty() and vertex_shaders.empty()) {
      err << "Warning, no shader to compile" << endl;
      return false;
    }
    // Create the program
    _program = glfuncs->glCreateProgram();
    REPORT_GL_ERROR("_program = glCreateProgramObject()");
    // Add shader
    forall(const GLuint& id, vertex_shaders) {
      glfuncs->glAttachShader(_program, id);
      REPORT_GL_ERROR("glAttachObject(_program, id)");
    }
    forall(const GLuint& id, fragment_shaders) {
      glfuncs->glAttachShader(_program, id);
      REPORT_GL_ERROR("glAttachObject(_program, id)");
    }
    // Link shader
    glfuncs->glLinkProgram(_program);
    REPORT_GL_ERROR("glLinkProgram(_program)");
  
    int status;
    glfuncs->glGetProgramiv(_program, GL_LINK_STATUS, &status);
    if(!status) {
      err << "Error while linking program:" << endl;
      printProgramInfoLog(_program);
      return false;
    }
    _initialized = true;
    return true;
  }
  
  QString Shader::shaderTypeName(GLenum shader_type)
  {
    switch(shader_type) {
    case GL_VERTEX_SHADER:
      return "vertex";
    case GL_FRAGMENT_SHADER:
      return "fragment";
    default:
      return "unknown";
    }
  }
  
  GLuint Shader::compileShaderFile(GLenum shader_type, QString filename)
  {
    if(!hasShaders())
      return 0;
    QFile f(filename);
    if(!f.open(QIODevice::ReadOnly)) {
      err << "Cannot open file " << f.fileName() << endl;
      return 0;
    }
    QTextStream ts(&f);
    QString content = ts.readAll();
    return compileShader(shader_type, content);
  }
  
  void Shader::printProgramInfoLog(GLuint object)
  {
    if(!hasShaders())
      return;
  
    GLsizei log_length;
    glfuncs->glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
    if(log_length > 0) {
      std::vector<char> cs(log_length + 1);
      GLsizei l;
      glfuncs->glGetProgramInfoLog(object, log_length, &l, &cs[0]);
      err << "*** START LOGS ***\n";
      err << &cs[0] << "\n*** END LOGS ***" << endl;
    } else
      err << "*** EMPTY LOG ***" << endl;
  }
  
  void Shader::printShaderInfoLog(GLuint object)
  {
    if(!hasShaders())
      return;
  
    GLsizei log_length;
    glfuncs->glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
    if(log_length > 0) {
      std::vector<char> cs(log_length + 1);
      GLsizei l;
      glfuncs->glGetShaderInfoLog(object, log_length, &l, &cs[0]);
      err << "*** START LOGS ***\n";
      err << &cs[0] << "\n*** END LOGS ***" << endl;
    } else
      err << "*** EMPTY LOG ***" << endl;
  }
  
  GLuint Shader::compileShader(GLenum shader_type, QString content)
  {
    if(!hasShaders())
      return 0;
    int shader = glfuncs->glCreateShader(shader_type);
    QByteArray ba = content.toLocal8Bit();
    const char* src = ba.constData();
    // int size = content.size();
    glfuncs->glShaderSource(shader, 1, (const GLchar**)(&src), NULL);
    glfuncs->glCompileShader(shader);
    int status;
    glfuncs->glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if(!status) {
      err << "Error while compiling this " << shaderTypeName(shader_type) << " shader: " << endl
          << "***** Beginning of shader *****" << endl << src << endl << "***** End of shader *****" << endl;
      printShaderInfoLog(shader);
      return 0;
    }
    return shader;
  }
  
  bool Shader::useShaders()
  {
    if(hasShaders() and _program) {
      // err << "glUseProgram(" << _program << ")" << endl;
      glfuncs->glUseProgram(_program);
      return true;
    }
    return false;
  }
  
  bool Shader::stopUsingShaders()
  {
    // err << "glUseProgram(0) (0x" << QString::number((uintptr_t)glUseProgram, 16) << endl;
    MGXOpenGLFunctions *funcs =
      QOpenGLContext::currentContext()->versionFunctions<MGXOpenGLFunctions>();
    funcs->glUseProgram(0);
    return true;
  }
  
  QTextStream& GLSLValue::write(QTextStream& s) const
  {
    if(!value) {
      s << "invalid value";
      return s;
    }
    switch(type) {
    case UNIFORM_INT:
      s << "int ";
      break;
    case UNIFORM_INT2:
      s << "ivec2 ";
      break;
    case UNIFORM_INT3:
      s << "ivec3 ";
      break;
    case UNIFORM_INT4:
      s << "ivec4 ";
      break;
    case UNIFORM_FLOAT:
      s << "float ";
      break;
    case UNIFORM_FLOAT2:
      s << "vec2 ";
      break;
    case UNIFORM_FLOAT3:
      s << "vec3 ";
      break;
    case UNIFORM_FLOAT4:
      s << "vec4 ";
      break;
    case UNIFORM_MATRIX2:
      s << "mat2 ";
      break;
    case UNIFORM_MATRIX3:
      s << "mat3 ";
      break;
    case UNIFORM_MATRIX4:
      s << "mat4 ";
      break;
    default:
      s << "Unkown type: " << type;
    }
    return value->write(s);
  }
  
  std::ostream& GLSLValue::write(std::ostream& s) const
  {
    if(!value) {
      s << "invalid value";
      return s;
    }
    switch(type) {
    case UNIFORM_INT:
      s << "int ";
      break;
    case UNIFORM_INT2:
      s << "ivec2 ";
      break;
    case UNIFORM_INT3:
      s << "ivec3 ";
      break;
    case UNIFORM_INT4:
      s << "ivec4 ";
      break;
    case UNIFORM_FLOAT:
      s << "float ";
      break;
    case UNIFORM_FLOAT2:
      s << "vec2 ";
      break;
    case UNIFORM_FLOAT3:
      s << "vec3 ";
      break;
    case UNIFORM_FLOAT4:
      s << "vec4 ";
      break;
    case UNIFORM_MATRIX2:
      s << "mat2 ";
      break;
    case UNIFORM_MATRIX3:
      s << "mat3 ";
      break;
    case UNIFORM_MATRIX4:
      s << "mat4 ";
      break;
    default:
      s << "Unkown type: " << type;
    }
    return value->write(s);
  }
  
  void glUniformTransposedMatrix2fv_(GLint location, GLsizei count, const GLfloat* v)
  {
    glfuncs->glUniformMatrix2fv(location, count, 1, v);
  }
  
  void glUniformTransposedMatrix3fv_(GLint location, GLsizei count, const GLfloat* v)
  {
    glfuncs->glUniformMatrix3fv(location, count, 1, v);
  }
  
  void glUniformTransposedMatrix4fv_(GLint location, GLsizei count, const GLfloat* v)
  {
    glfuncs->glUniformMatrix4fv(location, count, 1, v);
  }
  
  QTextStream& GLSLValue::read(QTextStream& s)
  {
    QString str;
    s >> str;
    delete value;
    if(str == "int") {
      type = UNIFORM_INT;
      setValue(ivec1());
    } else if(str == "ivec2") {
      type = UNIFORM_INT2;
      setValue(ivec2());
    } else if(str == "ivec3") {
      type = UNIFORM_INT3;
      setValue(ivec3());
    } else if(str == "ivec4") {
      type = UNIFORM_INT4;
      setValue(ivec4());
    } else if(str == "float") {
      type = UNIFORM_FLOAT;
      setValue(vec1());
    } else if(str == "vec2") {
      type = UNIFORM_FLOAT2;
      setValue(vec2());
    } else if(str == "vec3") {
      type = UNIFORM_FLOAT3;
      setValue(vec3());
    } else if(str == "vec4") {
      type = UNIFORM_FLOAT4;
      setValue(vec4());
    } else if(str == "mat2") {
      type = UNIFORM_MATRIX2;
      setValue(mat2());
    } else if(str == "mat3") {
      type = UNIFORM_MATRIX3;
      setValue(mat3());
    } else if(str == "mat4") {
      type = UNIFORM_MATRIX4;
      setValue(mat4());
    } else {
      value = 0;
      err << "Error, uniform type '" << str << "' not recognised" << endl;
      return s;
    }
    return value->read(s);
  }
  
  std::istream& GLSLValue::read(std::istream& s)
  {
    std::string str;
    s >> str;
    delete value;
    if(str == "int") {
      type = UNIFORM_INT;
      setValue(ivec1());
    } else if(str == "ivec2") {
      type = UNIFORM_INT2;
      setValue(ivec2());
    } else if(str == "ivec3") {
      type = UNIFORM_INT3;
      setValue(ivec3());
    } else if(str == "ivec4") {
      type = UNIFORM_INT4;
      setValue(ivec4());
    } else if(str == "float") {
      type = UNIFORM_FLOAT;
      setValue(vec1());
    } else if(str == "vec2") {
      type = UNIFORM_FLOAT2;
      setValue(vec2());
    } else if(str == "vec3") {
      type = UNIFORM_FLOAT3;
      setValue(vec3());
    } else if(str == "vec4") {
      type = UNIFORM_FLOAT4;
      setValue(vec4());
    } else if(str == "mat2") {
      type = UNIFORM_MATRIX2;
      setValue(mat2());
    } else if(str == "mat3") {
      type = UNIFORM_MATRIX3;
      setValue(mat3());
    } else if(str == "mat4") {
      type = UNIFORM_MATRIX4;
      setValue(mat4());
    } else {
      value = 0;
      err << "Error, uniform type '" << QString::fromStdString(str) << "' not recognised" << endl;
      return s;
    }
    return value->read(s);
  }

  #if true
//  #ifdef WIN32
  // Due to yet another stupid Microsoft crap ...
  
  static void glUniform1iv_(GLint i, GLsizei j, const GLint* k) {
    return glfuncs->glUniform1iv(i, j, k);
  }
  static void glUniform2iv_(GLint i, GLsizei j, const GLint* k) {
    return glfuncs->glUniform2iv(i, j, k);
  }
  static void glUniform3iv_(GLint i, GLsizei j, const GLint* k) {
    return glfuncs->glUniform3iv(i, j, k);
  }
  static void glUniform4iv_(GLint i, GLsizei j, const GLint* k) {
    return glfuncs->glUniform4iv(i, j, k);
  }
  static void glUniform1fv_(GLint i, GLsizei j, const GLfloat* k) {
    return glfuncs->glUniform1fv(i, j, k);
  }
  static void glUniform2fv_(GLint i, GLsizei j, const GLfloat* k) {
    return glfuncs->glUniform2fv(i, j, k);
  }
  static void glUniform3fv_(GLint i, GLsizei j, const GLfloat* k) {
    return glfuncs->glUniform3fv(i, j, k);
  }
  static void glUniform4fv_(GLint i, GLsizei j, const GLfloat* k) {
    return glfuncs->glUniform4fv(i, j, k);
  }
  static void glVertexAttrib1fv_(GLuint i, const GLfloat* j) {
    return glfuncs->glVertexAttrib1fv(i, j);
  }
  static void glVertexAttrib2fv_(GLuint i, const GLfloat* j) {
    return glfuncs->glVertexAttrib2fv(i, j);
  }
  static void glVertexAttrib3fv_(GLuint i, const GLfloat* j) {
    return glfuncs->glVertexAttrib3fv(i, j);
  }
  static void glVertexAttrib4fv_(GLuint i, const GLfloat* j) {
    return glfuncs->glVertexAttrib4fv(i, j);
  }
  #else // defined WIN32
  #  ifndef glUniform1iv_
  #    define glUniform1iv_ glUniform1iv
  #    define glUniform2iv_ glUniform2iv
  #    define glUniform3iv_ glUniform3iv
  #    define glUniform4iv_ glUniform4iv
  #    define glUniform1fv_ glUniform1fv
  #    define glUniform2fv_ glUniform2fv
  #    define glUniform3fv_ glUniform3fv
  #    define glUniform4fv_ glUniform4fv
  #    define glVertexAttrib1fv_ glVertexAttrib1fv
  #    define glVertexAttrib2fv_ glVertexAttrib2fv
  #    define glVertexAttrib3fv_ glVertexAttrib3fv
  #    define glVertexAttrib4fv_ glVertexAttrib4fv
  #  endif   // defined glUniform1iv_
  #endif // else defined WIN32
  
  void GLSLValue::setValue(const GLint* val, int count)
  {
    type = UNIFORM_INT;
    value = new ValueImpl<ivec1>(glUniform1iv_, 0, (const ivec1*)val, count);
  }
  
  void GLSLValue::setValue(const ivec1* val, int count)
  {
    type = UNIFORM_INT;
    value = new ValueImpl<ivec1>(glUniform1iv_, 0, val, count);
  }
  
  void GLSLValue::setValue(const ivec2* val, int count)
  {
    type = UNIFORM_INT2;
    value = new ValueImpl<ivec2>(glUniform2iv_, 0, val, count);
  }
  
  void GLSLValue::setValue(const ivec3* val, int count)
  {
    type = UNIFORM_INT3;
    value = new ValueImpl<ivec3>(glUniform3iv_, 0, val, count);
  }
  
  void GLSLValue::setValue(const ivec4* val, int count)
  {
    type = UNIFORM_INT4;
    value = new ValueImpl<ivec4>(glUniform4iv_, 0, val, count);
  }
  
  void GLSLValue::setValue(const GLfloat* val, int count)
  {
    type = UNIFORM_FLOAT;
    value = new ValueImpl<vec1>(glUniform1fv_, glVertexAttrib1fv_, (const vec1*)val, count);
  }
  
  void GLSLValue::setValue(const vec1* val, int count)
  {
    type = UNIFORM_FLOAT;
    value = new ValueImpl<vec1>(glUniform1fv_, glVertexAttrib1fv_, val, count);
  }
  
  void GLSLValue::setValue(const vec2* val, int count)
  {
    type = UNIFORM_FLOAT2;
    value = new ValueImpl<vec2>(glUniform2fv_, glVertexAttrib2fv_, val, count);
  }
  
  void GLSLValue::setValue(const vec3* val, int count)
  {
    type = UNIFORM_FLOAT3;
    value = new ValueImpl<vec3>(glUniform3fv_, glVertexAttrib3fv_, val, count);
  }
  
  void GLSLValue::setValue(const vec4* val, int count)
  {
    type = UNIFORM_FLOAT4;
    value = new ValueImpl<vec4>(glUniform4fv_, glVertexAttrib4fv_, val, count);
  }
  
  void GLSLValue::setValue(const mat2* val, int count)
  {
    type = UNIFORM_MATRIX2;
    value = new ValueImpl<mat2>(glUniformTransposedMatrix2fv_, 0, val, count);
  }
  
  void GLSLValue::setValue(const mat3* val, int count)
  {
    type = UNIFORM_MATRIX3;
    value = new ValueImpl<mat3>(glUniformTransposedMatrix3fv_, 0, val, count);
  }
  
  void GLSLValue::setValue(const mat4* val, int count)
  {
    type = UNIFORM_MATRIX4;
    value = new ValueImpl<mat4>(glUniformTransposedMatrix4fv_, 0, val, count);
  }
  
  bool Shader::setUniform(const QString& name, const GLSLValue& value)
  {
    bool found = false;
    for(size_t i = 0; i < model_uniform_names.size(); ++i) {
      if(model_uniform_names[i] == name) {
        model_uniform_values[i] = value;
        found = true;
      }
    }
    if(!found) {
      model_uniform_names.push_back(name);
      model_uniform_values.push_back(value);
    }
    return found;
  }
  
  void Shader::setUniform_instant(const QString& name, const GLSLValue& value) {
    loadUniform(_program, name, value);
  }
  
  void Shader::loadUniform(GLint program, const QString& name, const GLSLValue& value)
  {
    // err << "loadUniform(" << program << "," << name << "," << value << ")" << endl;
    // err << "glGetUniformLocation = 0x" << QString::number((uintptr_t)glGetUniformLocation, 16) << endl;
    QByteArray name_ba = name.toLocal8Bit();
    GLint loc = glfuncs->glGetUniformLocation(program, name_ba.constData());
    // err << "location: " << loc << endl;
    if(loc != -1) {
      // err << "Setting uniform to " << loc << endl;
      value.setUniform(loc);
    } else if(verbosity > 0) {
      err << "Error, uniform '" << name << "' was not found in the program" << endl;
    }
  }
  
  GLuint Shader::attribLocation(const QString& name)
  {
    QByteArray name_ba = name.toLocal8Bit();
    GLuint loc = glfuncs->glGetAttribLocation(_program, name_ba.constData());
    return loc;
  }
  
  void Shader::setAttrib(const QString& name, const GLSLValue& value)
  {
    GLuint loc = attribLocation(name);
    setAttrib(loc, value);
  }
  
  void Shader::setAttrib(GLuint loc, const GLSLValue& value) {
    value.setAttrib(loc);
  }
  
  void Shader::setupUniforms()
  {
    if(hasShaders() and _program) {
      QSet<QString> done_uniforms;
      for(size_t i = 0; i < model_uniform_names.size(); ++i) {
        const QString& name = model_uniform_names[i];
        if(!done_uniforms.contains(name)) {
          loadUniform(_program, name, model_uniform_values[i]);
          if(verbosity > 2)
            err << "Loading uniform '" << name << "' with value " << model_uniform_values[i] << endl;
          done_uniforms.insert(name);
        } else if(verbosity > 1) {
          err << "Warning: uniform " << name << " defined more than once" << endl;
        }
      }
      for(size_t i = 0; i < uniform_names.size(); ++i) {
        const QString& name = uniform_names[i];
        if(!done_uniforms.contains(name)) {
          loadUniform(_program, name, uniform_values[i]);
          if(verbosity > 2)
            err << "Loading uniform '" << name << "' with value " << uniform_values[i] << endl;
          done_uniforms.insert(name);
        } else if(verbosity > 1) {
          err << "Warning: uniform '" << name << "' overriden by model or defined more than once" << endl;
        }
      }
    } else if(verbosity > 2) {
      err << "Warning: trying to setup uniforms without having a running program" << endl;
    }
  }
}
