OZ++ Class: OpenGLShader
/******************************************************************************
 *
 * Copyright (c) 2016 Antillia.com TOSHIYUKI ARAI. 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. 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.
 *
 *
 *  OpenGLShader.h
 *
 *****************************************************************************/

//2016/07/30 Added a new constructor OpenGLShader(GLenum type, const char* filename)
//2016/08/01 Added some methods to get shader object information.

#pragma once

#ifdef GL_VERSION_2_0

#include <oz++/StringT.h>
#include <oz++/ModuleFileName.h>
#include <oz++/opengl/OpenGLObject.h>
#include <oz++/CharFileReader.h>

namespace OZ {

class OpenGLShader :public OpenGLObject {
private:
  GLuint shader;
  
public:
  OpenGLShader(GLenum type)
  :OpenGLObject(),
  shader(INVALID_VALUE)
  {
    shader = create(type);
    if (shader == 0) {
      throw IException("Failed to createShader: %d", (int)type); 
    }
  }
  
  //2016/07/30
  OpenGLShader(GLenum type, const char* filename)
  :OpenGLObject(),
  shader(INVALID_VALUE)
  {
    shader = create(type);
    if (shader == 0) {
      throw IException("Failed to createShader: %d", (int)type); 
    }
 
    char  fullpath[PATH_MAX];
 
    ModuleFileName moduleFileName;
    const char* path = moduleFileName.getPath();
      
    sprintf(fullpath,   "%s/%s", path, filename);
    shaderSource(fullpath);
    
    //Do compile automatically.
    compile();
  }

public:
  
  ~OpenGLShader()
  {
    destroy();
  }

  operator GLuint()
  {
    return shader;
  }

  GLuint create(GLenum type)
  {
    static PFNGLCREATESHADERPROC   glCreateShader = NULL;
    if (glCreateShader == NULL) {
      glCreateShader  = (PFNGLCREATESHADERPROC)load("glCreateShader"); 
    }
    return glCreateShader(type);
  }

  void  compile()
  {
    static PFNGLCOMPILESHADERPROC  glCompileShader = NULL;    
    if (glCompileShader == NULL) {
      glCompileShader   = (PFNGLCOMPILESHADERPROC)load("glCompileShader"); 
    }
    glCompileShader(shader);
    GLint compile_ok = GL_FALSE;
    getShader(GL_COMPILE_STATUS, &compile_ok);
    if (compile_ok == GL_FALSE) {
      const int length = getInfoLogLength()+1;
      GLchar* info = new GLchar[length];
      GLsizei len = 0;
      getInfoLog(length, &len, info);
      info[length-1] = '\0';
      printf("Error: %s\n", info);
      delete [] info;
      destroy();
      throw IException("Failed to compile a shader");
    }
  }
  
  void destroy()
  {
    static PFNGLDELETESHADERPROC  glDeleteShader = NULL;
    if (glDeleteShader == NULL) {
      glDeleteShader   = (PFNGLDELETESHADERPROC)load("glDeleteShader"); 
    }
    if (shader > 0) {
      glDeleteShader(shader);
    }
    shader = INVALID_VALUE;
  }
  
  void getInfoLog(GLsizei bufSize, GLsizei* length, GLchar* infoLog)
  {
    static PFNGLGETSHADERINFOLOGPROC  glGetShaderInfoLog = NULL; 
    if (glGetShaderInfoLog == NULL) {
      glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog"); 
    }
    glGetShaderInfoLog(shader, bufSize, length, infoLog);
  }
  
