/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkOpenGLPainterDeviceAdapter.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*
 * Copyright 2004 Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the
 * U.S. Government. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that this Notice and any
 * statement of authorship are reproduced on all copies.
 */

#include "vtkOpenGLPainterDeviceAdapter.h"

#include "vtkDataArray.h"
#include "vtkDataSetAttributes.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLGL2PSHelper.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkgl.h"

#include <algorithm>

#include "vtkOpenGL.h"
#include "vtkOpenGLError.h"

vtkStandardNewMacro(vtkOpenGLPainterDeviceAdapter);

//-----------------------------------------------------------------------------
vtkOpenGLPainterDeviceAdapter::vtkOpenGLPainterDeviceAdapter()
{
  this->PointSize = 1.0;
  this->RangeNear = 0.0;
  this->RangeFar = 1.0;
  this->MaxStencil = 0;
  this->Initialized = false;
}

//-----------------------------------------------------------------------------
vtkOpenGLPainterDeviceAdapter::~vtkOpenGLPainterDeviceAdapter()
{
}

//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::PrintSelf(ostream &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}

//-----------------------------------------------------------------------------

// This array is a map from VTK primitive identifiers (VTK_VERTEX, VTK_LINE,
// etc.) to OpenGL polygon primitive identifiers (GL_POINTS, GL_LINES, etc.)
// Note that some VTK polygon types (e.g. VTK_EMPTY_CELL and VTK_PIXEL) have no
// analogue in OpenGL.  Using them will undoubtedly result in an OpenGL error.
static const GLenum VTK2OpenGLPrimitive[] = {
  static_cast<GLenum>(0xFFFF),       // 0 - VTK_EMPTY_CELL
  GL_POINTS,            // 1 - VTK_VERTEX
  GL_POINTS,            // 2 - VTK_POLY_VERTEX
  GL_LINES,             // 3 - VTK_LINE
  GL_LINE_STRIP,        // 4 - VTK_POLY_LINE
  GL_TRIANGLES,         // 5 - VTK_TRIANGLE
  GL_TRIANGLE_STRIP,    // 6 - VTK_TRIANGLE_STRIP
  GL_POLYGON,           // 7 - VTK_POLYGON
  static_cast<GLenum>(0xFFFF),       // 8 - VTK_PIXEL
  GL_QUADS,             // 9 - VTK_QUAD
  GL_LINE_LOOP          // 10 - VTK_TETRA
};

static inline GLenum VTK2OpenGLType(int type)
{
  switch (type)
    {
#if VTK_SIZEOF_CHAR == 1
    case VTK_CHAR:              return GL_BYTE;
    case VTK_UNSIGNED_CHAR:     return GL_UNSIGNED_BYTE;
#elif VTK_SIZE_OF_CHAR == 2
    case VTK_CHAR:              return GL_SHORT;
    case VTK_UNSIGNED CHAR:     return GL_UNSIGNED_SHORT;
#endif

#if VTK_SIZEOF_SHORT == 1
    case VTK_SHORT:             return GL_BYTE;
    case VTK_UNSIGNED_SHORT:    return GL_UNSIGNED_BYTE;
#elif VTK_SIZEOF_SHORT == 2
    case VTK_SHORT:             return GL_SHORT;
    case VTK_UNSIGNED_SHORT:    return GL_UNSIGNED_SHORT;
#elif VTK_SIZEOF_SHORT == 4
    case VTK_SHORT:             return GL_INT;
    case VTK_UNSIGNED_SHORT:    return GL_UNSIGNED_INT;
#endif

#if VTK_SIZEOF_INT == 2
    case VTK_INT:               return GL_SHORT;
    case VTK_UNSIGNED_INT:      return GL_UNSIGNED_SHORT;
#elif VTK_SIZEOF_INT == 4
    case VTK_INT:               return GL_INT;
    case VTK_UNSIGNED_INT:      return GL_UNSIGNED_INT;
#endif

#if VTK_SIZEOF_LONG == 4
    case VTK_LONG:              return GL_INT;
    case VTK_UNSIGNED_LONG:     return GL_UNSIGNED_INT;
#endif

#if VTK_SIZEOF_ID_TYPE == 4
    case VTK_ID_TYPE:           return GL_INT;
#endif

#if VTK_SIZEOF_FLOAT == 4
    case VTK_FLOAT:             return GL_FLOAT;
#elif VTK_SIZEOF_FLOAT == 8
    case VTK_FLOAT:             return GL_DOUBLE;
#endif

#if VTK_SIZEOF_DOUBLE == 4
    case VTK_DOUBLE:            return GL_FLOAT;
#elif VTK_SIZEOF_DOUBLE == 8
    case VTK_DOUBLE:            return GL_DOUBLE;
#endif

    default:                    return GL_FALSE;
    }
}

