// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2010 Emweb bvba, Leuven, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WGLWIDGET_H_
#define WGLWIDGET_H_

#include <Wt/WInteractWidget>
#include <Wt/WGenericMatrix>

namespace Wt {

// define to enable WebGL debug code
//#define WT_WGLWIDGET_DEBUG

#ifdef WT_WGLWIDGET_DEBUG
#define GLDEBUG do {if (debugging_) {js_ << "\n{var err = ctx.getError(); if(err != ctx.NO_ERROR) {alert('error " << __FUNCTION__ << ": ' + err); debugger;}}\n";}} while(false)
//#define GLDEBUG do {js_ << "\n{var err = ctx.getError(); if(err != ctx.NO_ERROR) {debugger;}}\n";} while(false)
#else
#define GLDEBUG
#endif

class WImage;

// Incomplete implementation of the new webgl standard. This is to be
// considered as a technology preview, and we welcome comments regarding
// this interface.
// Expect this interface to change.
// TODO: provide a mechanism to write your own JavaScript

/*! \class WGLWidget Wt/WGLWidget Wt/WGLWidget
 *  \brief WebGL support class
 *
 * The WGLWidget class is an interface to the HTML5 WebGL infrastructure. Its
 * implementation started based on the working drafts of the Khronos group, and
 * will be updated as the standard stabilizes. To fully understand WebGL, it is
 * recommended to read the WebGL standard in addition to this documentation.
 *
 * The most recent version of the WebGL specification can be found here:
 * https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/doc/spec/WebGL-spec.html
 *
 * This widget is work in progress. The API may change in the future because
 * of new features, reorganizations or changes in the WebGL draft.
 *
 * The goal of the WGLWidget class is to provide a method to render 3D
 * structures in the browser, where rendering and rerendering is normally
 * done at the client side in JavaScript without interaction from the server,
 * in order to obtain a smooth user interaction. Unless the scene requires
 * server-side updates, there is no communication with the server.
 *
 * The rendering interface resembles to OpenGL ES, the same standard as
 * WebGL is based on. This is a stripped down version of the normal OpenGL
 * as we usually find them on desktops. Many stateful OpenGL features are
 * not present in OpenGL ES: no modelview and camera transformation stacks,
 * no default lighting models, no support for other rendering methods than
 * through VBOs, ... Therefore much existing example code for OpenGL
 * applications and shaders will not work on WebGL without modifications.
 * The 'learning webgl' web site at http://learningwebgl.com/ is a good
 * starting point to get familiar with WebGL.
 *
 * To use a %WGLWidget, you must derive from it and reimplement
 * the painter methods. Usually, you will always need to implement
 * initializeGL() and paintGL(). Optionally, you may choose to implement
 * resizeGL() (if your widget does not have a fixed size), and updateGL().
 * If you need to modify the painting methods, a repaint is triggered by
 * calling the repaintGL() method. The default behaviour for any of these
 * four painting functions is to do nothing.
 *
 * The four painter methods (initializeGL(), resizeGL(), paintGL() and
 * updateGL()) all record JavaScript which is sent to the
 * browser. The JavaScript code of paintGL() is cached client-side, and
 * may be executed many times, e.g. to repaint a scene from different
 * viewpoints. The JavaScript code of initializeGL(), resizeGL() and
 * updateGL() are intended for OpenGL state updates, and is therefore
 * only executed once on the client and is then discarded.
 *
 * There are four painting methods that you may implement in a
 * specialization of this class. The purpose of these functions is
 * to register what JavaScript code has to be executed to render a
 * scene. Through invocations of the WebGL functions documented below,
 * Wt records the JavaScript calls that have to be invoked in the
 * browser.
 * <ul>
 * <li><b>initializeGL()</b>: this function is executed only once, right
 *     after the successful detection of WebGL in the browser. (note: in
 *     future versions, it may be re-executed when the webglcontextlost
 *     signal is fired, but this is currently not yet implemented). This
 *     is the ideal location to compose shader programs, send VBO's to
 *     the client, extract uniform and attribute locations, ... Due to
 *     the presence of VBO's, this function may generate a large amount
 *     of data to the client.
 * <li><b>resizeGL()</b>: this function is executed whenever the canvas
 *     dimensions change. A change in canvas size will require you to
 *     invoke the viewport() function again, as well as recalculate the
 *     projection matrices (especially when the aspect ratio has changed).
 *     The resizeGL() function is therefore the ideal location to set those
 *     properties.
 *     The resizeGL() function is invoked automatically on every resize,
 *     and after the first initializeGL() invocation. Additional invocations
 *     may be triggered by calling repaint() with the RESIZE_GL flag.
 * <li><b>paintGL()</b>: this is the main scene drawing function. Through
 *     its execution, Wt records what has to be done to render a scene,
 *     and it is executed every time that the scene is to be redrawn. You
 *     can use the VBO's and shaders prepared in the initializeGL() phase.
 *     Usually, this function sets uniforms and attributes, links
 *     attributes to VBO's, applies textures, and draws primitives.
 *     You may also create local programs, buffers, ... Remeber that this
 *     function is executed a lot of times, so every buffer/program created
 *     in this function should also be destroyed to avoid memory leaks.
 *     This function is transmitted once to the client, and is executed
 *     when the scene needs to be redrawn. Redraws may be triggered from
 *     mouse events, timer triggers, events on e.g. a video element, or
 *     whatever other event.
 *     The paintGL() function can be updated through invoking repaintGL()
 *     with the PAINT_GL flag.
 * <li><b>updateGL()</b>: VBO's, programs, uniforms, GL properties,
 *     or anything else set during intializeGL() are not necessarily
 *     immutable. If you want to change, add, remove or reconfigure those
 *     properties, the execution of an updateGL() function can be triggered
 *     by invoking repaintGL() with the UPDATE_GL flag. This signals that
 *     updateGL() needs to be evaluated - just once. It is possible that
 *     the paintGL() function also requires updates as consequence of the
 *     changes in the updateGL() function; in this case, you should also set
 *     the PAINT_GL flag of repaintGL().
 * </ul>
 *
 * The WebGL functions are intended to be used exclusively from within the
 * invocation of the four callback functions mentioned above. In order to
 * manually trigger the execution of these function, use the repaintGL().
 *
 * A WGLWidget must be given a size explicitly, or must be put inside a
 * layout manager that manages its width and height. The behaviour of a
 * WGLWidget that was not given a size is undefined.
 *
 * <h3>Binary buffer transfers</h3>
 *
 * Using createAndLoadArrayBuffer(), you can load an array buffer in
 * binary format from an URL. This will cause the client to fetch the
 * given URL, and make the contents of the file available in an
 * ArrayBuffer, which can then be used by BufferData() to bind them
 * to an OpenGL buffer. This is ideal to load VBO buffers in a faster
 * way, as it avoids converting floats to text strings on the server
 * and then back to floats on the client. You can combine this with
 * the use of WResource (e.g. WMemoryResource) to send an std::vector of
 * vertices to the client.
 *
 * <h3>Client side matrices.</h3>
 *
 * The WGLWidget provides the WGLWidget::JavaScriptMatrix4x4 class as
 * a mechanism to use client-side modifiable matrices in the render
 * functions. These matrices can be used identically to the
 * 'constant', with the advantage that there is no need to have a
 * roundtrip to the server to redraw the scene when they are changed.
 * As such, they are ideal for mouse-based camera manipulations, timer
 * triggered animations, or object manipulations.
 *
 * Note: the client side matrices are currently not yet communicated back
 * to the server, but that is an implementation idea for the future.
 */
class WT_API WGLWidget: public WInteractWidget
{
public:
  typedef std::string Shader;
  typedef std::string Program;
  typedef std::string AttribLocation;
  typedef std::string Buffer;
  typedef std::string Framebuffer;
  typedef std::string Renderbuffer;
  typedef std::string UniformLocation;
  typedef std::string Texture;
  typedef std::string ArrayBuffer;

  /*! \brief A client-side JavaScript matrix
   *
   * A JavaScriptMatrix has methods that make it possible to do client-side
   * calculations on matrices.
   *
   * Important: only the jsRef() of the return value from a call to
   * WGLWidget::createJavaScriptMatrix() is a variable name that can be used 
   * in custom JavaScript to modify a matrix from external scripts.
   * The jsRef() of return values of operations refer to unnamed temporary
   * objects - rvalues in C++-lingo.
   */
  class WT_API JavaScriptMatrix4x4 {
  public:
    // Create an identity matrix
    JavaScriptMatrix4x4(const std::string &jsVariable);
    JavaScriptMatrix4x4();
    JavaScriptMatrix4x4(const JavaScriptMatrix4x4 &other);

    JavaScriptMatrix4x4 &operator=(const JavaScriptMatrix4x4 &rhs);

    std::string jsRef() const { return jsRef_; }

    JavaScriptMatrix4x4 inverted() const;
    JavaScriptMatrix4x4 transposed() const;

    JavaScriptMatrix4x4 operator*(const WGenericMatrix<double, 4, 4> &m) const;

  private:
    std::string jsRef_;
  };

  /*! \brief Construct a WebGL widget.
   *
   * Before the first rendering, you must apply a size to the WGLWidget.
   *
   * /code
   * /endcode
   */
  WGLWidget(WContainerWidget *parent);

  /*! \brief Destructor
   */
  ~WGLWidget();