  void getShaderSource(GLsizei maxLength, GLsizei* length, GLchar* source)
  {
    static PFNGLGETSHADERSOURCEPROC   glGetShaderSource  = NULL;
    if (glGetShaderSource == NULL) {
      glGetShaderSource  = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource"); 
    }
    glGetShaderSource(shader, maxLength, length, source);
  }
  
  
  /**
  pname takes one of the following values:
   GL_SHADER_TYPE,
   GL_DELETE_STATUS
   GL_COMPILE_STATUS
   GL_INFO_LOG_LENGTH
   GL_SHADER_SOURCE_LENGTH
   */
  void getShader(GLenum pname, GLint* param)
  {
    static PFNGLGETSHADERIVPROC   glGetShaderiv = NULL;      
    if (glGetShaderiv == NULL) {
      glGetShaderiv   = (PFNGLGETSHADERIVPROC)load("glGetShaderiv"); 
    }
    glGetShaderiv(shader, pname, param);
  }
  
  //<added date="2016/08/01">
  int getShaderType()
  {
    GLint value = 0;
    getShader(GL_SHADER_TYPE, &value);
    return value;
  }
  
  int getDeleteStatus()
  {
    GLint value = 0;
    getShader(GL_DELETE_STATUS, &value);
    return value;
  }

  int getCompileStatus()
  {
    GLint value = 0;
    getShader(GL_COMPILE_STATUS, &value);
    return value;
  }

  int getInfoLogLength()
  {
    GLint value = 0;
    getShader(GL_INFO_LOG_LENGTH, &value);
    return value;
  }

  int getShaderSourceLength()
  {
    GLint value = 0;
    getShader(GL_SHADER_SOURCE_LENGTH, &value);
    return value;
  }
  //</added>
  
  void shaderSource(GLsizei count, const GLchar *const* string, const GLint* length)
  {
    static PFNGLSHADERSOURCEPROC   glShaderSource = NULL;     
    if (glShaderSource == NULL) {
      glShaderSource  = (PFNGLSHADERSOURCEPROC)load("glShaderSource"); 
    }
    glShaderSource(shader, count, string, length);
  }
  
  void shaderSource(const char* filename)
  {
    CharFileReader reader(filename);
    const char* string = reader.getData();
    shaderSource(1, (const GLchar *const*)&string, NULL);
  }

  GLboolean isShader()
  {
    static PFNGLISSHADERPROC  glIsShader = NULL;
    if (glIsShader == NULL) {
      glIsShader   = (PFNGLISSHADERPROC)load("glIsShader"); 
    }
    return glIsShader(shader);
  }
  
  void getSource(StringT<char>& shaderSource)
  {
    GLuint maxLength = getShaderSourceLength();
    GLchar* source = new GLchar[maxLength+1];
    memset(source, 0, maxLength + 1);
    GLsizei length = 0;
    getShaderSource(maxLength, &length, source);
    shaderSource.shallowCopy(source);
  }

  void displaySource()
  {
    GLuint maxLength = getShaderSourceLength();
    GLchar* source = new GLchar[maxLength+1];
    memset(source, 0, maxLength + 1);
    GLsizei length = 0;
    getShaderSource(maxLength, &length, source);
    
#ifdef WIN32 
    MessageBoxA(NULL, (const char*)source, "ShaderSource", MB_OK);
#else
    ::printf("ShaderSource: %s\n", (const char*)source);
#endif
    delete [] source;
  }

  void getLogInfo(StringT<char>& logInfo)
  {
    GLuint maxLength = getInfoLogLength();
    GLchar* info = new GLchar[maxLength+1];
    memset(info, 0, maxLength + 1);
    GLsizei length = 0;
    getInfoLog(maxLength, &length, info);
    logInfo.shallowCopy(info);
  }

  void displayLogInfo()
  {
    GLuint maxLength = getInfoLogLength();
    GLchar* info = new GLchar[maxLength+1];
    memset(info, 0, maxLength + 1);
    GLsizei length = 0;
    getInfoLog(maxLength, &length, info);
    
#ifdef WIN32 
    MessageBoxA(NULL, (const char*)info, "ShaderLogInfo", MB_OK);
#else
    ::printf("ShaderLogInfo: %s\n", (const char*)info);
#endif
    delete [] info;
  }


};

}

#endif