static inline GLenum VTK2SignedOpenGLType(int type)
{
  switch (type)
    {
#if VTK_SIZEOF_CHAR == 1
    case VTK_CHAR:              return GL_BYTE;
    case VTK_UNSIGNED_CHAR:     return GL_BYTE;
#elif VTK_SIZE_OF_CHAR == 2
    case VTK_CHAR:              return GL_SHORT;
    case VTK_UNSIGNED CHAR:     return GL_SHORT;
#endif

#if VTK_SIZEOF_SHORT == 1
    case VTK_SHORT:             return GL_BYTE;
    case VTK_UNSIGNED_SHORT:    return GL_BYTE;
#elif VTK_SIZEOF_SHORT == 2
    case VTK_SHORT:             return GL_SHORT;
    case VTK_UNSIGNED_SHORT:    return GL_SHORT;
#elif VTK_SIZEOF_SHORT == 4
    case VTK_SHORT:             return GL_INT;
    case VTK_UNSIGNED_SHORT:    return GL_INT;
#endif

#if VTK_SIZEOF_INT == 2
    case VTK_INT:               return GL_SHORT;
    case VTK_UNSIGNED_INT:      return GL_SHORT;
#elif VTK_SIZEOF_INT == 4
    case VTK_INT:               return GL_INT;
    case VTK_UNSIGNED_INT:      return GL_INT;
#endif

#if VTK_SIZEOF_ID_TYPE == 4
    case VTK_ID_TYPE:           return GL_INT;
#endif

#if VTK_SIZEOF_LONG == 4
    case VTK_LONG:              return GL_INT;
    case VTK_UNSIGNED_LONG:     return GL_INT;
#endif

#if VTK_SIZEOF_FLOAT == 4
    case VTK_FLOAT:             return GL_FLOAT;
#elif VTK_SIZEOF_FLOAT == 8
    case VTK_FLOAT:             return GL_DOUBLE;
#endif

#if VTK_SIZEOF_DOUBLE == 4
    case VTK_DOUBLE:            return GL_FLOAT;
#elif VTK_SIZEOF_DOUBLE == 8
    case VTK_DOUBLE:            return GL_DOUBLE;
#endif

    default:                    return GL_FALSE;
    }
}

static inline GLenum VTK2UnsignedOpenGLType(int type)
{
  switch (type)
    {
#if VTK_SIZEOF_CHAR == 1
    case VTK_CHAR:              return GL_UNSIGNED_BYTE;
    case VTK_UNSIGNED_CHAR:     return GL_UNSIGNED_BYTE;
#elif VTK_SIZE_OF_CHAR == 2
    case VTK_CHAR:              return GL_UNSIGNED_SHORT;
    case VTK_UNSIGNED CHAR:     return GL_UNSIGNED_SHORT;
#endif

#if VTK_SIZEOF_SHORT == 1
    case VTK_SHORT:             return GL_UNSIGNED_BYTE;
    case VTK_UNSIGNED_SHORT:    return GL_UNSIGNED_BYTE;
#elif VTK_SIZEOF_SHORT == 2
    case VTK_SHORT:             return GL_UNSIGNED_SHORT;
    case VTK_UNSIGNED_SHORT:    return GL_UNSIGNED_SHORT;
#elif VTK_SIZEOF_SHORT == 4
    case VTK_SHORT:             return GL_UNSIGNED_INT;
    case VTK_UNSIGNED_SHORT:    return GL_UNSIGNED_INT;
#endif

#if VTK_SIZEOF_INT == 2
    case VTK_INT:               return GL_UNSIGNED_SHORT;
    case VTK_UNSIGNED_INT:      return GL_UNSIGNED_SHORT;
#elif VTK_SIZEOF_INT == 4
    case VTK_INT:               return GL_UNSIGNED_INT;
    case VTK_UNSIGNED_INT:      return GL_UNSIGNED_INT;
#endif

#if VTK_SIZEOF_ID_TYPE == 4
    case VTK_ID_TYPE:           return GL_UNSIGNED_INT;
#endif

#if VTK_SIZEOF_LONG == 4
    case VTK_LONG:              return GL_UNSIGNED_INT;
    case VTK_UNSIGNED_LONG:     return GL_UNSIGNED_INT;
#endif

    default:                    return GL_FALSE;
    }
}