  /*! \brief Initialize the WebGL state when the widget is first shown.
   *
   * initializeGL() is called once, when the widget is first rendered. It
   * usually creates most of the WebGL related state: shaders, VBOs,
   * uniform locations, ...
   *
   * If this state is to be updated during the lifetime of the widget,
   * you should specialize the updateGL() to accomodate for this.
   *
   * Note: in a future version, this method will probably also be invoked
   * on contextrestore events.
   */
  virtual void initializeGL();

  /*! \brief Act on resize events
   *
   * Usually, this method only contains functions to set the viewport
   * and the projection matrix (as this is aspect ration dependent).
   *
   * resizeGL() is rendered after initializeGL, and whenever widget is
   * resized. After this method finishes, the widget is repainted with
   * the cached client-side paint function.
   */
  virtual void resizeGL(int width, int height);

  /*! \brief Update the client-side painting function.
   *
   * This method is invoked client-side when a repaint is required,
   * i.e. when the repaintSlot() (a JavaScript-side JSlot) is triggered.
   * Typical examples are: after mouse-based camera movements, after
   * a timed update of a camera or an object's position, after
   * a resize event (resizeGL() will also be called then), after
   * an animation event, ... In many cases, this function will be
   * executed client-side many many times.
   *
   * Using the WebGL functions from this class, you construct a scene.
   * The implementation tracks all JavaScript calls that need to be
   * performed to draw the scenes, and will replay them verbatim on
   * every trigger of the repaintSlot(). There are a few mechanisms
   * that may be employed to change what is rendered without updating
   * the paintGL() cache:
   * <ul>
   * <li>Client-side matrices may be used to change camera viewpoints,
   *     manipilate separate object's model transformation matrices, ...
   *     </li>
   * <li>Shader sources can be updated without requiring the paint
   *     function to be renewed</li>
   * </ul>
   *
   * Updating the paintGL() cache is usually not too expensive; the VBOs,
   * which are large in many cases, are already at the client side, while
   * the paintGL() code only draws the VBOs. Of course, if you have to
   * draw many separate objects, the paintGL() JS code may become large
   * and updating is more expensive.
   *
   * In order to update the paintGL() cache, call repaintGL() with
   * the PAINT_GL parameter, which will cause the invocation of this
   * method.
   */
  virtual void paintGL();

  /*! \brief Update state set in initializeGL()
   *
   * Invoked when repaint is called with the UPDATE_GL call.
   *
   * This is intended to be executed when you want to change programs,
   * 'constant' uniforms, or even VBO's, ... without resending already
   * initialized data. It is a mechanism to make changes to what you've
   * set in intializeGL(). For every server-side invocation of this method,
   * the result will be rendered client-side exactly once.
   */
  virtual void updateGL();

  /*! \brief Specifies what WebGL function needs to be updated
   */
  enum ClientSideRenderer {PAINT_GL = 1,  //!< refresh paintGL()
                           RESIZE_GL = 2, //!< refresh resizeGL()
                           UPDATE_GL = 4  //!< refresh updateGL()
  };

  /*! \brief Request invocation of resizeGL, paintGL and/or updateGL.
   *
   * If invoked with PAINT_GL, the client-side cached paint function
   * is updated. If invoked with RESIZE_GL or UPDATE_GL, the code
   * will be executed once.
   *
   * If invoked with multiple flags set, the order of execution will be
   * updateGL(), resizeGL(), paintGL().
   */
  void repaintGL(WFlags<ClientSideRenderer> which);

  void resize(const WLength &width, const WLength &height);



  /*! \brief The enourmous GLenum
   *
   * This enum contains all numeric constants defined by the WebGL standard.
   */
  enum GLenum {
    /* ClearBufferMask */
    DEPTH_BUFFER_BIT               = 0x00000100,
    STENCIL_BUFFER_BIT             = 0x00000400,
    COLOR_BUFFER_BIT               = 0x00004000,
    
    /* BeginMode */
    POINTS                         = 0x0000,
    LINES                          = 0x0001,
    LINE_LOOP                      = 0x0002,
    LINE_STRIP                     = 0x0003,
    TRIANGLES                      = 0x0004,
    TRIANGLE_STRIP                 = 0x0005,
    TRIANGLE_FAN                   = 0x0006,
    
    /* AlphaFunction (not supported in ES20) */
    /*      NEVER */
    /*      LESS */
    /*      EQUAL */
    /*      LEQUAL */
    /*      GREATER */
    /*      NOTEQUAL */
    /*      GEQUAL */
    /*      ALWAYS */
    
    /* BlendingFactorDest */
    ZERO                           = 0,
    ONE                            = 1,
    SRC_COLOR                      = 0x0300,
    ONE_MINUS_SRC_COLOR            = 0x0301,
    SRC_ALPHA                      = 0x0302,
    ONE_MINUS_SRC_ALPHA            = 0x0303,
    DST_ALPHA                      = 0x0304,
    ONE_MINUS_DST_ALPHA            = 0x0305,
    
    /* BlendingFactorSrc */
    /*      ZERO */
    /*      ONE */
    DST_COLOR                      = 0x0306,
    ONE_MINUS_DST_COLOR            = 0x0307,
    SRC_ALPHA_SATURATE             = 0x0308,
    /*      SRC_ALPHA */
    /*      ONE_MINUS_SRC_ALPHA */
    /*      DST_ALPHA */
    /*      ONE_MINUS_DST_ALPHA */
    
    /* BlendEquationSeparate */
    FUNC_ADD                       = 0x8006,
    BLEND_EQUATION                 = 0x8009,
    BLEND_EQUATION_RGB             = 0x8009,   /* same as BLEND_EQUATION */
    BLEND_EQUATION_ALPHA           = 0x883D,
    
    /* BlendSubtract */
    FUNC_SUBTRACT                  = 0x800A,
    FUNC_REVERSE_SUBTRACT          = 0x800B,
    
    /* Separate Blend Functions */
    BLEND_DST_RGB                  = 0x80C8,
    BLEND_SRC_RGB                  = 0x80C9,
    BLEND_DST_ALPHA                = 0x80CA,
    BLEND_SRC_ALPHA                = 0x80CB,
    CONSTANT_COLOR                 = 0x8001,
    ONE_MINUS_CONSTANT_COLOR       = 0x8002,
    CONSTANT_ALPHA                 = 0x8003,
    ONE_MINUS_CONSTANT_ALPHA       = 0x8004,
    BLEND_COLOR                    = 0x8005,
    
    /* Buffer Objects */
    ARRAY_BUFFER                   = 0x8892,
    ELEMENT_ARRAY_BUFFER           = 0x8893,
    ARRAY_BUFFER_BINDING           = 0x8894,
    ELEMENT_ARRAY_BUFFER_BINDING   = 0x8895,
    
    STREAM_DRAW                    = 0x88E0,
    STATIC_DRAW                    = 0x88E4,
    DYNAMIC_DRAW                   = 0x88E8,
    
    BUFFER_SIZE                    = 0x8764,
    BUFFER_USAGE                   = 0x8765,
    
    CURRENT_VERTEX_ATTRIB          = 0x8626,
    
    /* CullFaceMode */
    FRONT                          = 0x0404,
    BACK                           = 0x0405,
    FRONT_AND_BACK                 = 0x0408,
    
    /* DepthFunction */
    /*      NEVER */
    /*      LESS */
    /*      EQUAL */
    /*      LEQUAL */
    /*      GREATER */
    /*      NOTEQUAL */
    /*      GEQUAL */
    /*      ALWAYS */
    
    /* EnableCap */
    /* TEXTURE_2D */
    CULL_FACE                      = 0x0B44,
    BLEND                          = 0x0BE2,
    DITHER                         = 0x0BD0,
    STENCIL_TEST                   = 0x0B90,
    DEPTH_TEST                     = 0x0B71,
    SCISSOR_TEST                   = 0x0C11,
    POLYGON_OFFSET_FILL            = 0x8037,
    SAMPLE_ALPHA_TO_COVERAGE       = 0x809E,
    SAMPLE_COVERAGE                = 0x80A0,
    
    /* ErrorCode */
    NO_ERROR                       = 0,
    INVALID_ENUM                   = 0x0500,
    INVALID_VALUE                  = 0x0501,
    INVALID_OPERATION              = 0x0502,
    OUT_OF_MEMORY                  = 0x0505,
    
    /* FrontFaceDirection */
    CW                             = 0x0900,
    CCW                            = 0x0901,
    
