
#include "gl_util.h"

int gl_texparameter_count(GLenum pname)
{

#ifdef GL_EXT_texture_object
	switch (pname) {
	case GL_TEXTURE_PRIORITY_EXT:
	case GL_TEXTURE_RESIDENT_EXT:
		return 1;
	}
#endif

/* FIXME: Missing stuff? */

#ifdef GL_VERSION_1_2
	switch (pname) {
	case GL_TEXTURE_WRAP_R:
	case GL_TEXTURE_DEPTH:
		return 1;
	
	case GL_TEXTURE_MIN_LOD:
	case GL_TEXTURE_MAX_LOD:
	case GL_TEXTURE_BASE_LEVEL:
	case GL_TEXTURE_MAX_LEVEL:
		return 1;
	}
#endif

	switch (pname) {

	case GL_TEXTURE_MAG_FILTER:
	case GL_TEXTURE_MIN_FILTER:
	case GL_TEXTURE_WRAP_S:
	case GL_TEXTURE_WRAP_T:
	case GL_TEXTURE_PRIORITY:
	case GL_TEXTURE_RESIDENT:
		return 1;
	case GL_TEXTURE_BORDER_COLOR:
		return 4;
	default:
		croak("Unknown texparameter parameter");
	}
	return 0;	// Just to make the compiler happy
}


int gl_texenv_count(GLenum pname)
{
	switch (pname) {
	case GL_TEXTURE_ENV_MODE:
		return 1;
	case GL_TEXTURE_ENV_COLOR:
		return 4;
	default:
		croak("Unknown texenv parameter");
	}
	return 0;	// Just to make the compiler happy
}

int gl_texgen_count(GLenum pname)
{
	switch (pname) {
	case GL_TEXTURE_GEN_MODE:
		return 1;
	case GL_OBJECT_PLANE:
	case GL_EYE_PLANE:
		return 4;
	default:
		croak("Unknown texgen parameter");
	}
	return 0;	// Just to make the compiler happy
}

int gl_material_count(GLenum pname)
{
	switch (pname) {
	case GL_AMBIENT:
	case GL_DIFFUSE:
	case GL_AMBIENT_AND_DIFFUSE:
	case GL_SPECULAR:
	case GL_EMISSION:
		return 4;
	case GL_COLOR_INDEXES:
		return 3;
	case GL_SHININESS:
		return 1;
	default:
		croak("Unknown material parameter");
	}
	return 0;	// Just to make the compiler happy
}


int gl_map_count(GLenum target, GLenum query)
{
	switch (query) {
	case GL_COEFF:
		switch (target) {
		case GL_MAP1_VERTEX_3:
		case GL_MAP1_NORMAL:
		case GL_MAP1_TEXTURE_COORD_3:
			return 3;
		case GL_MAP1_VERTEX_4:
		case GL_MAP1_COLOR_4:
		case GL_MAP1_TEXTURE_COORD_4:
			return 4;
		case GL_MAP1_TEXTURE_COORD_2:
			return 2;
		case GL_MAP1_INDEX:
		case GL_MAP1_TEXTURE_COORD_1:
			return 1;
		case GL_MAP2_VERTEX_3:
		case GL_MAP2_NORMAL:
		case GL_MAP2_TEXTURE_COORD_3:
			return 3;
		case GL_MAP2_VERTEX_4:
		case GL_MAP2_COLOR_4:
		case GL_MAP2_TEXTURE_COORD_4:
			return 4;
		case GL_MAP2_TEXTURE_COORD_2:
			return 2;
		case GL_MAP2_INDEX:
		case GL_MAP2_TEXTURE_COORD_1:
			return 1;
		default:
			croak("Unknown map target");
		}
	case GL_ORDER:
		switch (target) {
		case GL_MAP1_VERTEX_3:
		case GL_MAP1_NORMAL:
		case GL_MAP1_TEXTURE_COORD_3:
		case GL_MAP1_VERTEX_4:
		case GL_MAP1_COLOR_4:
		case GL_MAP1_TEXTURE_COORD_4:
		case GL_MAP1_TEXTURE_COORD_2:
		case GL_MAP1_INDEX:
		case GL_MAP1_TEXTURE_COORD_1:
			return 1;
		case GL_MAP2_VERTEX_3:
		case GL_MAP2_NORMAL:
		case GL_MAP2_TEXTURE_COORD_3:
		case GL_MAP2_VERTEX_4:
		case GL_MAP2_COLOR_4:
		case GL_MAP2_TEXTURE_COORD_4:
		case GL_MAP2_TEXTURE_COORD_2:
		case GL_MAP2_INDEX:
		case GL_MAP2_TEXTURE_COORD_1:
			return 2;
		default:
			croak("Unknown map target");
		}
	case GL_DOMAIN:
		switch (target) {
		case GL_MAP1_VERTEX_3:
		case GL_MAP1_NORMAL:
		case GL_MAP1_TEXTURE_COORD_3:
		case GL_MAP1_VERTEX_4:
		case GL_MAP1_COLOR_4:
		case GL_MAP1_TEXTURE_COORD_4:
		case GL_MAP1_TEXTURE_COORD_2:
		case GL_MAP1_INDEX:
		case GL_MAP1_TEXTURE_COORD_1:
			return 2;
		case GL_MAP2_VERTEX_3:
		case GL_MAP2_NORMAL:
		case GL_MAP2_TEXTURE_COORD_3:
		case GL_MAP2_VERTEX_4:
		case GL_MAP2_COLOR_4:
		case GL_MAP2_TEXTURE_COORD_4:
		case GL_MAP2_TEXTURE_COORD_2:
		case GL_MAP2_INDEX:
		case GL_MAP2_TEXTURE_COORD_1:
			return 4;
		default:
			croak("Unknown map target");
		}
	default:
		croak("Unknown map query");
	}
	return 0;	// Just to make the compiler happy
}