//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::BeginPrimitive(int mode)
{
  glBegin(VTK2OpenGLPrimitive[mode]);
}

//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::EndPrimitive()
{
  glEnd();
}

//-----------------------------------------------------------------------------
int vtkOpenGLPainterDeviceAdapter::IsAttributesSupported(int attribute)
{
  switch(attribute)
    {
  case vtkDataSetAttributes::NUM_ATTRIBUTES:
  case vtkDataSetAttributes::NORMALS:
  case vtkDataSetAttributes::SCALARS:
  case vtkDataSetAttributes::TCOORDS:
  case vtkDataSetAttributes::EDGEFLAG:
    return 1;
    }
  return 0;
}

//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::SendAttribute(int index, int numcomp,
  int type, const void *attribute, vtkIdType offset/*=0*/)
{
  switch (index)
    {
  case vtkDataSetAttributes::NUM_ATTRIBUTES:     // Vertex
      if ((numcomp < 2) || (numcomp > 4))
        {
        vtkErrorMacro("Bad number of components.");
        return;
        }
      switch (VTK2SignedOpenGLType(type))
        {
        case GL_SHORT:
          switch (numcomp)
            {
            case 2:
              glVertex2sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            case 3:
              glVertex3sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            case 4:
              glVertex4sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            }
          break;
        case GL_INT:
          switch (numcomp)
            {
            case 2:
              glVertex2iv(static_cast<const GLint *>(attribute) + offset);
              break;
            case 3:
              glVertex3iv(static_cast<const GLint *>(attribute) + offset);
              break;
            case 4:
              glVertex4iv(static_cast<const GLint *>(attribute) + offset);
              break;
            }
          break;
        case GL_FLOAT:
          switch (numcomp)
            {
            case 2:
              glVertex2fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            case 3:
              glVertex3fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            case 4:
              glVertex4fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            }
          break;
        case GL_DOUBLE:
          switch (numcomp)
            {
            case 2:
              glVertex2dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            case 3:
              glVertex3dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            case 4:
              glVertex4dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            }
          break;
        default:
          vtkErrorMacro("Unsupported type for vertices: " << type);
          return;
        }
      break;
  case vtkDataSetAttributes::NORMALS:     // Normal
      if (numcomp != 3)
        {
        vtkErrorMacro("Bad number of components.");
        return;
        }
      switch (VTK2SignedOpenGLType(type))
        {
        case GL_BYTE:
          glNormal3bv(static_cast<const GLbyte *>(attribute) + offset);
          break;
        case GL_SHORT:
          glNormal3sv(static_cast<const GLshort *>(attribute) + offset);
          break;
        case GL_INT:
          glNormal3iv(static_cast<const GLint *>(attribute) + offset);
          break;
        case GL_FLOAT:
          glNormal3fv(static_cast<const GLfloat *>(attribute) + offset);
          break;
        case GL_DOUBLE:
          glNormal3dv(static_cast<const GLdouble *>(attribute) + offset);
          break;
        default:
          vtkErrorMacro("Unsupported type for normals: " << type);
          return;
        }
      break;
  case vtkDataSetAttributes::SCALARS:     // Color
      if ((numcomp != 3) && (numcomp != 4))
        {
        vtkErrorMacro("Bad number of components.");
        return;
        }
      switch (VTK2OpenGLType(type))
        {
        case GL_BYTE:
          switch (numcomp)
            {
            case 3: glColor3bv(static_cast<const GLbyte *>(attribute) + offset);
              break;
            case 4: glColor4bv(static_cast<const GLbyte *>(attribute) + offset);
              break;
            }
          break;
        case GL_UNSIGNED_BYTE:
          switch (numcomp)
            {
            case 3: glColor3ubv(static_cast<const GLubyte *>(attribute) + offset);
              break;
            case 4: glColor4ubv(static_cast<const GLubyte *>(attribute) + offset);
              break;
            }
          break;
        case GL_SHORT:
          switch (numcomp)
            {
            case 3: glColor3sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            case 4: glColor4sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            }
          break;
        case GL_UNSIGNED_SHORT:
          switch (numcomp)
            {
            case 3: glColor3usv(static_cast<const GLushort *>(attribute) + offset);
              break;
            case 4: glColor4usv(static_cast<const GLushort *>(attribute) + offset);
              break;
            }
          break;
        case GL_INT:
          switch (numcomp)
            {
            case 3: glColor3iv(static_cast<const GLint *>(attribute) + offset);
              break;
            case 4: glColor4iv(static_cast<const GLint *>(attribute) + offset);
              break;
            }
          break;
        case GL_UNSIGNED_INT:
          switch (numcomp)
            {
            case 3: glColor3uiv(static_cast<const GLuint *>(attribute) + offset);
              break;
            case 4: glColor4uiv(static_cast<const GLuint *>(attribute) + offset);
              break;
            }
          break;
        case GL_FLOAT:
          switch (numcomp)
            {
            case 3: glColor3fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            case 4: glColor4fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            }
          break;
        case GL_DOUBLE:
          switch (numcomp)
            {
            case 3: glColor3dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            case 4: glColor4dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            }
          break;
        default:
          vtkErrorMacro("Unsupported type for colors: " << type);
          return;
        }
      break;

  case vtkDataSetAttributes::TCOORDS:     // Texture Coordinate
      if ((numcomp < 1) || (numcomp > 4))
        {
        vtkErrorMacro("Bad number of components.");
        return;
        }
      switch (VTK2SignedOpenGLType(type))
        {
        case GL_SHORT:
          switch (numcomp)
            {
            case 1:
              glTexCoord1sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            case 2:
              glTexCoord2sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            case 3:
              glTexCoord3sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            case 4:
              glTexCoord4sv(static_cast<const GLshort *>(attribute) + offset);
              break;
            }
          break;
        case GL_INT:
          switch (numcomp)
            {
            case 1:
              glTexCoord1iv(static_cast<const GLint *>(attribute) + offset);
              break;
            case 2:
              glTexCoord2iv(static_cast<const GLint *>(attribute) + offset);
              break;
            case 3:
              glTexCoord3iv(static_cast<const GLint *>(attribute) + offset);
              break;
            case 4:
              glTexCoord4iv(static_cast<const GLint *>(attribute) + offset);
              break;
            }
          break;
        case GL_FLOAT:
          switch (numcomp)
            {
            case 1:
              glTexCoord1fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            case 2:
              glTexCoord2fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            case 3:
              glTexCoord3fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            case 4:
              glTexCoord4fv(static_cast<const GLfloat *>(attribute) + offset);
              break;
            }
          break;
        case GL_DOUBLE:
          switch (numcomp)
            {
            case 1:
              glTexCoord1dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            case 2:
              glTexCoord2dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            case 3:
              glTexCoord3dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            case 4:
              glTexCoord4dv(static_cast<const GLdouble *>(attribute) + offset);
              break;
            }
          break;
        default:
          vtkErrorMacro("Unsupported type for texture coordinates: " << type);
          return;
        }
      break;
    case vtkDataSetAttributes::EDGEFLAG:        // Edge Flag
      if (numcomp != 1)
        {
        vtkErrorMacro("Bad number of components.");
        return;
        }
       switch (type)
        {
        vtkTemplateMacro(glEdgeFlag(static_cast<GLboolean>(
                          reinterpret_cast<const VTK_TT*>(attribute)[offset])));
        }
      break;
    default:
      vtkErrorMacro("Unsupported attribute index: " << index);
      return;
    };
}