    /* GetPName */
    LINE_WIDTH                     = 0x0B21,
    ALIASED_POINT_SIZE_RANGE       = 0x846D,
    ALIASED_LINE_WIDTH_RANGE       = 0x846E,
    CULL_FACE_MODE                 = 0x0B45,
    FRONT_FACE                     = 0x0B46,
    DEPTH_RANGE                    = 0x0B70,
    DEPTH_WRITEMASK                = 0x0B72,
    DEPTH_CLEAR_VALUE              = 0x0B73,
    DEPTH_FUNC                     = 0x0B74,
    STENCIL_CLEAR_VALUE            = 0x0B91,
    STENCIL_FUNC                   = 0x0B92,
    STENCIL_FAIL                   = 0x0B94,
    STENCIL_PASS_DEPTH_FAIL        = 0x0B95,
    STENCIL_PASS_DEPTH_PASS        = 0x0B96,
    STENCIL_REF                    = 0x0B97,
    STENCIL_VALUE_MASK             = 0x0B93,
    STENCIL_WRITEMASK              = 0x0B98,
    STENCIL_BACK_FUNC              = 0x8800,
    STENCIL_BACK_FAIL              = 0x8801,
    STENCIL_BACK_PASS_DEPTH_FAIL   = 0x8802,
    STENCIL_BACK_PASS_DEPTH_PASS   = 0x8803,
    STENCIL_BACK_REF               = 0x8CA3,
    STENCIL_BACK_VALUE_MASK        = 0x8CA4,
    STENCIL_BACK_WRITEMASK         = 0x8CA5,
    VIEWPORT                       = 0x0BA2,
    SCISSOR_BOX                    = 0x0C10,
    /*      SCISSOR_TEST */
    COLOR_CLEAR_VALUE              = 0x0C22,
    COLOR_WRITEMASK                = 0x0C23,
    UNPACK_ALIGNMENT               = 0x0CF5,
    PACK_ALIGNMENT                 = 0x0D05,
    MAX_TEXTURE_SIZE               = 0x0D33,
    MAX_VIEWPORT_DIMS              = 0x0D3A,
    SUBPIXEL_BITS                  = 0x0D50,
    RED_BITS                       = 0x0D52,
    GREEN_BITS                     = 0x0D53,
    BLUE_BITS                      = 0x0D54,
    ALPHA_BITS                     = 0x0D55,
    DEPTH_BITS                     = 0x0D56,
    STENCIL_BITS                   = 0x0D57,
    POLYGON_OFFSET_UNITS           = 0x2A00,
    /*      POLYGON_OFFSET_FILL */
    POLYGON_OFFSET_FACTOR          = 0x8038,
    TEXTURE_BINDING_2D             = 0x8069,
    SAMPLE_BUFFERS                 = 0x80A8,
    SAMPLES                        = 0x80A9,
    SAMPLE_COVERAGE_VALUE          = 0x80AA,
    SAMPLE_COVERAGE_INVERT         = 0x80AB,
    
    /* GetTextureParameter */
    /*      TEXTURE_MAG_FILTER */
    /*      TEXTURE_MIN_FILTER */
    /*      TEXTURE_WRAP_S */
    /*      TEXTURE_WRAP_T */
    
    NUM_COMPRESSED_TEXTURE_FORMATS = 0x86A2,
    COMPRESSED_TEXTURE_FORMATS     = 0x86A3,
    
    /* HintMode */
    DONT_CARE                      = 0x1100,
    FASTEST                        = 0x1101,
    NICEST                         = 0x1102,
    
    /* HintTarget */
    GENERATE_MIPMAP_HINT            = 0x8192,
    
    /* DataType */
    BYTE                           = 0x1400,
    UNSIGNED_BYTE                  = 0x1401,
    SHORT                          = 0x1402,
    UNSIGNED_SHORT                 = 0x1403,
    INT                            = 0x1404,
    UNSIGNED_INT                   = 0x1405,
    FLOAT                          = 0x1406,
    
    /* PixelFormat */
    DEPTH_COMPONENT                = 0x1902,
    ALPHA                          = 0x1906,
    RGB                            = 0x1907,
    RGBA                           = 0x1908,
    LUMINANCE                      = 0x1909,
    LUMINANCE_ALPHA                = 0x190A,
    
    /* PixelType */
    /*      UNSIGNED_BYTE */
    UNSIGNED_SHORT_4_4_4_4         = 0x8033,
    UNSIGNED_SHORT_5_5_5_1         = 0x8034,
    UNSIGNED_SHORT_5_6_5           = 0x8363,
    
    /* Shaders */
    FRAGMENT_SHADER                  = 0x8B30,
    VERTEX_SHADER                    = 0x8B31,
    MAX_VERTEX_ATTRIBS               = 0x8869,
    MAX_VERTEX_UNIFORM_VECTORS       = 0x8DFB,
    MAX_VARYING_VECTORS              = 0x8DFC,
    MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D,
    MAX_VERTEX_TEXTURE_IMAGE_UNITS   = 0x8B4C,
    MAX_TEXTURE_IMAGE_UNITS          = 0x8872,
    MAX_FRAGMENT_UNIFORM_VECTORS     = 0x8DFD,
    SHADER_TYPE                      = 0x8B4F,
    DELETE_STATUS                    = 0x8B80,
    LINK_STATUS                      = 0x8B82,
    VALIDATE_STATUS                  = 0x8B83,
    ATTACHED_SHADERS                 = 0x8B85,
    ACTIVE_UNIFORMS                  = 0x8B86,
    ACTIVE_UNIFORM_MAX_LENGTH        = 0x8B87,
    ACTIVE_ATTRIBUTES                = 0x8B89,
    ACTIVE_ATTRIBUTE_MAX_LENGTH      = 0x8B8A,
    SHADING_LANGUAGE_VERSION         = 0x8B8C,
    CURRENT_PROGRAM                  = 0x8B8D,
    
    /* StencilFunction */
    NEVER                          = 0x0200,
    LESS                           = 0x0201,
    EQUAL                          = 0x0202,
    LEQUAL                         = 0x0203,
    GREATER                        = 0x0204,
    NOTEQUAL                       = 0x0205,
    GEQUAL                         = 0x0206,
    ALWAYS                         = 0x0207,
    
    /* StencilOp */
    /*      ZERO */
    KEEP                           = 0x1E00,
    REPLACE                        = 0x1E01,
    INCR                           = 0x1E02,
    DECR                           = 0x1E03,
    INVERT                         = 0x150A,
    INCR_WRAP                      = 0x8507,
    DECR_WRAP                      = 0x8508,
    
    /* StringName */
    VENDOR                         = 0x1F00,
    RENDERER                       = 0x1F01,
    VERSION                        = 0x1F02,
    
    /* TextureMagFilter */
    NEAREST                        = 0x2600,
    LINEAR                         = 0x2601,
    
    /* TextureMinFilter */
    /*      NEAREST */
    /*      LINEAR */
    NEAREST_MIPMAP_NEAREST         = 0x2700,
    LINEAR_MIPMAP_NEAREST          = 0x2701,
    NEAREST_MIPMAP_LINEAR          = 0x2702,
    LINEAR_MIPMAP_LINEAR           = 0x2703,
    
    /* TextureParameterName */
    TEXTURE_MAG_FILTER             = 0x2800,
    TEXTURE_MIN_FILTER             = 0x2801,
    TEXTURE_WRAP_S                 = 0x2802,
    TEXTURE_WRAP_T                 = 0x2803,
    
    /* TextureTarget */
    TEXTURE_2D                     = 0x0DE1,
    TEXTURE                        = 0x1702,
    
    TEXTURE_CUBE_MAP               = 0x8513,
    TEXTURE_BINDING_CUBE_MAP       = 0x8514,
    TEXTURE_CUBE_MAP_POSITIVE_X    = 0x8515,
    TEXTURE_CUBE_MAP_NEGATIVE_X    = 0x8516,
    TEXTURE_CUBE_MAP_POSITIVE_Y    = 0x8517,
    TEXTURE_CUBE_MAP_NEGATIVE_Y    = 0x8518,
    TEXTURE_CUBE_MAP_POSITIVE_Z    = 0x8519,
    TEXTURE_CUBE_MAP_NEGATIVE_Z    = 0x851A,
    MAX_CUBE_MAP_TEXTURE_SIZE      = 0x851C,
    
    /* TextureUnit */
    TEXTURE0                       = 0x84C0,
    TEXTURE1                       = 0x84C1,
    TEXTURE2                       = 0x84C2,
    TEXTURE3                       = 0x84C3,
    TEXTURE4                       = 0x84C4,
    TEXTURE5                       = 0x84C5,
    TEXTURE6                       = 0x84C6,
    TEXTURE7                       = 0x84C7,
    TEXTURE8                       = 0x84C8,
    TEXTURE9                       = 0x84C9,
    TEXTURE10                      = 0x84CA,
    TEXTURE11                      = 0x84CB,
    TEXTURE12                      = 0x84CC,
    TEXTURE13                      = 0x84CD,
    TEXTURE14                      = 0x84CE,
    TEXTURE15                      = 0x84CF,
    TEXTURE16                      = 0x84D0,
    TEXTURE17                      = 0x84D1,
    TEXTURE18                      = 0x84D2,
    TEXTURE19                      = 0x84D3,
    TEXTURE20                      = 0x84D4,
    TEXTURE21                      = 0x84D5,
    TEXTURE22                      = 0x84D6,
    TEXTURE23                      = 0x84D7,
    TEXTURE24                      = 0x84D8,
    TEXTURE25                      = 0x84D9,
    TEXTURE26                      = 0x84DA,
    TEXTURE27                      = 0x84DB,
    TEXTURE28                      = 0x84DC,
    TEXTURE29                      = 0x84DD,
    TEXTURE30                      = 0x84DE,
    TEXTURE31                      = 0x84DF,
    ACTIVE_TEXTURE                 = 0x84E0,
    
    /* TextureWrapMode */
    REPEAT                         = 0x2901,
    CLAMP_TO_EDGE                  = 0x812F,
    MIRRORED_REPEAT                = 0x8370,
    
    /* Uniform Types */
    FLOAT_VEC2                     = 0x8B50,
    FLOAT_VEC3                     = 0x8B51,
    FLOAT_VEC4                     = 0x8B52,
    INT_VEC2                       = 0x8B53,
    INT_VEC3                       = 0x8B54,
    INT_VEC4                       = 0x8B55,
    BOOL                           = 0x8B56,
    BOOL_VEC2                      = 0x8B57,
    BOOL_VEC3                      = 0x8B58,
    BOOL_VEC4                      = 0x8B59,
    FLOAT_MAT2                     = 0x8B5A,
    FLOAT_MAT3                     = 0x8B5B,
    FLOAT_MAT4                     = 0x8B5C,
    SAMPLER_2D                     = 0x8B5E,
    SAMPLER_CUBE                   = 0x8B60,
    