int gl_light_count(GLenum pname)
{
	switch (pname) {
	case GL_AMBIENT:
	case GL_DIFFUSE:
	case GL_SPECULAR:
	case GL_POSITION:
		return 4;
	case GL_SPOT_DIRECTION:
		return 3;
	case GL_SPOT_EXPONENT:
	case GL_SPOT_CUTOFF:
	case GL_CONSTANT_ATTENUATION:
	case GL_LINEAR_ATTENUATION:
	case GL_QUADRATIC_ATTENUATION:
		return 1;
	default:
		croak("Unknown light parameter");
	}
	return 0;	// Just to make the compiler happy
}

int gl_lightmodel_count(GLenum pname)
{
	switch (pname) {
	case GL_LIGHT_MODEL_AMBIENT:
		return 4;
	case GL_LIGHT_MODEL_LOCAL_VIEWER:
	case GL_LIGHT_MODEL_TWO_SIDE:
		return 1;
	default:
		croak("Unknown light model");
	}
	return 0;	// Just to make the compiler happy
}

int gl_fog_count(GLenum pname)
{
	switch (pname) {
	case GL_FOG_COLOR:
		return 4;
	case GL_FOG_MODE:
	case GL_FOG_DENSITY:
	case GL_FOG_START:
	case GL_FOG_END:
	case GL_FOG_INDEX:
		return 1;
	default:
		croak("Unknown fog parameter");
	}
	return 0;	// Just to make the compiler happy
}