//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::SendMultiTextureCoords(int numcomp,
  int type, const void *attribute, int idx, vtkIdType offset)
{
  if(! vtkgl::MultiTexCoord2d)
    {
    vtkErrorMacro("MultiTexturing not supported.");
    return;
    }

  if ((numcomp < 1) || (numcomp > 4))
    {
    vtkErrorMacro("Bad number of components.");
    return;
    }

  int textureIndex = vtkgl::TEXTURE0 + idx;
  switch (VTK2SignedOpenGLType(type))
    {
    case GL_SHORT:
      switch (numcomp)
        {
        case 1:
          vtkgl::MultiTexCoord1sv(textureIndex, static_cast<const GLshort *>(attribute) + offset);
          break;
        case 2:
          vtkgl::MultiTexCoord2sv(textureIndex, static_cast<const GLshort *>(attribute) + offset);
          break;
        case 3:
          vtkgl::MultiTexCoord3sv(textureIndex, static_cast<const GLshort *>(attribute) + offset);
          break;
        case 4:
          vtkgl::MultiTexCoord4sv(textureIndex, static_cast<const GLshort *>(attribute) + offset);
          break;
        }
      break;
    case GL_INT:
      switch (numcomp)
        {
        case 1:
          vtkgl::MultiTexCoord1iv(textureIndex, static_cast<const GLint *>(attribute) + offset);
          break;
        case 2:
          vtkgl::MultiTexCoord2iv(textureIndex, static_cast<const GLint *>(attribute) + offset);
          break;
        case 3:
          vtkgl::MultiTexCoord3iv(textureIndex, static_cast<const GLint *>(attribute) + offset);
          break;
        case 4:
          vtkgl::MultiTexCoord4iv(textureIndex, static_cast<const GLint *>(attribute) + offset);
          break;
        }
      break;
    case GL_FLOAT:
      switch (numcomp)
        {
        case 1:
          vtkgl::MultiTexCoord1fv(textureIndex, static_cast<const GLfloat *>(attribute) + offset);
          break;
        case 2:
          vtkgl::MultiTexCoord2fv(textureIndex, static_cast<const GLfloat *>(attribute) + offset);
          break;
        case 3:
          vtkgl::MultiTexCoord3fv(textureIndex, static_cast<const GLfloat *>(attribute) + offset);
          break;
        case 4:
          vtkgl::MultiTexCoord4fv(textureIndex, static_cast<const GLfloat *>(attribute) + offset);
          break;
        }
      break;
    case GL_DOUBLE:
      switch (numcomp)
        {
        case 1:
          vtkgl::MultiTexCoord1dv(textureIndex, static_cast<const GLdouble *>(attribute) + offset);
          break;
        case 2:
          vtkgl::MultiTexCoord2dv(textureIndex, static_cast<const GLdouble *>(attribute) + offset);
          break;
        case 3:
          vtkgl::MultiTexCoord3dv(textureIndex, static_cast<const GLdouble *>(attribute) + offset);
          break;
        case 4:
          vtkgl::MultiTexCoord4dv(textureIndex, static_cast<const GLdouble *>(attribute) + offset);
          break;
        }
      break;
    default:
      vtkErrorMacro("Unsupported type for texture coordinates: " << type);
      return;
    }

}