    /* Vertex Arrays */
    VERTEX_ATTRIB_ARRAY_ENABLED        = 0x8622,
    VERTEX_ATTRIB_ARRAY_SIZE           = 0x8623,
    VERTEX_ATTRIB_ARRAY_STRIDE         = 0x8624,
    VERTEX_ATTRIB_ARRAY_TYPE           = 0x8625,
    VERTEX_ATTRIB_ARRAY_NORMALIZED     = 0x886A,
    VERTEX_ATTRIB_ARRAY_POINTER        = 0x8645,
    VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F,
    
    /* Shader Source */
    COMPILE_STATUS                 = 0x8B81,
    INFO_LOG_LENGTH                = 0x8B84,
    SHADER_SOURCE_LENGTH           = 0x8B88,
    
    /* Shader Precision-Specified Types */
    LOW_FLOAT                      = 0x8DF0,
    MEDIUM_FLOAT                   = 0x8DF1,
    HIGH_FLOAT                     = 0x8DF2,
    LOW_INT                        = 0x8DF3,
    MEDIUM_INT                     = 0x8DF4,
    HIGH_INT                       = 0x8DF5,
    
    /* Framebuffer Object. */
    FRAMEBUFFER                    = 0x8D40,
    RENDERBUFFER                   = 0x8D41,
    
    RGBA4                          = 0x8056,
    RGB5_A1                        = 0x8057,
    RGB565                         = 0x8D62,
    DEPTH_COMPONENT16              = 0x81A5,
    STENCIL_INDEX                  = 0x1901,
    STENCIL_INDEX8                 = 0x8D48,
    DEPTH_STENCIL                  = 0x84F9,
    
    RENDERBUFFER_WIDTH             = 0x8D42,
    RENDERBUFFER_HEIGHT            = 0x8D43,
    RENDERBUFFER_INTERNAL_FORMAT   = 0x8D44,
    RENDERBUFFER_RED_SIZE          = 0x8D50,
    RENDERBUFFER_GREEN_SIZE        = 0x8D51,
    RENDERBUFFER_BLUE_SIZE         = 0x8D52,
    RENDERBUFFER_ALPHA_SIZE        = 0x8D53,
    RENDERBUFFER_DEPTH_SIZE        = 0x8D54,
    RENDERBUFFER_STENCIL_SIZE      = 0x8D55,
    
    FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE           = 0x8CD0,
    FRAMEBUFFER_ATTACHMENT_OBJECT_NAME           = 0x8CD1,
    FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL         = 0x8CD2,
    FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3,
    
    COLOR_ATTACHMENT0              = 0x8CE0,
    DEPTH_ATTACHMENT               = 0x8D00,
    STENCIL_ATTACHMENT             = 0x8D20,
    DEPTH_STENCIL_ATTACHMENT       = 0x821A,
    
    NONE                           = 0,
    
    FRAMEBUFFER_COMPLETE                      = 0x8CD5,
    FRAMEBUFFER_INCOMPLETE_ATTACHMENT         = 0x8CD6,
    FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7,
    FRAMEBUFFER_INCOMPLETE_DIMENSIONS         = 0x8CD9,
    FRAMEBUFFER_UNSUPPORTED                   = 0x8CDD,
    
    FRAMEBUFFER_BINDING            = 0x8CA6,
    RENDERBUFFER_BINDING           = 0x8CA7,
    MAX_RENDERBUFFER_SIZE          = 0x84E8,
    
    INVALID_FRAMEBUFFER_OPERATION  = 0x0506,
    
    /* WebGL-specific enums */
    UNPACK_FLIP_Y_WEBGL            = 0x9240,
    UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
    CONTEXT_LOST_WEBGL             = 0x9242,
    UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
    BROWSER_DEFAULT_WEBGL          = 0x9244,
  };
  
  
  /*! @name WebGL methods
   * The WebGL methods are mostly 1-on-1 translated to the identical
   * JavaScript call. You can use the WebGL methods in your resizeGL(),
   * paintGL() and updateGL() specializations. Wt takes care that data,
   * arguments, ... are transfered to the client side and that the equivalent
   * JavaScript WebGL funtion is executed.
   * @{
   */
  void debugger();
  /*! \brief GL function to activate an existing texture
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glActiveTexture.xml">
   * glActiveTexture() OpenGL ES manpage</a>
   */
  void activeTexture(GLenum texture);

  /*! \brief GL function to attach a shader to a program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glAttachShader.xml">
   * glAttachShader() OpenGL ES manpage</a>
   */
  void attachShader(Program program, Shader shader);

  /*! \brief GL function to bind an attribute to a given location
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBindAttribLocation.xml">
   * glBindAttribLocation() OpenGL ES manpage</a>
   */
  void bindAttribLocation(Program program, unsigned index,
                          const std::string &name);

  /*! \brief GL function to bind a buffer to a target
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBindBuffer.xml">
   * glBindBuffer() OpenGL ES manpage</a>
   */
  void bindBuffer(GLenum target, Buffer buffer);

  /*! \brief GL function to bind a frame buffer to a target
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBindFramebuffer.xml">
   * glBindFramebuffer() OpenGL ES manpage</a>
   */
  void bindFramebuffer(GLenum target, Framebuffer framebuffer);

  /*! \brief GL function to bind a render buffer to a target
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBindRenderbuffer.xml">
   * glBindRenderbuffer() OpenGL ES manpage</a>
   */
  void bindRenderbuffer(GLenum target, Renderbuffer renderbuffer);

  /*! \brief GL function to bind a texture to a target
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBindTexture.xml">
   * glBindTexture() OpenGL ES manpage</a>
   */
  void bindTexture(GLenum target, Texture texture);

  /*! \brief GL function to set the blending color
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBlendColor.xml">
   * glBlendColor() OpenGL ES manpage</a>
   */
  void blendColor(double red, double green, double blue, double alpha);

  /*! \brief GL function to set the blending equation
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBlendEquation.xml">
   * glBlendEquation() OpenGL ES manpage</a>
   */
  void blendEquation(GLenum mode);

  /*! \brief GL function that sets separate blending functions for RGB and alpha
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBlendEquationSeparate.xml">
   * glBlendEquationSeparate() OpenGL ES manpage</a>
   */
  void blendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);

  /*! \brief GL function to configure the blending function
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBlendFunc.xml">
   * glBlendFunc() OpenGL ES manpage</a>
   */
  void blendFunc(GLenum sfactor, GLenum dfactor);

  /*! \brief GL function that configures the blending function
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBlendFuncSeparate.xml">
   * glBlendFuncSeparate() OpenGL ES manpage</a>
   */
  void blendFuncSeparate(GLenum srcRGB, GLenum dstRGB, 
                         GLenum srcAlpha, GLenum dstAlpha);

  /*! \brief glBufferData - create and initialize a buffer object's data store
   *
   * Set the size of the currently bound WebGLBuffer object for the passed target.
   * The buffer is initialized to 0.
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferData.xml">
   * glBufferData() OpenGL ES manpage</a>
   */
  void bufferData(GLenum target, int size, GLenum usage) {
    js_ << "ctx.bufferData(" << toString(target) << ",";
    js_ << size << ",";
    js_ << toString(usage) << ");";
    GLDEBUG;
  }

  /*! \brief glBufferData - create and initialize a buffer object's data store
   *         from an ArrayBuffer
   *
   * Set the size and contents of the currently bound WebGLBuffer object to
   * be a copy of the given ArrayBuffer.
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferData.xml">
   * glBufferData() OpenGL ES manpage</a>
   *
   * \sa createAndLoadArrayBuffer
   */
  void bufferData(GLenum target, ArrayBuffer res, GLenum usage);

  /*! \brief glBufferData - create and initialize a buffer object's data store
   *         from an ArrayBuffer
   *
   * Set the size of the currently bound WebGLBuffer object to
   * arrayBufferSize, and copy the contents of the ArrayBuffer to
   * the buffer, starting at the given offset.
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferData.xml">
   * glBufferData() OpenGL ES manpage</a>
   *
   * \sa createAndLoadArrayBuffer
   */
  void bufferData(GLenum target, ArrayBuffer res, unsigned arrayBufferOffset,
                  unsigned arrayBufferSize, GLenum usage);

  /*! \brief GL function that loads float or double data in a VBO
   *
   * Unlike the C version, we can't accept a void * here. We must be able
   * to interpret the buffer's data in order to transmit it to the JS side.
   *
   * Later we may also want versions with strides and offsets to cope with
   * more complex buffer layouts that we typically see on desktop WebGL apps;
   * suggestions to improve this are welcome
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferData.xml">
   * glBufferData() OpenGL ES manpage</a>
   */
  template<typename Iterator>
  void bufferDatafv(GLenum target, const Iterator begin, const Iterator end, GLenum usage) {
    js_ << "ctx.bufferData(" << toString(target) << ",";
    renderfv(js_, begin, end);
    js_ << ","<< toString(usage) << ");";
    GLDEBUG;
  }

  /*! \brief GL function that updates an existing VBO with new integer data
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferData.xml">
   * glBufferData() OpenGL ES manpage</a>
   */
  template<typename Iterator>
  void bufferDataiv(GLenum target, const Iterator begin, const Iterator end,
                    GLenum usage, GLenum type)
  {
    js_ << "ctx.bufferData(" << toString(target) << ",";
    renderiv(js_, begin, end, type);
    js_ << ","<< toString(usage) << ");";
    GLDEBUG;
  }