int gl_get_count(GLenum param)
{

/* 3 */
#ifdef GL_EXT_polygon_offset
	switch (param) {
	case GL_POLYGON_OFFSET_EXT:
	case GL_POLYGON_OFFSET_FACTOR_EXT:
	case GL_POLYGON_OFFSET_BIAS_EXT:
		return 1;
	}
#endif

#ifdef GL_VERSION_1_2

	switch (param) {

	case GL_TEXTURE_3D:
	case GL_UNPACK_SKIP_IMAGES:
	case GL_UNPACK_IMAGE_HEIGHT:
	case GL_PACK_SKIP_IMAGES:
	case GL_PACK_IMAGE_HEIGHT:
	case GL_MAX_3D_TEXTURE_SIZE:
		return 1;

	case GL_RESCALE_NORMAL:
		return 1;
	
	case GL_LIGHT_MODEL_COLOR_CONTROL:
		return 1;

	case GL_MAX_ELEMENTS_VERTICES:
	case GL_MAX_ELEMENTS_INDICES:
		return 1;
	}
#endif

#ifdef GL_VERSION_1_1
	switch (param) {
	case GL_TEXTURE_COORD_ARRAY:
	case GL_TEXTURE_COORD_ARRAY_SIZE:
	case GL_TEXTURE_COORD_ARRAY_STRIDE:
	case GL_TEXTURE_COORD_ARRAY_TYPE:
	case GL_VERTEX_ARRAY:
	case GL_VERTEX_ARRAY_SIZE:
	case GL_VERTEX_ARRAY_STRIDE:
	case GL_VERTEX_ARRAY_TYPE:
	case GL_POLYGON_OFFSET_FACTOR:
	case GL_POLYGON_OFFSET_UNITS:
	case GL_POLYGON_OFFSET_FILL:
	case GL_POLYGON_OFFSET_LINE:
	case GL_POLYGON_OFFSET_POINT:
	case GL_NORMAL_ARRAY:
	case GL_NORMAL_ARRAY_STRIDE:
	case GL_NORMAL_ARRAY_TYPE:
	case GL_INDEX_LOGIC_OP:
	case GL_INDEX_ARRAY:
	case GL_INDEX_ARRAY_STRIDE:
	case GL_INDEX_ARRAY_TYPE:
	case GL_EDGE_FLAG_ARRAY:
	case GL_EDGE_FLAG_ARRAY_STRIDE:
	case GL_COLOR_ARRAY:
	case GL_COLOR_ARRAY_SIZE:
	case GL_COLOR_ARRAY_STRIDE:
	case GL_COLOR_ARRAY_TYPE:
	case GL_COLOR_LOGIC_OP:
		return 1;
	}
#endif

/* 18 */
#ifdef GL_EXT_cmyka
	switch (param) {
	case GL_PACK_CMYK_HINT_EXT:
	case GL_UNPACK_CMYK_HINT_EXT:
		return 1;
	}
#endif

/* 30 */
#ifdef GL_EXT_vertex_array
	switch (param) {
	case GL_VERTEX_ARRAY_EXT:
	case GL_VERTEX_ARRAY_SIZE_EXT:
	case GL_VERTEX_ARRAY_STRIDE_EXT:
	case GL_VERTEX_ARRAY_TYPE_EXT:
	case GL_VERTEX_ARRAY_COUNT_EXT:
	case GL_NORMAL_ARRAY_EXT:
	case GL_NORMAL_ARRAY_STRIDE_EXT:
	case GL_NORMAL_ARRAY_TYPE_EXT:
	case GL_NORMAL_ARRAY_COUNT_EXT:
	case GL_COLOR_ARRAY_EXT:
	case GL_COLOR_ARRAY_SIZE_EXT:
	case GL_COLOR_ARRAY_STRIDE_EXT:
	case GL_COLOR_ARRAY_TYPE_EXT:
	case GL_COLOR_ARRAY_COUNT_EXT:
	case GL_INDEX_ARRAY_EXT:
	case GL_INDEX_ARRAY_STRIDE_EXT:
	case GL_INDEX_ARRAY_TYPE_EXT:
	case GL_INDEX_ARRAY_COUNT_EXT:
	case GL_TEXTURE_COORD_ARRAY_EXT:
	case GL_TEXTURE_COORD_ARRAY_SIZE_EXT:
	case GL_TEXTURE_COORD_ARRAY_STRIDE_EXT:
	case GL_TEXTURE_COORD_ARRAY_TYPE_EXT:
	case GL_TEXTURE_COORD_ARRAY_COUNT_EXT:
	case GL_EDGE_FLAG_ARRAY_EXT:
	case GL_EDGE_FLAG_ARRAY_STRIDE_EXT:
	case GL_EDGE_FLAG_ARRAY_COUNT_EXT:
		return 1;
	}
#endif

#ifdef GL_EXT_framebuffer_object
	switch (param) {
	case GL_FRAMEBUFFER_BINDING_EXT:
        case GL_RENDERBUFFER_BINDING_EXT:
        case GL_MAX_COLOR_ATTACHMENTS_EXT:
        case GL_MAX_RENDERBUFFER_SIZE_EXT:
		return 1;
	}
#endif

#ifdef GL_ARB_point_sprite
	switch (param) {
	case GL_POINT_SPRITE_ARB:
	case GL_COORD_REPLACE_ARB:
		return 1;
	}
#endif

#ifdef GL_ARB_point_parameters
	switch (param) {
	case GL_POINT_SIZE_MIN_ARB:
	case GL_POINT_SIZE_MAX_ARB:
	case GL_POINT_FADE_THRESHOLD_SIZE_ARB:
		return 1;
	case GL_POINT_DISTANCE_ATTENUATION_ARB:
		return 3;
	}
#endif

/* 79 */
#if defined(GL_EXT_clip_volume_hint) && defined(GL_VOLUME_CLIPPING_HINT_EXT)
	switch (param) {
	case GL_VOLUME_CLIPPING_HINT_EXT:
		return 1;
	}
#endif

	switch (param) {
	case GL_ACCUM_ALPHA_BITS:
	case GL_ACCUM_RED_BITS:
	case GL_ACCUM_BLUE_BITS:
	case GL_ACCUM_GREEN_BITS:
	case GL_ALPHA_BIAS:
	case GL_ALPHA_BITS:
	case GL_ALPHA_SCALE:
	case GL_ALPHA_TEST:
	case GL_ALPHA_TEST_FUNC:
	case GL_ALPHA_TEST_REF:
	case GL_ATTRIB_STACK_DEPTH:
	case GL_AUTO_NORMAL:
	case GL_AUX_BUFFERS:
	case GL_BLEND:
	case GL_BLEND_DST:
	case GL_BLEND_EQUATION_EXT:
	case GL_BLEND_SRC:
	case GL_BLUE_BIAS:
	case GL_BLUE_BITS:
	case GL_BLUE_SCALE:
	case GL_CLIENT_ATTRIB_STACK_DEPTH:
	case GL_COLOR_MATERIAL:
	case GL_COLOR_MATERIAL_FACE:
	case GL_COLOR_MATERIAL_PARAMETER:
	case GL_CULL_FACE:
	case GL_CULL_FACE_MODE:
	case GL_CURRENT_INDEX:
	case GL_CURRENT_RASTER_DISTANCE:
	case GL_CURRENT_RASTER_INDEX:
	case GL_CURRENT_RASTER_POSITION_VALID:
	case GL_DEPTH_BIAS:
	case GL_DEPTH_BITS:
	case GL_DEPTH_CLEAR_VALUE:
	case GL_DEPTH_FUNC:
	case GL_DEPTH_SCALE:
	case GL_DEPTH_TEST:
	case GL_DEPTH_WRITEMASK:
	case GL_DITHER:
	case GL_DOUBLEBUFFER:
	case GL_DRAW_BUFFER:
	case GL_EDGE_FLAG:
	case GL_FOG_DENSITY:
	case GL_FOG_END:
	case GL_FOG_HINT:
	case GL_FOG_INDEX:
	case GL_FOG_MODE:
	case GL_FOG_START:
	case GL_FRONT_FACE:
	case GL_GREEN_BIAS:
	case GL_GREEN_BITS:
	case GL_GREEN_SCALE:
	case GL_INDEX_BITS:
	case GL_INDEX_CLEAR_VALUE:
	case GL_INDEX_MODE:
	case GL_INDEX_OFFSET:
	case GL_INDEX_SHIFT:
	case GL_INDEX_WRITEMASK:
	case GL_LIGHTING:
	case GL_LIGHT_MODEL_LOCAL_VIEWER:
	case GL_LIGHT_MODEL_TWO_SIDE:
	case GL_LINE_SMOOTH:
	case GL_LINE_SMOOTH_HINT:
	case GL_LINE_STIPPLE:
	case GL_LINE_STIPPLE_PATTERN:
	case GL_LINE_STIPPLE_REPEAT:
	case GL_LINE_WIDTH:
	case GL_LINE_WIDTH_GRANULARITY:
	case GL_LIST_BASE:
	case GL_LIST_INDEX:
	case GL_LIST_MODE:
	case GL_LOGIC_OP_MODE:
	case GL_MAP1_COLOR_4:
	case GL_MAP1_GRID_SEGMENTS:
	case GL_MAP1_INDEX:
	case GL_MAP1_NORMAL:
	case GL_MAP1_TEXTURE_COORD_1:
	case GL_MAP1_TEXTURE_COORD_2:
	case GL_MAP1_TEXTURE_COORD_3:
	case GL_MAP1_TEXTURE_COORD_4:
	case GL_MAP1_VERTEX_3:
	case GL_MAP1_VERTEX_4:
	case GL_MAP2_INDEX:
	case GL_MAP2_NORMAL:
	case GL_MAP2_TEXTURE_COORD_1:
	case GL_MAP2_TEXTURE_COORD_2:
	case GL_MAP2_TEXTURE_COORD_3:
	case GL_MAP2_TEXTURE_COORD_4:
	case GL_MAP2_VERTEX_3:
	case GL_MAP2_VERTEX_4:
	case GL_MAP_COLOR:
	case GL_MAP_STENCIL:
	case GL_MATRIX_MODE:
	case GL_MAX_CLIENT_ATTRIB_STACK_DEPTH:
	case GL_MAX_ATTRIB_STACK_DEPTH:
	case GL_MAX_CLIP_PLANES:
	case GL_MAX_EVAL_ORDER:
	case GL_MAX_LIGHTS:
	case GL_MAX_LIST_NESTING:
	case GL_MAX_MODELVIEW_STACK_DEPTH:
	case GL_MAX_NAME_STACK_DEPTH:
	case GL_MAX_PIXEL_MAP_TABLE:
	case GL_MAX_PROJECTION_STACK_DEPTH:
	case GL_MAX_TEXTURE_SIZE:
	case GL_MAX_TEXTURE_STACK_DEPTH:
	case GL_MODELVIEW_STACK_DEPTH:
	case GL_NAME_STACK_DEPTH:
	case GL_NORMALIZE:
	case GL_PACK_ALIGNMENT:
	case GL_PACK_LSB_FIRST:
	case GL_PACK_ROW_LENGTH:
	case GL_PACK_SKIP_PIXELS:
	case GL_PACK_SKIP_ROWS:
	case GL_PACK_SWAP_BYTES:
	case GL_PERSPECTIVE_CORRECTION_HINT:
	case GL_PIXEL_MAP_A_TO_A_SIZE:
	case GL_PIXEL_MAP_B_TO_B_SIZE:
	case GL_PIXEL_MAP_G_TO_G_SIZE:
	case GL_PIXEL_MAP_I_TO_A_SIZE:
	case GL_PIXEL_MAP_I_TO_B_SIZE:
	case GL_PIXEL_MAP_I_TO_G_SIZE:
	case GL_PIXEL_MAP_I_TO_I_SIZE:
	case GL_PIXEL_MAP_I_TO_R_SIZE:
	case GL_PIXEL_MAP_R_TO_R_SIZE:
	case GL_PIXEL_MAP_S_TO_S_SIZE:
	case GL_POINT_SIZE:
	case GL_POINT_SIZE_GRANULARITY:
	case GL_POINT_SIZE_RANGE:
	case GL_POINT_SMOOTH:
	case GL_POINT_SMOOTH_HINT:
	case GL_POLYGON_SMOOTH:
	case GL_POLYGON_SMOOTH_HINT:
	case GL_POLYGON_STIPPLE:
	case GL_PROJECTION_STACK_DEPTH:
	case GL_READ_BUFFER:
	case GL_RED_BIAS:
	case GL_RED_BITS:
	case GL_RED_SCALE:
	case GL_RENDER_MODE:
	case GL_RGBA_MODE:
	case GL_SCISSOR_TEST:
	case GL_SHADE_MODEL:
	case GL_STENCIL_BITS:
	case GL_STENCIL_CLEAR_VALUE:
	case GL_STENCIL_FAIL:
	case GL_STENCIL_FUNC:
	case GL_STENCIL_PASS_DEPTH_FAIL:
	case GL_STENCIL_PASS_DEPTH_PASS:
	case GL_STENCIL_REF:
	case GL_STENCIL_TEST:
	case GL_STENCIL_VALUE_MASK:
	case GL_STENCIL_WRITEMASK:
	case GL_STEREO:
	case GL_SUBPIXEL_BITS:
	case GL_TEXTURE_1D:
	case GL_TEXTURE_BINDING_1D:
	case GL_TEXTURE_2D:
	case GL_TEXTURE_BINDING_2D:
	case GL_TEXTURE_GEN_Q:
	case GL_TEXTURE_GEN_R:
	case GL_TEXTURE_GEN_S:
	case GL_TEXTURE_GEN_T:
	case GL_TEXTURE_STACK_DEPTH:
	case GL_UNPACK_ALIGNMENT:
	case GL_UNPACK_LSB_FIRST:
	case GL_UNPACK_ROW_LENGTH:
	case GL_UNPACK_SKIP_PIXELS:
	case GL_UNPACK_SKIP_ROWS:
	case GL_UNPACK_SWAP_BYTES:
	case GL_ZOOM_X:
	case GL_ZOOM_Y:
		return 1;
	case GL_DEPTH_RANGE:
	case GL_LINE_WIDTH_RANGE:
	case GL_MAP1_GRID_DOMAIN:
	case GL_MAP2_GRID_SEGMENTS:
	case GL_MAX_VIEWPORT_DIMS:
	case GL_POLYGON_MODE:
		return 2;
	case GL_CURRENT_NORMAL:
		return 3;
	case GL_ACCUM_CLEAR_VALUE:
	case GL_BLEND_COLOR_EXT:
	case GL_COLOR_CLEAR_VALUE:
	case GL_COLOR_WRITEMASK:
	case GL_CURRENT_COLOR:
	case GL_CURRENT_RASTER_COLOR:
	case GL_CURRENT_RASTER_POSITION:
	case GL_CURRENT_RASTER_TEXTURE_COORDS:
	case GL_CURRENT_TEXTURE_COORDS:
	case GL_FOG_COLOR:
	case GL_LIGHT_MODEL_AMBIENT:
	case GL_MAP2_GRID_DOMAIN:
	case GL_SCISSOR_BOX:
	case GL_VIEWPORT:
		return 4;
	case GL_MODELVIEW_MATRIX:
	case GL_PROJECTION_MATRIX:
	case GL_TEXTURE_MATRIX:
		return 16;
	default:
		{
			/* GL_LIGHTi = 1 */
			static GLint max_lights = 0;
			if (!max_lights)
				glGetIntegerv(GL_MAX_LIGHTS, &max_lights);
			if ((param > GL_LIGHT0) && (param <= (GLenum)(GL_LIGHT0 + max_lights)))
				return 1;
		}
		{
			/* GL_CLIP_PLANEi = 1 */
			static GLint max_clip_planes = 0;
			if (!max_clip_planes)
				glGetIntegerv(GL_MAX_CLIP_PLANES, &max_clip_planes);
			if ((param > GL_CLIP_PLANE0) && (param <= (GLenum)(GL_CLIP_PLANE0 + max_clip_planes)))
				return 1;
		}
		croak("Unknown param");
	}
	return 0;	// Just to make the compiler happy
}