//-----------------------------------------------------------------------------

void vtkOpenGLPainterDeviceAdapter::SetAttributePointer(int index,
              int numcomponents,
              int type, int stride,
              const void *pointer)
{
  GLenum gltype;

  switch (index)
    {
  case vtkDataSetAttributes::NUM_ATTRIBUTES:     // Vertex
      gltype = VTK2SignedOpenGLType(type);
      switch (gltype)
        {
        case GL_SHORT:
        case GL_INT:
        case GL_FLOAT:
        case GL_DOUBLE:
          break;
        default:
          vtkErrorMacro("Unsupported type for vertices: " << type);
          return;
        }
      glVertexPointer(numcomponents, gltype, stride, pointer);
      break;
  case vtkDataSetAttributes::NORMALS:     // Normal
      gltype = VTK2SignedOpenGLType(type);
      switch (gltype)
        {
        case GL_BYTE:
        case GL_SHORT:
        case GL_INT:
        case GL_FLOAT:
        case GL_DOUBLE:
          break;
        default:
          vtkErrorMacro("Unsupported type for normals: " << type);
          return;
        }
      if (numcomponents != 3)
        {
        vtkErrorMacro("Unsupported number of components for normals.");
        return;
        }
      glNormalPointer(gltype, stride, pointer);
      break;
  case vtkDataSetAttributes::SCALARS:     // Color
      gltype = VTK2OpenGLType(type);
      switch (gltype)
        {
        case GL_BYTE:
        case GL_UNSIGNED_BYTE:
        case GL_SHORT:
        case GL_UNSIGNED_SHORT:
        case GL_INT:
        case GL_UNSIGNED_INT:
        case GL_FLOAT:
        case GL_DOUBLE:
          break;
        default:
          vtkErrorMacro("Unsupported type for colors: " << type);
          return;
        }
      glColorPointer(numcomponents, gltype, stride, pointer);
      break;
  case vtkDataSetAttributes::TCOORDS:     // Texture Coordinate
      gltype = VTK2SignedOpenGLType(type);
      switch (gltype)
        {
        case GL_SHORT:
        case GL_INT:
        case GL_FLOAT:
        case GL_DOUBLE:
          break;
        default:
          vtkErrorMacro("Unsupported type for texture coordinates: " << type);
          return;
        }
      glTexCoordPointer(numcomponents, gltype, stride, pointer);
      break;
    case vtkDataSetAttributes::EDGEFLAG:        // Edge flag
      if (numcomponents != 1)
        {
        vtkErrorMacro("Edge flag must have one component.");
        return;
        }
      // Flag must be conformant to GLboolean
      if ((type == VTK_FLOAT) || (type == GL_DOUBLE))
        {
        vtkErrorMacro("Unsupported type for edge flag: " << type);
        return;
        }
      // Thus is an unfriendly way to force the array to be conformant to
      // a GLboolean array.  At the very least there should be some indication
      // in VTK outside of OpenGL to determine which VTK type to use.
      switch (type)
        {
        vtkTemplateMacro(if (sizeof(VTK_TT) != sizeof(GLboolean))
                           {
                           vtkErrorMacro(<< "Unsupported tyep for edge flag: "
                                         << type);
                           return;
                           }
                         );
        }
      glEdgeFlagPointer(stride, pointer);
      break;
    default:
      vtkErrorMacro("Unsupported attribute index: " << index);
      return;
    };
}