  /*! \brief GL function that updates an existing VBO with new float or double
   * data
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferSubData.xml">
   * glBufferSubData() OpenGL ES manpage</a>
   */
  template<typename Iterator>
  void bufferSubDatafv(GLenum target, unsigned offset, const Iterator begin, const Iterator end)
  {
    js_ << "ctx.bufferSubData(" << toString(target) << "," << offset << ",";
    renderfv(js_, begin, end);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function that loads integer data in a VBO
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferSubData.xml">
   * glBufferSubData() OpenGL ES manpage</a>
   */
  template<typename Iterator>
  void bufferSubDataiv(GLenum target, unsigned offset, const Iterator begin, Iterator end, GLenum type)
  {
    js_ << "ctx.bufferSubData(" << toString(target) << "," << offset << ",";
    renderiv(js_, begin, end, type);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief Initialize a buffer object's data store from an ArrayBuffer
   *
   * Load the data of the currently bound WebGLBuffer object from
   * the given ArrayBuffer. The first byte of the resource data will
   * be written at the given offset of the currently bound buffer.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferSubData.xml">
   * glBufferSubData() OpenGL ES manpage</a>
   *
   * \sa createAndLoadArrayBuffer
   */
  void bufferSubData(GLenum target, unsigned offset, ArrayBuffer res);

  /*! \brief Initialize a buffer object's data store from an ArrayBuffer
   *
   * Load the data of the currently bound WebGLBuffer object from
   * the given ArrayBuffer. The byte at position arrayBufferOffset
   * will be written to the currently bound buffer at position offset.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glBufferSubData.xml">
   * glBufferSubData() OpenGL ES manpage</a>
   *
   * \sa createAndLoadArrayBuffer
   */
  void bufferSubData(GLenum target, unsigned offset, ArrayBuffer res,
                     unsigned arrayBufferOffset, unsigned size);

  //GLenum checkFramebufferStatus(GLenum target);

  /*! \brief GL function that clears the given buffers
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glClear.xml">
   * glClear() OpenGL ES manpage</a>
   */
  void clear(WFlags<GLenum> mask);

  /*! \brief GL function that sets the clear color of the color buffer
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glClearColor.xml">
   * glClearColor() OpenGL ES manpage</a>
   */
  void clearColor(double r, double g, double b, double a);

  /*! \brief GL function that configures the depth to be set when the
   * depth buffer is cleared
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glClearDepthf.xml">
   * glClearDepthf() OpenGL ES manpage</a>
   */
  void clearDepth(double depth);

  /*! \brief GL function
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glClearStencil.xml">
   * glClearStencil() OpenGL ES manpage</a>
   */
  void clearStencil(int s);

  /*! \brief GL function
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glColorMask.xml">
   * glColorMask() OpenGL ES manpage</a>
   */
  void colorMask(bool red, bool green, bool blue, bool alpha);

  /*! \brief GL function to compile a shader
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glCompileShader.xml">
   * glCompileShader() OpenGL ES manpage</a>
   */
  void compileShader(Shader shader);

  /*! \brief GL function to copy a texture image
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glCopyTexImage2D.xml">
   * glCopyTexImage2D() OpenGL ES manpage</a>
   */
  void copyTexImage2D(GLenum target, int level,
                      GLenum internalformat,
                      int x, int y,
                      unsigned width, unsigned height, 
                      int border);

  /*! \brief GL function that copies a part of a texture image
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glCopyTexSubImage2D.xml">
   * glCopyTexSubImage2D() OpenGL ES manpage</a>
   */
  void copyTexSubImage2D(GLenum target, int level,
                         int xoffset, int yoffset,
                         int x, int y,
                         unsigned width, unsigned height);

  /*! \brief GL function that creates an empty VBO
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glGenBuffers.xml">
   * glGenBuffers() OpenGL ES manpage</a>
   */
  Buffer createBuffer();

  /*! \brief Function that creates an ArrayBuffer by loading data from
   *         a given URL.
   *
   * The ArrayBuffer is loaded before executing initGL.
   * Use it to load binary data for use in bufferData() or bufferSubData().
   */
  ArrayBuffer createAndLoadArrayBuffer(const std::string &url);

  /*! \brief GL function that creates a frame buffer object
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glGenFramebuffers.xml">
   * glGenFramebuffers() OpenGL ES manpage</a>
   */
  Framebuffer createFramebuffer();

  /*! \brief GL function that creates an empty program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glCreateProgram.xml">
   * glCreateProgram() OpenGL ES manpage</a>
   */
  Program createProgram();

  /*! \brief GL function that creates a render buffer object
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glGenRenderbuffers.xml">
   * glGenRenderbuffers() OpenGL ES manpage</a>
   */
  Renderbuffer createRenderbuffer();

  /*! \brief GL function that creates an empty shader
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glCreateShader.xml">
   * glCreateShader() OpenGL ES manpage</a>
   */
  Shader createShader(GLenum shader);

  /*! \brief GL function that creates an empty texture
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glGenTextures.xml">
   * glGenTextures() OpenGL ES manpage</a>
   */
  Texture createTexture();

  /*! \brief GL function that creates an image texture
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glGenTextures.xml">
   * glGenTextures() OpenGL ES manpage</a>
   */
  Texture createTextureAndLoad(const std::string &url);

  /*! \brief GL function that configures the backface culling mode
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glCullFace.xml">
   * glCullFace() OpenGL ES manpage</a>
   */
  void cullFace(GLenum mode);

  /*! \brief GL function that deletes a VBO
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDeleteBuffers.xml">
   * glDeleteBuffers() OpenGL ES manpage</a>
   */
  void deleteBuffer(Buffer buffer);

  /*! \brief GL function that deletes a frame buffer
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDeleteFramebuffers.xml">
   * glDeleteFramebuffers() OpenGL ES manpage</a>
   */
  void deleteFramebuffer(Framebuffer framebuffer);

  /*! \brief GL function that deletes a program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDeleteProgram.xml">
   * glDeleteProgram() OpenGL ES manpage</a>
   */
  void deleteProgram(Program program);

  /*! \brief GL function that deletes a render buffer
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDeleteRenderbuffers.xml">
   * glDeleteRenderbuffers() OpenGL ES manpage</a>
   */
  void deleteRenderbuffer(Renderbuffer renderbuffer);

  /*! \brief GL function that depetes a shader
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDeleteShader.xml">
   * glDeleteShader() OpenGL ES manpage</a>
   */
  void deleteShader(Shader shader);

  /*! \brief GL function that deletes a texture
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDeleteTextures.xml">
   * glDeleteTextures() OpenGL ES manpage</a>
   */
  void deleteTexture(Texture texture);

  /*! \brief GL function to set the depth test function
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDepthFunc.xml">
   * glDepthFunc() OpenGL ES manpage</a>
   */
  void depthFunc(GLenum func);

  /*! \brief GL function that enables or disables writing to the depth buffer
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDepthMask.xml">
   * glDepthMask() OpenGL ES manpage</a>
   */
  void depthMask(bool flag);

  /*! \brief GL function that specifies to what range the normalized [-1,1] z
   * values should match.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDepthRangef.xml">
   * glDepthRangef() OpenGL ES manpage</a>
   */
  void depthRange(double zNear, double zFar);

  /*! \brief GL function that detaches a shader from a program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDetachShader.xml">
   * glDetachShader() OpenGL ES manpage</a>
   */
  void detachShader(Program program, Shader shader);

  /*! \brief GL function to disable features
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDisable.xml">
   * glDisable() OpenGL ES manpage</a>
   */
  void disable(GLenum cap);

  /*! \brief GL function to disable the vertex attribute array
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDisableVertexAttribArray.xml">
   * glDisableVertexAttribArray() OpenGL ES manpage</a>
   */
  void disableVertexAttribArray(AttribLocation index);

  /*! \brief GL function to draw a VBO
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDrawArrays.xml">
   * glDrawArrays() OpenGL ES manpage</a>
   */
  void drawArrays(GLenum mode, int first, unsigned count);

  /*! \brief GL function to draw indexed VBOs
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glDrawElements.xml">
   * glDrawElements() OpenGL ES manpage</a>
   */
  void drawElements(GLenum mode, unsigned count, GLenum type, unsigned offset);

  /*! \brief GL function to enable features
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glEnable.xml">
   * glEnable() OpenGL ES manpage</a>
   */
  void enable(GLenum cap);

  /*! \brief GL function to enable the vertex attribute array
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glEnableVertexAttribArray.xml">
   * glEnableVertexAttribArray() OpenGL ES manpage</a>
   */
  void enableVertexAttribArray(AttribLocation index);

  /*! \brief GL function to wait until given commands are executed
   *
   * This call is transfered to JS, but the server will never wait on
   * this call.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glFinish.xml">
   * glFinish() OpenGL ES manpage</a>
   */
  void finish();

  /*! \brief GL function to force execution of GL commands in finite time.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glFlush.xml">
   * glFlush() OpenGL ES manpage</a>
   */
  void flush();

  /*! \brief GL function to attach the given renderbuffer to the currently
   *         bound frame buffer.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glFramebufferRenderbuffer.xml">
   * glFramebufferRenderbuffer() OpenGL ES manpage</a>
   */
  void framebufferRenderbuffer(GLenum target, GLenum attachment, 
                               GLenum renderbuffertarget, 
                               Renderbuffer renderbuffer);

  /*! \brief GL function to render directly into a texture image.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glFramebufferTexture2D.xml">
   * glFramebufferTexture2D() OpenGL ES manpage</a>
   */
  void framebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, 
                            Texture texture, int level);

  /*! \brief GL function that specifies which side of a triangle is the
   * front side
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glFrontFace.xml">
   * glFrontFace() OpenGL ES manpage</a>
   */
  void frontFace(GLenum mode);

  /*! \brief GL function that generates a set of mipmaps for a texture
   * object.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glGenerateMipmap.xml">
   * glGenerateMipmap() OpenGL ES manpage</a>
   */
  void generateMipmap(GLenum target);

  //WebGLActiveInfo getActiveAttrib(WebGLProgram program, GLuint index);
  //WebGLActiveInfo getActiveUniform(WebGLProgram program, GLuint index);
  //WebGLShader[ ] getAttachedShaders(WebGLProgram program);

  /*! \brief GL function to retrieve an attribute's location in a Program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glGetAttribLocation.xml">
   * glGetAttribLocation() OpenGL ES manpage</a>
   */
  AttribLocation getAttribLocation(Program program, const std::string &attrib);

  //any getParameter(GLenum pname);
  //any getBufferParameter(GLenum target, GLenum pname);

  //GLenum getError();

  //any getFramebufferAttachmentParameter(GLenum target, GLenum attachment, 
  //                                      GLenum pname);
  //any getProgramParameter(WebGLProgram program, GLenum pname);
  //DOMString getProgramInfoLog(WebGLProgram program);
  //any getRenderbufferParameter(GLenum target, GLenum pname);
  //any getShaderParameter(WebGLShader shader, GLenum pname);
  //DOMString getShaderInfoLog(WebGLShader shader);

  //DOMString getShaderSource(WebGLShader shader);

  //any getTexParameter(GLenum target, GLenum pname);

  //any getUniform(WebGLProgram program, WebGLUniformLocation location);

  /*! \brief GL function to retrieve a Uniform's location in a Program.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glGetUniformLocation.xml">
   * glGetUniformLocation() OpenGL ES manpage</a>
   */
  UniformLocation getUniformLocation(Program program, const std::string location);

  //any getVertexAttrib(GLuint index, GLenum pname);

  //GLsizeiptr getVertexAttribOffset(GLuint index, GLenum pname);

  /*! \brief GL function to give hints to the render pipeline
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glHint.xml">
   * glHint() OpenGL ES manpage</a>
   */
  void hint(GLenum target, GLenum mode);

  //GLboolean isBuffer(WebGLBuffer buffer);
  //GLboolean isEnabled(GLenum cap);
  //GLboolean isFramebuffer(WebGLFramebuffer framebuffer);
  //GLboolean isProgram(WebGLProgram program);
  //GLboolean isRenderbuffer(WebGLRenderbuffer renderbuffer);
  //GLboolean isShader(WebGLShader shader);
  //GLboolean isTexture(WebGLTexture texture);

  /*! \brief GL function to set the line width
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glLineWidth.xml">
   * glLineWidth() OpenGL ES manpage</a>
   */
  void lineWidth(double width);

  /*! \brief GL function to link a program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glLinkProgram.xml">
   * glLinkProgram() OpenGL ES manpage</a>
   */
  void linkProgram(Program program);

  /*! \brief GL function to set the pixel storage mode
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glPixelStorei.xml">
   * glPixelStorei() OpenGL ES manpage</a>
   */
  void pixelStorei(GLenum pname, int param);

  /*! \brief GL function to apply modifications to Z values
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glPolygonOffset.xml">
   * glPolygonOffset() OpenGL ES manpage</a>
   */
  void polygonOffset(double factor, double units);

  //void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, 
  //                GLenum format, GLenum type, ArrayBufferView pixels);

  /*! \brief GL function to allocate the appropriate amount of memory for
   *         a render buffer
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glRenderbufferStorage.xml">
   * glSampleCoverage() OpenGL ES manpage</a>
   */
  void renderbufferStorage(GLenum target, GLenum internalformat, 
                           unsigned width, unsigned height);

  /*! \brief GL function to set multisample parameters
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glSampleCoverage.xml">
   * glSampleCoverage() OpenGL ES manpage</a>
   */
  void sampleCoverage(double value, bool invert);

  /*! \brief GL function to define the scissor box
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glScissor.xml">
   * glScissor() OpenGL ES manpage</a>
   */
  void scissor(int x, int y, unsigned width, unsigned height);

  /*! \brief GL function to set a shader's source code
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glShaderSource.xml">
   * glShaderSource() OpenGL ES manpage</a>
   */
  void shaderSource(Shader shader, const std::string &src);

  /*! \brief GL function to set stencil test parameters
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glStencilFunc.xml">
   * glStencilFunc() OpenGL ES manpage</a>
   */
  void stencilFunc(GLenum func, int ref, unsigned mask);

  /*! \brief GL function to set stencil test parameters for front and/or
   * back stencils
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glStencilFuncSeparate.xml">
   * glStencilFuncSeparate() OpenGL ES manpage</a>
   */
  void stencilFuncSeparate(GLenum face, GLenum func, int ref, unsigned mask);

  /*! \brief GL function to control which bits are to be written in the stencil
   * buffer
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glStencilMask.xml">
   * glStencilMask() OpenGL ES manpage</a>
   */
  void stencilMask(unsigned mask);

  /*! \brief GL function to control which bits are written to the front and/or
   * back stencil buffers
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glStencilMaskSeparate.xml">
   * glStencilMaskSeparate() OpenGL ES manpage</a>
   */
  void stencilMaskSeparate(GLenum face, unsigned mask);

  /*! \brief GL function to set stencil test actions
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glStencilOp.xml">
   * glStencilOp() OpenGL ES manpage</a>
   */
  void stencilOp(GLenum fail, GLenum zfail, GLenum zpass);

  /*! \brief GL function to set front and/or back stencil test actions
   * separately
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glStencilOpSeparate.xml">
   * glStencilOpSeparate() OpenGL ES manpage</a>
   */
  void stencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);