int gl_pixelmap_size(GLenum map)
{
	GLint s;
	switch (map) {
		case GL_PIXEL_MAP_I_TO_I:
			glGetIntegerv(GL_PIXEL_MAP_I_TO_I_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_S_TO_S:
			glGetIntegerv(GL_PIXEL_MAP_S_TO_S_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_I_TO_R:
			glGetIntegerv(GL_PIXEL_MAP_I_TO_R_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_I_TO_G:
			glGetIntegerv(GL_PIXEL_MAP_I_TO_G_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_I_TO_B:
			glGetIntegerv(GL_PIXEL_MAP_I_TO_B_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_I_TO_A:
			glGetIntegerv(GL_PIXEL_MAP_I_TO_A_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_R_TO_R:
			glGetIntegerv(GL_PIXEL_MAP_R_TO_R_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_G_TO_G:
			glGetIntegerv(GL_PIXEL_MAP_G_TO_G_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_B_TO_B:
			glGetIntegerv(GL_PIXEL_MAP_B_TO_B_SIZE, &s);
			return s;
		case GL_PIXEL_MAP_A_TO_A:
			glGetIntegerv(GL_PIXEL_MAP_A_TO_A_SIZE, &s);
			return s;
		default:
			croak("unknown pixelmap");
	}
	return 0;	// Just to make the compiler happy
}

int gl_state_count(GLenum state) {
	switch (state) {
		case GL_CURRENT_COLOR: return 4;
		case GL_CURRENT_INDEX: return 1;
	}
	return 0;
}

unsigned long gl_pixelbuffer_size(
	GLenum format,
	GLsizei	width,
	GLsizei	height,
	GLenum	type,
	int mode);

GLvoid * EL(SV * sv, int needlen)
{
	STRLEN skip = 0;
    SV * svref;
	
	if (SvREADONLY(sv))
		croak("Readonly value for buffer");

	if(SvROK(sv)) {
        svref = SvRV(sv);
        sv = svref;
    }
    else
    {
#ifdef USE_STRICT_UNGLOB
        if (SvFAKE(sv) && SvTYPE(sv) == SVt_PVGV)
            sv_unglob(sv);
#endif

        SvUPGRADE(sv, SVt_PV);
        SvGROW(sv, (unsigned int)(needlen + 1));
        SvPOK_on(sv);
        SvCUR_set(sv, needlen);
        *SvEND(sv) = '\0';  /* Why is this here? -chm */
    }

	return SvPV_force(sv, skip);
}

GLvoid * ELI(SV * sv, GLsizei width, GLsizei height, 
             GLenum format, GLenum type, int mode)
{
	int needlen = 0;
    if (!SvROK(sv)) /* don't calc length if arg is a perl ref */
        needlen = gl_pixelbuffer_size(format, width, height, type, mode);
	return EL(sv, needlen);
}

int gl_type_size(GLenum type)
{
	switch (type) {

#ifdef GL_VERSION_1_2
		case GL_UNSIGNED_BYTE_3_3_2:
		case GL_UNSIGNED_BYTE_2_3_3_REV:
			return sizeof(GLubyte);
		case GL_UNSIGNED_SHORT_5_6_5:
		case GL_UNSIGNED_SHORT_5_6_5_REV:
		case GL_UNSIGNED_SHORT_4_4_4_4:
		case GL_UNSIGNED_SHORT_4_4_4_4_REV:
		case GL_UNSIGNED_SHORT_5_5_5_1:
		case GL_UNSIGNED_SHORT_1_5_5_5_REV:
			return sizeof(GLushort);
		case GL_UNSIGNED_INT_8_8_8_8:
		case GL_UNSIGNED_INT_8_8_8_8_REV:
		case GL_UNSIGNED_INT_10_10_10_2:
		case GL_UNSIGNED_INT_2_10_10_10_REV:
			return sizeof(GLuint);
#endif


		case GL_UNSIGNED_BYTE: return sizeof(GLubyte); break;
		case GL_BITMAP: return sizeof(GLubyte); break;
		case GL_BYTE: return  sizeof(GLbyte); break;
		case GL_UNSIGNED_SHORT: return sizeof(GLushort); break;
		case GL_SHORT: return sizeof(GLshort); break;
		case GL_UNSIGNED_INT: return sizeof(GLuint); break;
		case GL_INT: return sizeof(GLint); break;
		case GL_FLOAT: return  sizeof(GLfloat); break;
		case GL_DOUBLE: return  sizeof(GLdouble); break;
		case GL_2_BYTES: return 2;
		case GL_3_BYTES: return 3;
		case GL_4_BYTES: return 4;
	default:
		croak("unknown type");
	}
	return 0;	// Just to make the compiler happy
}

int gl_component_count(GLenum format, GLenum type)
{
	int n;
	switch (format) {
#ifdef GL_VERSION_1_2
		case GL_BGR:
			n = 3; break;
		case GL_BGRA:
			n = 4; break;
#endif


/* 18 */
#ifdef GL_EXT_cmyka
		case GL_CMYK:
			n = 4; break;
		case GL_CMYKA:
			n = 5; break;
#endif

#ifdef GL_EXT_agbr
		case GL_AGBR:
			n = 4; break;
#endif

		case GL_COLOR_INDEX:
		case GL_STENCIL_INDEX:
		case GL_DEPTH_COMPONENT:
		case GL_RED:
		case GL_GREEN:
		case GL_BLUE:
		case GL_ALPHA:
		case GL_LUMINANCE:
			n = 1; break;
		case GL_LUMINANCE_ALPHA:
			n = 2; break;
		case GL_RGB:
			n = 3; break;
		case GL_RGBA:
			n = 4; break;
		default:
			croak("unknown format");
	}

#ifdef GL_VERSION_1_2
	switch (type) {
		case GL_UNSIGNED_BYTE_3_3_2:
		case GL_UNSIGNED_BYTE_2_3_3_REV:
		case GL_UNSIGNED_SHORT_5_6_5:
		case GL_UNSIGNED_SHORT_5_6_5_REV:
		case GL_UNSIGNED_SHORT_4_4_4_4:
		case GL_UNSIGNED_SHORT_4_4_4_4_REV:
		case GL_UNSIGNED_SHORT_5_5_5_1:
		case GL_UNSIGNED_SHORT_1_5_5_5_REV:
		case GL_UNSIGNED_INT_8_8_8_8:
		case GL_UNSIGNED_INT_8_8_8_8_REV:
		case GL_UNSIGNED_INT_10_10_10_2:
		case GL_UNSIGNED_INT_2_10_10_10_REV:
			n = 1;
	}
#endif
	return n;
}


/* From Mesa */

/* Compute ceiling of integer quotient of A divided by B: */
#define CEILING( A, B )  ( (A) % (B) == 0 ? (A)/(B) : (A)/(B)+1 )

unsigned long gl_pixelbuffer_size(
	GLenum format,
	GLsizei	width,
	GLsizei	height,
	GLenum	type,
	int mode)
{
	GLint n; /* elements in a group */
	GLint l; /* number of groups in a row */
	GLint r; /* pack/unpack row length (overrides l if nonzero) */
	GLint s; /* size (in bytes) of an element */
	GLint a; /* alignment */
	unsigned long k; /* size in bytes of row */
	
	r = 0;
	a = 4;
	
	if (mode == gl_pixelbuffer_pack) {
		glGetIntegerv(GL_PACK_ROW_LENGTH, &r);
		glGetIntegerv(GL_PACK_ALIGNMENT, &a);
	} else if (mode == gl_pixelbuffer_unpack) {
		glGetIntegerv(GL_UNPACK_ROW_LENGTH, &r);
		glGetIntegerv(GL_UNPACK_ALIGNMENT, &a);
	}

	l = r > 0 ? r : width;

	s = gl_type_size(type);
	
	n = gl_component_count(format, type);

/* From Mesa, more or less */

	if (type == GL_BITMAP) {
		k = a * CEILING( n * l, 8 * a);
	} else {
		k = l * s * n;

		if ( s < a ) {
			k = (a / s * CEILING(k, a)) * s;
		}
	}

	return k * height;
}

/* Compute ceiling of integer quotient of A divided by B: */
#define CEILING( A, B )  ( (A) % (B) == 0 ? (A)/(B) : (A)/(B)+1 )

void gl_pixelbuffer_size2(
	GLsizei	width,
	GLsizei	height,
	GLsizei depth,
	GLenum format,
	GLenum	type,
	int mode,
	GLsizei * length,
	GLsizei * items)
{
	GLint n; /* elements in a group */
	GLint l; /* number of groups in a row */
	GLint s; /* size (in bytes) of an element */
	GLint a; /* alignment */
	unsigned long k; /* size in bytes of row */
	
	a = 4;
	l = width;
	
	if (mode == gl_pixelbuffer_pack) {
		glGetIntegerv(GL_PACK_ROW_LENGTH, &l);
		glGetIntegerv(GL_PACK_ALIGNMENT, &a);
	} else if (mode == gl_pixelbuffer_unpack) {
		glGetIntegerv(GL_UNPACK_ROW_LENGTH, &l);
		glGetIntegerv(GL_UNPACK_ALIGNMENT, &a);
	}

	s = gl_type_size(type);
	
	n = gl_component_count(format, type);

/* From Mesa, more or less */

	if (type == GL_BITMAP) {
		k = a * CEILING( n * l, 8 * a);
	} else {
		k = l * s * n;

		if ( s < a ) {
			k = (a / s * CEILING(k, a)) * s;
		}
	}
	
	*items = l * n * height * depth;
	*length = (k * height * depth);
	
}

void pgl_set_type(SV * sv, GLenum type, void ** ptr)
{
#define RIV(t)	\
		(*(t*)*ptr) = (t)SvIV(sv);	\
		*(unsigned char**)ptr += sizeof(t);\
		break;
#define RNV(t)	\
		(*(t*)*ptr) = (t)SvNV(sv);	\
		*(unsigned char**)ptr += sizeof(t);\
		break;
	switch (type) {
#ifdef GL_VERSION_1_2
	case GL_UNSIGNED_BYTE_3_3_2:
	case GL_UNSIGNED_BYTE_2_3_3_REV:
		RIV(GLubyte)
	case GL_UNSIGNED_SHORT_5_6_5:
	case GL_UNSIGNED_SHORT_5_6_5_REV:
	case GL_UNSIGNED_SHORT_4_4_4_4:
	case GL_UNSIGNED_SHORT_4_4_4_4_REV:
	case GL_UNSIGNED_SHORT_5_5_5_1:
	case GL_UNSIGNED_SHORT_1_5_5_5_REV:
		RIV(GLushort)
	case GL_UNSIGNED_INT_8_8_8_8:
	case GL_UNSIGNED_INT_8_8_8_8_REV:
	case GL_UNSIGNED_INT_10_10_10_2:
	case GL_UNSIGNED_INT_2_10_10_10_REV:
		RIV(GLuint)
#endif
	case GL_UNSIGNED_BYTE:
	case GL_BITMAP:
	case GL_BYTE:
		RIV(GLubyte)
	case GL_UNSIGNED_SHORT:
	case GL_SHORT:
		RIV(GLushort)
	case GL_UNSIGNED_INT:
	case GL_INT:
		RIV(GLuint)
	case GL_FLOAT:
		RNV(GLfloat)
	case GL_DOUBLE:
		RNV(GLdouble)
	case GL_2_BYTES:
	{
		unsigned long v = SvIV(sv);
		(*(GLubyte*)*ptr) = (GLubyte)(v >> 8);
		*(unsigned char**)ptr++;
		(*(GLubyte*)*ptr) = (GLubyte)(v & 0xff);
		*(unsigned char**)ptr++;
		break;
	}
	case GL_3_BYTES:
	{
		unsigned long v = SvIV(sv);
		(*(GLubyte*)*ptr) = (GLubyte)((v >> 16) & 0xff);
		*(unsigned char**)ptr++;
		(*(GLubyte*)*ptr) = (GLubyte)((v >> 8) & 0xff);
		*(unsigned char**)ptr++;
		(*(GLubyte*)*ptr) = (GLubyte)((v >> 0) & 0xff);
		*(unsigned char**)ptr++;
		break;
	}
	case GL_4_BYTES:
	{
		unsigned long v = SvIV(sv);
		(*(GLubyte*)*ptr) = (GLubyte)((v >> 24) & 0xff);
		*(unsigned char**)ptr++;
		(*(GLubyte*)*ptr) = (GLubyte)((v >> 16) & 0xff);
		*(unsigned char**)ptr++;
		(*(GLubyte*)*ptr) = (GLubyte)((v >> 8) & 0xff);
		*(unsigned char**)ptr++;
		(*(GLubyte*)*ptr) = (GLubyte)((v >> 0) & 0xff);
		*ptr++;
		break;
	}
	default:
		croak("Unable to set data with unknown type");
	}
#undef RIV
#undef RNV
}

SV * pgl_get_type(GLenum type, void ** ptr)
{
	SV * result = 0;
#define RIV(t)	\
		result = newSViv((*(t*)*ptr));	\
		*(unsigned char**)ptr += sizeof(t);	\
		break;
#define RNV(t)	\
		result = newSVnv((*(t*)*ptr));	\
		*(unsigned char**)ptr += sizeof(t);	\
		break;

	switch (type) {
#ifdef GL_VERSION_1_2
	case GL_UNSIGNED_BYTE_3_3_2:
	case GL_UNSIGNED_BYTE_2_3_3_REV:
		RIV(GLubyte)
	case GL_UNSIGNED_SHORT_5_6_5:
	case GL_UNSIGNED_SHORT_5_6_5_REV:
	case GL_UNSIGNED_SHORT_4_4_4_4:
	case GL_UNSIGNED_SHORT_4_4_4_4_REV:
	case GL_UNSIGNED_SHORT_5_5_5_1:
	case GL_UNSIGNED_SHORT_1_5_5_5_REV:
		RIV(GLushort)
	case GL_UNSIGNED_INT_8_8_8_8:
	case GL_UNSIGNED_INT_8_8_8_8_REV:
	case GL_UNSIGNED_INT_10_10_10_2:
	case GL_UNSIGNED_INT_2_10_10_10_REV:
		RIV(GLuint)
#endif
	case GL_UNSIGNED_BYTE:
	case GL_BITMAP:
	case GL_BYTE:
		RIV(GLubyte)
	case GL_UNSIGNED_SHORT:
	case GL_SHORT:
		RIV(GLushort)
	case GL_UNSIGNED_INT:
	case GL_INT:
		RIV(GLuint)
	case GL_FLOAT:
		RNV(GLfloat)
	case GL_DOUBLE:
		RNV(GLdouble)
	case GL_2_BYTES:
	{
		unsigned long v;
		v = (*(GLubyte*)*ptr) << 8;
		*(unsigned char**)ptr++;
		v |= (*(GLubyte*)*ptr) << 0;
		*(unsigned char**)ptr++;
		result = newSViv(v);
		break;
	}
	case GL_3_BYTES:
	{
		unsigned long v;
		v = (*(GLubyte*)*ptr) << 16;
		*(unsigned char**)ptr++;
		v |= (*(GLubyte*)*ptr) << 8;
		*(unsigned char**)ptr++;
		v |= (*(GLubyte*)*ptr) << 0;
		*(unsigned char**)ptr++;
		result = newSViv(v);
		break;
	}
	case GL_4_BYTES:
	{
		unsigned long v;
		v = (*(GLubyte*)*ptr) << 24;
		*(unsigned char**)ptr++;
		v |= (*(GLubyte*)*ptr) << 16;
		*(unsigned char**)ptr++;
		v |= (*(GLubyte*)*ptr) << 8;
		*(unsigned char**)ptr++;
		v |= (*(GLubyte*)*ptr) << 0;
		*(unsigned char**)ptr++;
		result = newSViv(v);
		break;
	}
	default:
		croak("Unable to get data with unknown type");
	}
	return result;
#undef RIV
#undef RNV
}

GLvoid * pack_image_ST(SV ** stack, int count, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, int mode)
{
	int i;
	void * ptr, * optr;
	GLsizei size, max;
	gl_pixelbuffer_size2(width, height, depth, format, type, mode, &size, &max);
	optr = ptr = malloc(size);

	for (i=0;i<count;i++) {
		if (SvROK(stack[i])) {
			AV * ast[8];
			int apos[8];
			int level = 0;
			ast[0] = (AV*)SvRV(stack[i]);
			apos[0] = 0;
			
			if (SvTYPE(ast[0]) != SVt_PVAV) {
				croak("Weird nest 1");
			}
			
			while (level >= 0) {
				SV ** sv;

	 			sv = av_fetch(ast[level], apos[level]++, 0);
	 			if (!sv) {
	 				level--;
	 				continue;
	 			}
	 			
				if (SvROK(*sv)) {
					if (SvTYPE(SvRV(*sv)) != SVt_PVAV) {
						croak("Weird nest 2");
					}
					level++;
					if (level >= 8)
						goto too_many_levels;
					ast[level] = (AV*)SvRV(*sv);
					apos[level] = 0;
				} else {
					if (!max--)
						goto too_much_data;
					pgl_set_type(*sv, type, &ptr);
				}
			}
			
		} else {
			if (!max--)
				goto too_much_data;
			pgl_set_type(stack[i], type, &ptr);
		}
	}

	if (max>0)
		goto too_little_data;

	return optr;

too_little_data:
	croak("too little data");
too_much_data:
	croak("too much data");
too_many_levels:
	croak("too many levels");
	return 0;	// Just to make the compiler happy
}

GLvoid * allocate_image_ST(GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, int mode)
{
	//int i;
	void * ptr;
	GLsizei size, max;
	
	gl_pixelbuffer_size2(width, height, depth, format, type, mode, &size, &max);
	ptr = malloc(size);

	return ptr;
}

SV ** unpack_image_ST(SV ** SP, void * data, 
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, int mode)
{
#define	sp SP
	int i;
	GLsizei size, max;

	gl_pixelbuffer_size2(width, height, depth, format, type, mode, &size, &max);

	EXTEND(sp, max);
	for (i=0;i<max;i++) {
		PUSHs(sv_2mortal(pgl_get_type(type, &data)));
	}
	
	return sp;
#undef sp
}