//-----------------------------------------------------------------------------

void vtkOpenGLPainterDeviceAdapter::EnableAttributeArray(int index)
{
  switch (index)
    {
  case vtkDataSetAttributes::NUM_ATTRIBUTES:
      glEnableClientState(GL_VERTEX_ARRAY);  break;
  case vtkDataSetAttributes::NORMALS:
      glEnableClientState(GL_NORMAL_ARRAY);  break;
  case vtkDataSetAttributes::SCALARS:
      glEnableClientState(GL_COLOR_ARRAY);  break;
  case vtkDataSetAttributes::TCOORDS:
      glEnableClientState(GL_TEXTURE_COORD_ARRAY);  break;
  case vtkDataSetAttributes::EDGEFLAG:
      glEnableClientState(GL_EDGE_FLAG_ARRAY);  break;
    default:
      vtkErrorMacro("Unsupported attribute index: " << index);
      return;
    };
}

void vtkOpenGLPainterDeviceAdapter::DisableAttributeArray(int index)
{
  switch (index)
    {
  case vtkDataSetAttributes::NUM_ATTRIBUTES:
      glDisableClientState(GL_VERTEX_ARRAY);  break;
  case vtkDataSetAttributes::NORMALS:
      glDisableClientState(GL_NORMAL_ARRAY);  break;
  case vtkDataSetAttributes::SCALARS:
      glDisableClientState(GL_COLOR_ARRAY);  break;
  case vtkDataSetAttributes::TCOORDS:
      glDisableClientState(GL_TEXTURE_COORD_ARRAY);  break;
  case vtkDataSetAttributes::EDGEFLAG:
      glDisableClientState(GL_EDGE_FLAG_ARRAY);  break;
    default:
      vtkErrorMacro("Unsupported attribute index: " << index);
      return;
    };
}

//-----------------------------------------------------------------------------