  //void texImage2D(TextureTargetEnum target, int level, PixelFormatEnum internalformat, 
  //                GLsizei width, GLsizei height, GLint border, PixelFormatEnum format, 
  //                GLenum type, ArrayBufferView pixels);
  /*! \brief GL function to reserve space for a 2D texture, without specifying its
   *         contents.
   *
   * This corresponds to calling the WebGL function
   * void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
   * GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView pixels)
   * with null as last parameters. The value of 'type' is then of no importance and is
   * therefore omitted from this function.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glTexImage2D.xml">
   * glTexImage2D() OpenGL ES manpage</a>
   */
  void texImage2D(GLenum target, int level, GLenum internalformat, 
                  unsigned width, unsigned height, int border, GLenum format);

  //void texImage2D(TextureTargetEnum target, int level, PixelFormatEnum internalformat,
  //                PixelFormatEnum format, GLenum type, ImageData pixels);

  /*! \brief GL function to load a 2D texture from a WImage
   *
   * Note: WImage must be loaded before this function is executed.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glTexImage2D.xml">
   * glTexImage2D() OpenGL ES manpage</a>
   */
  void texImage2D(GLenum target, int level, GLenum internalformat,
                  GLenum format, GLenum type, WImage *image);

  //void texImage2D(TextureTargetEnum target, int level, PixelFormatEnum internalformat,
  //                PixelFormatEnum format, GLenum type, HTMLCanvasElement canvas);

  /*! \brief GL function to load a 2D texture from a WVideo
   *
   * Note: the video must be loaded prior to calling this function. The
   * current frame is used as texture image.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glTexImage2D.xml">
   * glTexImage2D() OpenGL ES manpage</a>
   */
  void texImage2D(GLenum target, int level, GLenum internalformat, GLenum format,
                  GLenum type, WVideo *video);

  /*! \brief GL function to load a 2D texture loaded with createTextureAndLoad()
   *
   * This function must only be used for textures created with
   * createTextureAndLoad()
   *
   * Note: the WGLWidget implementation will delay rendering until
   * all textures created with createTextureAndLoad() are loaded in the
   * browser.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glTexImage2D.xml">
   * glTexImage2D() OpenGL ES manpage</a>
   */
  void texImage2D(GLenum target, int level, GLenum internalformat,
                  GLenum format, GLenum type, Texture texture);

  //void texParameterf(TextureTargetEnum target,
  //                   TextureParameterNameEnum pname, double param);

  /*! \brief GL function to set texture parameters
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glTexParameter.xml">
   * glTexParameter() OpenGL ES manpage</a>
   */
  void texParameteri(GLenum target, GLenum pname, GLenum param);

  //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, 
  //                   GLsizei width, GLsizei height, 
  //                   GLenum format, GLenum type, ArrayBufferView pixels);
  //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, 
  //                   GLenum format, GLenum type, ImageData pixels);
  //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, 
  //                   GLenum format, GLenum type, HTMLImageElement image);
  //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, 
  //                   GLenum format, GLenum type, HTMLCanvasElement canvas);
  //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, 
  //                   GLenum format, GLenum type, HTMLVideoElement video);


  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniform1f(const UniformLocation &location, double x) {
    char buf[30];
    js_ << "ctx.uniform1f(" << location << "," << makeFloat(x, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename Array>
  void uniform1fv(const UniformLocation &location, const Array *value) {
    js_ << "ctx.uniform1fv(" << location << ",";
    renderfv(js_, value, value + 1);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniform1i(const UniformLocation &location, int x) {
    char buf[30];
    js_ << "ctx.uniform1i(" << location << ",";
    js_ << makeInt(x, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename Array>
  void uniform1iv(const UniformLocation &location, const Array *value) {
    js_ << "ctx.uniform1iv(" << location << ",";
    renderiv(js_, value, value + 1, INT);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniform2f(const UniformLocation &location, double x, double y) {
    char buf[30];
    js_ << "ctx.uniform2f(" << location << ",";
    js_ << makeFloat(x, buf) << ",";
    js_ << makeFloat(y, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename Array>
  void uniform2fv(const UniformLocation &location, const Array *value) {
    js_ << "ctx.uniform2fv(" << location << ",";
    renderfv(js_, value, value + 2);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniform2i(const UniformLocation &location, int x, int y) {
    char buf[30];
    js_ << "ctx.uniform2i(" << location << ",";
    js_ << makeInt(x, buf) << ",";
    js_ << makeInt(y, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename Array>
  void uniform2iv(const UniformLocation &location, const Array *value) {
    js_ << "ctx.uniform2iv(" << location << ",";
    renderiv(js_, value, value + 2, INT);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniform3f(const UniformLocation &location,
                 double x, double y, double z) {
    char buf[30];
    js_ << "ctx.uniform3f(" << location << ",";
    js_ << makeFloat(x, buf) << ",";
    js_ << makeFloat(y, buf) << ",";
    js_ << makeFloat(z, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename Array>
  void uniform3fv(const UniformLocation &location, const Array *value) {
    js_ << "ctx.uniform3fv(" << location << ",";
    renderfv(js_, value, value + 3);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniform3i(const UniformLocation &location, int x, int y, int z) {
    char buf[30];
    js_ << "ctx.uniform3i(" << location << ",";
    js_ << makeInt(x, buf) << ",";
    js_ << makeInt(y, buf) << ",";
    js_ << makeInt(z, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename Array>
  void uniform3iv(const UniformLocation &location, const Array *value) {
    js_ << "ctx.uniform3iv(" << location << ",";
    renderiv(js_, value, value + 3, INT);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniform4f(const UniformLocation &location,
                 double x, double y, double z, double w) {
    char buf[30];
    js_ << "ctx.uniform4f(" << location << ",";
    js_ << makeFloat(x, buf) << ",";
    js_ << makeFloat(y, buf) << ",";
    js_ << makeFloat(z, buf) << ",";
    js_ << makeFloat(w, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename Array>
  void uniform4fv(const UniformLocation &location, const Array *value) {
    js_ << "ctx.uniform4fv(" << location << ",";
    renderfv(js_, value, value + 4);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniform4i(const UniformLocation &location, int x, int y, int z, int w) {
    char buf[30];
    js_ << "ctx.uniform4i(" << location << ",";
    js_ << makeInt(x, buf) << ",";
    js_ << makeInt(y, buf) << ",";
    js_ << makeInt(z, buf) << ",";
    js_ << makeInt(w, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform variable of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename Array>
  void uniform4iv(const UniformLocation &location, const Array *value) {
    js_ << "ctx.uniform4iv(" << location << ",";
    renderiv(js_, value, value + 4, INT);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform matrix of the current
   * program
   *
   * Attention: The OpenGL ES specification states that transpose MUST be
   * false.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename MatrixType>
  void uniformMatrix2fv(const UniformLocation &location,
                        bool transpose, const MatrixType *value) {
    js_ << "ctx.uniformMatrix2fv(" << location << ","
      << (transpose?"true":"false") << ",";
    renderfv(js_, value, value + 4);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform matrix of the current
   * program
   *
   * This function renders the matrix in the proper row/column order.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template< typename T>
  void uniformMatrix2(const UniformLocation &location,
                      const WGenericMatrix<T, 2, 2> &m) {
    js_ << "ctx.uniformMatrix2fv(" << location << ",false,";
    WGenericMatrix<T, 2, 2> t(m.transposed());
    renderfv(js_, t.data().begin(), t.data().end());
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform matrix of the current
   * program
   *
   * Attention: The OpenGL ES specification states that transpose MUST be
   * false.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename MatrixType>
  void uniformMatrix3fv(const UniformLocation &location, bool transpose, const MatrixType *value) {
    js_ << "ctx.uniformMatrix3fv(" << location << ","
      << (transpose?"true":"false") << ",";
    renderfv(js_, value,value + 9);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform matrix of the current
   * program
   *
   * This function renders the matrix in the proper row/column order.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename T>
  void uniformMatrix3(const UniformLocation &location,
                      const WGenericMatrix<T, 3, 3> &m) {
    js_ << "ctx.uniformMatrix3fv(" << location << ",false,";
    WGenericMatrix<T, 3, 3> t(m.transposed());
    renderfv(js_, t.data().begin(), t.data().end());
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform matrix of the current
   * program
   *
   * Attention: The OpenGL ES specification states that transpose MUST be
   * false.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename MatrixType>
  void uniformMatrix4fv(const UniformLocation &location, bool transpose,
                        const MatrixType *value) {
    js_ << "ctx.uniformMatrix4fv(" << location << ","
      << (transpose?"true":"false") << ",";
    renderfv(js_, value, value + 16);
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform matrix of the current
   * program
   *
   * This function renders the matrix in the proper row/column order.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  template<typename T>
  void uniformMatrix4(const UniformLocation &location,
                      const WGenericMatrix<T, 4, 4> &m) {
    js_ << "ctx.uniformMatrix4fv(" << location << ",false,";
    WGenericMatrix<T, 4, 4> t(m.transposed());
    renderfv(js_, t.data().begin(), t.data().end());
    js_ << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of a uniform matrix of the current
   * program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUniform.xml">
   * glUniform() OpenGL ES manpage</a>
   */
  void uniformMatrix4(const UniformLocation &location,
                      const JavaScriptMatrix4x4 &m) {
    js_ << "ctx.uniformMatrix4fv(" << location << ",false,";
    js_ << m.jsRef() << ");";
    GLDEBUG;
  }


  /*! \brief GL function to set the current active shader program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glUseProgram.xml">
   * glUseProgram() OpenGL ES manpage</a>
   */
  void useProgram(Program program);

  /*! \brief GL function to validate a program
   *
   * implementation note: there is currently not yet a method to read
   * out the validation result.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glValidateProgram.xml">
   * glValidateProgram() OpenGL ES manpage</a>
   */
  void validateProgram(Program program);

  /*! \brief GL function to set the value of an attribute of the current program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttrib.xml">
   * glVertexAttrib() OpenGL ES manpage</a>
   */
  void vertexAttrib1f(AttribLocation location, double x) {
    char buf[30];
    js_ << "ctx.vertexAttrib1f(" << location << ",";
    js_ << makeFloat(x, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of an attribute of the current program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttrib.xml">
   * glVertexAttrib() OpenGL ES manpage</a>
   */
  template<typename Array>
  void vertexAttrib1fv(AttribLocation location, const Array *values) {
    vertexAttrib1f(location, values[0]);
  }

  /*! \brief GL function to set the value of an attribute of the current program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttrib.xml">
   * glVertexAttrib() OpenGL ES manpage</a>
   */
  void vertexAttrib2f(AttribLocation location, double x, double y) {
    char buf[30];
    js_ << "ctx.vertexAttrib2f(" << location << ",";
    js_ << makeFloat(x, buf) << ",";
    js_ << makeFloat(y, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of an attribute of the current program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttrib.xml">
   * glVertexAttrib() OpenGL ES manpage</a>
   */
  template<typename Array>
  void vertexAttrib2fv(AttribLocation location, const Array *values) {
    vertexAttrib2f(location, values[0], values[1]);
  }

  /*! \brief GL function to set the value of an attribute of the current program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttrib.xml">
   * glVertexAttrib() OpenGL ES manpage</a>
   */
  void vertexAttrib3f(AttribLocation location, double x, double y, double z) {
    char buf[30];
    js_ << "ctx.vertexAttrib3f(" << location << ",";
    js_ << makeFloat(x, buf) << ",";
    js_ << makeFloat(y, buf) << ",";
    js_ << makeFloat(z, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of an attribute of the current program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttrib.xml">
   * glVertexAttrib() OpenGL ES manpage</a>
   */
  template<typename Array>
  void vertexAttrib3fv(AttribLocation location, const Array *values) {
    vertexAttrib3f(location, values[0], values[1], values[2]);
  }

  /*! \brief GL function to set the value of an attribute of the current program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttrib.xml">
   * glVertexAttrib() OpenGL ES manpage</a>
   */
  void vertexAttrib4f(AttribLocation location,
                      double x, double y, double z, double w) {
    char buf[30];
    js_ << "ctx.vertexAttrib4f(" << location << ",";
    js_ << makeFloat(x, buf) << ",";
    js_ << makeFloat(y, buf) << ",";
    js_ << makeFloat(z, buf) << ",";
    js_ << makeFloat(w, buf) << ");";
    GLDEBUG;
  }

  /*! \brief GL function to set the value of an attribute of the current program
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttrib.xml">
   * glVertexAttrib() OpenGL ES manpage</a>
   */
  template<typename Array>
  void vertexAttrib4fv(AttribLocation location, const Array *values) {
    vertexAttrib4f(location, values[0], values[1], values[2], values[3]);
  }

  /*! \brief GL function to bind a VBO to an attribute
   *
   * This function links the given attribute to the VBO currently bound
   * to the ARRAY_BUFFER target.
   *
   * The size parameter specifies the number of components per attribute (1
   * to 4). The type parameter is also used to determine the size of each
   * component.
   *
   * The size of a float is 8 bytes.
   *
   * In WGLWidget, the size of an int is 4 bytes.
   *
   * The stride is in bytes.
   *
   * The maximum stride is 255.
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glVertexAttribPointer.xml">
   * glVertexAttribPointer() OpenGL ES manpage</a>
   */
  void vertexAttribPointer(AttribLocation location, int size,
    GLenum type, bool normalized, unsigned stride, unsigned offset);

  /*! \brief GL function to set the viewport
   *
   * <a href="http://www.khronos.org/opengles/sdk/2.0/docs/man/glViewport.xml">
   * glViewport() OpenGL ES manpage</a>
   */
  void viewport(int x, int y, unsigned width, unsigned height);

  /*! @}
   */

  /*! @name Additional WebGL methods
   *
   * These methods are used to render WebGL content, but have no equivalent
   * in WebGL.
   *
   * Like the WebGL methods, you can use the WebGL methods in your resizeGL(),
   * paintGL() and updateGL() specializations. It makes no sense to call these
   * methods outside those functions.
   * @{
   */
  /*! \brief Create a matrix that can be manipulated in client-side
   * JavaScript
   */
  JavaScriptMatrix4x4 createJavaScriptMatrix4();

  /*! \brief Set the value of a client-side JavaScript matrix created by
   * createJavaScriptMatrix4x4()
   */
  template<typename T>
  void setJavaScriptMatrix4(const JavaScriptMatrix4x4 &jsm,
                            const WGenericMatrix<T, 4, 4> &m) {
    js_ << WT_CLASS ".glMatrix.mat4.set(";
    WGenericMatrix<T, 4, 4> t(m.transposed());
    renderfv(js_, t.data().begin(), t.data().end());
    js_ << ", " << jsm.jsRef() << ");";
    GLDEBUG;
  }

  /*! @}
   */

  // Todo:
  // Get the current client-side value of a JavaScriptMatrix
  //WMatrix4x4 getJavaScriptMatrix4();

  /*! \brief Add a mouse handler to the widget that looks at a given point
   *
   * This will allow a user to change client-side matrix m with the
   * mouse. M is a model transformation matrix, representing the viewpoint of
   * the camera.
   *
   * Through mouse operations, the camera can be changed by the user, but
   * (lX, lY, lZ) will always be at the center of the display, (uX, uY, uZ)
   * is considered to be the up direction, and the distance of the camera to
   * (lX, lY, lZ) will never change.
   *
   * Pressing the left mouse button and moving the mouse left/right will
   * rotate the camera around the up (uX, uY, uZ) direction. Moving up/down
   * will tilt the camera (causing it to move up/down to keep the lookpoint
   * centered). The scroll wheel simulates zooming by scaling the scene.
   *
   * pitchRate and yawRate control how much the camera will move per mouse
   * pixel.
   *
   * Usually this method is called after setting a camera transformation
   * with a client-side matrix in initializeGL(). However, this function
   * may also be called from outside the intializeGL()/paintGL()/updateGL()
   * methods (but not before m was initialized).
   */
  void setClientSideLookAtHandler(const JavaScriptMatrix4x4 &m,
                                  double lX, double lY, double lZ,
                                  double uX, double uY, double uZ,
                                  double pitchRate, double yawRate);

  /*! \brief Add a mouse handler to the widget that allows 'walking' in
   * the scene
   *
   * This will allow a user to change client-side matrix m with the
   * mouse. M is a model transformation matrix, representing the viewpoint of
   * the camera.
   *
   * Through mouse operations, the camera can be changed by the user, as if
   * he is walking around on a plane. 
   *
   * Pressing the left mouse button and moving the mouse left/right will
   * rotate the camera around Y axis. Moving the mouse up/down will move
   * the camera in the Z direction (walking forward/backward).
   * centered).
   *
   * frontStep and rotStep control how much the camera will move per mouse
   * pixel.
   *
   * Usually this method is called after setting a camera transformation
   * with a client-side matrix in initializeGL(). However, this function
   * may also be called from outside the intializeGL()/paintGL()/updateGL()
   * methods (but not before m was initialized).
   */
  void setClientSideWalkHandler(const JavaScriptMatrix4x4 &m,
                                double frontStep, double rotStep);

  /*! \brief Sets the content to be displayed when WebGL is not available.
   *
   * If Wt cannot create a working WebGL context, this content will be
   * shown to the user. This may be a text explanation, or a pre-rendered
   * image, or a video, a flash movie, ...
   *
   * The default is a widget that explains to the user that he has no
   * WebGL support.
   */
  void setAlternativeContent(WWidget *alternative);

  /*! \brief A JavaScript slot that repaints the widget when triggered.
   *
   * This is useful for client-side initiated repaints. You may e.g. use this
   * if you write your own client-side mouse handler, or if you updated
   * a texture, or if you're playing a video texture.
   */
  JSlot &repaintSlot() { return repaintSlot_; }

  /*! \brief enable client-side error messages (read detailed doc!)
   *
   * This option will add client-side code to check the result of every
   * WebGL call, and will popup an error dialog if a WebGL call returned
   * an error. The JavaScript then invokes the client-side debugger. This
   * code is intended to test your application, and should not be used in
   * production.
   *
   * Note that this option will not have any effect unless both Wt and your
   * application is built with WT_WGLWIDGET_DEBUG defined.
   */
  static void enableClientErrorChecks(bool enable = true) {
#ifdef WT_WGLWIDGET_DEBUG
    debugging_ = enable;
#endif
  }

    /*! \brief Inject JavaScript into the current js-stream.
    *
    * Careful: this method directly puts the given jsString into
    * the JavaScript stream, whatever state it current has.
    * For example, if called in initGL(), it will put the jsString
    * into the client-side initGL() code.
    */
    void injectJS(const std::string & jsString){js_ << jsString;}

protected:
  virtual DomElementType domElementType() const;
  virtual DomElement *createDomElement(WApplication *app);
  virtual void getDomChanges(std::vector<DomElement *>& result, WApplication *app);
  virtual void updateDom(DomElement &element, bool all);

  virtual void render(WFlags<RenderFlag> flags);
  virtual std::string renderRemoveJs();

private:
  std::string glObjJsRef();
  virtual void layoutSizeChanged(int width, int height);

  int renderWidth_;
  int renderHeight_;
  bool updatePaintGL_;
  bool updateResizeGL_;
  bool updateGL_;
  bool sizeChanged_;

  WWidget *alternative_;

  unsigned shaders_;
  unsigned programs_;
  unsigned attributes_;
  unsigned uniforms_;
  unsigned buffers_;
  unsigned arrayBuffers_;

  unsigned framebuffers_;
  unsigned renderbuffers_;
  unsigned textures_;
  unsigned matrices_;
  std::vector<std::pair<std::string, std::string> > preloadImages_;

  // first: ArrayBuffer string, second: url
  std::vector<std::pair<ArrayBuffer, std::string> > preloadArrayBuffers_;

  // If client detects that a WebGL context cannot be created, it fires this
  // signal, and the handler sets wegGlNotAvailable_ to true. If this happens,
  // the client side will have deleted the canvas widget.
  JSignal<void> webglNotAvailable_;
  void webglNotAvailable();
  bool webGlNotAvailable_;

  JSlot mouseWentDownSlot_;
  JSlot mouseWentUpSlot_;
  JSlot mouseDraggedSlot_;
  JSlot mouseWheelSlot_;
  JSlot repaintSlot_;

  std::stringstream delayedJavaScript_;
  std::stringstream js_;

#ifndef WT_TARGET_JAVA
  template<typename Iterator>
  static void renderfv(std::ostream &os, Iterator begin, Iterator end)
  {
    char buf[30];
    os << "new Float32Array([";
    for (Iterator i = begin; i != end; ++i)
      os << (i == begin ? "" : ",") << makeFloat(*i, buf);
    os << "])";
  }
#endif

  template<typename Iterator>
  static void renderiv(std::ostream &os, Iterator begin, Iterator end, GLenum type)
  {
    switch (type) {
    case BYTE:
      os << "new Int8Array([";
      break;
    case UNSIGNED_BYTE:
      os << "new Uint8Array([";
      break;
    case SHORT:
      os << "new Int16Array([";
      break;
    case UNSIGNED_SHORT:
      os << "new Uint16Array([";
      break;
    case INT:
      os << "new Int32Array([";
      break;
    default:
      // Should we warn?
    case UNSIGNED_INT:
      os << "new Uint32Array([";
      break;
    }
    char buf[30];
    for (Iterator i = begin; i != end; ++i)
      os << (i == begin ? "" : ",") << makeInt(*i, buf);
    os << "])";
  }

  static const char *makeFloat(double d, char *buf);
  static const char *makeInt(int i, char *buf);
  static const char *toString(GLenum e);

  void defineJavaScript();
  void connectJavaScript(EventSignalBase &s,
                         const std::string &methodName);

#ifdef WT_WGLWIDGET_DEBUG
  static bool debugging_;
#endif
};

W_DECLARE_OPERATORS_FOR_FLAGS(WGLWidget::GLenum)
W_DECLARE_OPERATORS_FOR_FLAGS(WGLWidget::ClientSideRenderer)
}

#endif