void vtkOpenGLPainterDeviceAdapter::DrawArrays(int mode, vtkIdType first,
                                               vtkIdType count)
{
  glDrawArrays(VTK2OpenGLPrimitive[mode],static_cast<GLint>(first),
               static_cast<GLsizei>(count));
}

void vtkOpenGLPainterDeviceAdapter::DrawElements(int mode, vtkIdType count,
                                                 int type, void *indices)
{
  GLenum gltype = VTK2UnsignedOpenGLType(type);
  switch (gltype)
    {
    case GL_UNSIGNED_BYTE:
    case GL_UNSIGNED_SHORT:
    case GL_UNSIGNED_INT:
      break;
    default:
      if (type == VTK_ID_TYPE)
        {
        // This seems really inefficient for handling vtkIdType when they
        // are 64 bit, but OpenGL does not handle 64 bit indices.  What
        // else can I do?
        vtkIdType *oldarray = static_cast<vtkIdType *>(indices);
        GLuint *newarray = new GLuint[count];
        std::copy(oldarray, oldarray + count, newarray);
        glDrawElements(VTK2OpenGLPrimitive[mode], static_cast<GLsizei>(count),
                       GL_UNSIGNED_INT, newarray);
        delete[] newarray;
        return;
        }
      else
        {
        vtkErrorMacro("Invalid type for indices.");
        return;
        }
    }
  glDrawElements(VTK2OpenGLPrimitive[mode],static_cast<GLsizei>(count), gltype,
                 indices);
}

//-----------------------------------------------------------------------------
int vtkOpenGLPainterDeviceAdapter::Compatible(vtkRenderer *renderer)
{
  vtkOpenGLRenderWindow *context
    = vtkOpenGLRenderWindow::SafeDownCast(renderer->GetRenderWindow());
  if (!context)
    {
    return false;
    }
  return true;
}

//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::MakeVertexEmphasis(bool mode)
{
  if (mode)
    {
    float s;
    glGetFloatv(GL_POINT_SIZE, &s);
    this->PointSize = s;
    glPointSize(4.0); //make verts large enough to be sure to overlap cell
    vtkOpenGLGL2PSHelper::SetPointSize(4.0);

    float nf[2];   //put verts just in front of associated cells
    glGetFloatv(GL_DEPTH_RANGE, nf);
    this->RangeNear = nf[0];
    this->RangeFar = nf[1];
    glDepthRange(0.0, nf[1]*0.999999);
    glDepthMask(GL_FALSE); //prevent verts from interfering with each other
    }
  else
    {
    glPointSize(static_cast<GLfloat>(this->PointSize));
    vtkOpenGLGL2PSHelper::SetPointSize(static_cast<GLfloat>(this->PointSize));
    glDepthRange(this->RangeNear, this->RangeFar);
    glDepthMask(GL_TRUE);
    }
  vtkOpenGLCheckErrorMacro("failed after MakeVertexEmphasis");
}

//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::WriteStencil(vtkIdType value)
{
  if (this->MaxStencil)
    {
    value = value % this->MaxStencil + 1;
    if (value == 1)
      {
      glClearStencil(0); //start over so don't write into some previous area
      }
    glStencilFunc(GL_ALWAYS, static_cast<GLint>(value), this->MaxStencil);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
    vtkOpenGLCheckErrorMacro("failed after WriteStencil");
    }
}

//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::TestStencil(vtkIdType value)
{
  if (this->MaxStencil)
    {
    value = value % this->MaxStencil + 1;
    glStencilFunc(GL_EQUAL, static_cast<GLint>(value), this->MaxStencil);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    vtkOpenGLCheckErrorMacro("failed after TestStencil");
    }
}

//-----------------------------------------------------------------------------
void vtkOpenGLPainterDeviceAdapter::Stencil(int on)
{
  if (on)
    {
    glEnable(GL_STENCIL_TEST);
    GLint stencilbits;
    glGetIntegerv(GL_STENCIL_BITS, &stencilbits);
    this->MaxStencil = (1<<stencilbits)-1;
    }
  else
    {
    glDisable(GL_STENCIL_TEST);
    }
  vtkOpenGLCheckErrorMacro("failed after Stencil");
}


