/**
 * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
 *
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 */

import { idFactory } from '../../mol-util/id-factory';

const c = {
    ACTIVE_ATTRIBUTE_MAX_LENGTH: 35722,
    ACTIVE_UNIFORM_MAX_LENGTH: 35719,
    INFO_LOG_LENGTH: 35716,
    NUM_COMPRESSED_TEXTURE_FORMATS: 34466,
    SHADER_COMPILER: 36346,
    SHADER_SOURCE_LENGTH: 35720,

    MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0x84FF,
    MAX_TEXTURE_IMAGE_UNITS_NV: 0x8872
};

const gl = {
    ACTIVE_ATTRIBUTES: 35721,
    ACTIVE_TEXTURE: 34016,
    ACTIVE_UNIFORMS: 35718,
    ALIASED_LINE_WIDTH_RANGE: 33902,
    ALIASED_POINT_SIZE_RANGE: 33901,
    ALPHA: 6406,
    ALPHA_BITS: 3413,
    ALWAYS: 519,
    ARRAY_BUFFER: 34962,
    ARRAY_BUFFER_BINDING: 34964,
    ATTACHED_SHADERS: 35717,
    BACK: 1029,
    BLEND: 3042,
    BLEND_COLOR: 32773,
    BLEND_DST_ALPHA: 32970,
    BLEND_DST_RGB: 32968,
    BLEND_EQUATION: 32777,
    BLEND_EQUATION_ALPHA: 34877,
    BLEND_EQUATION_RGB: 32777,
    BLEND_SRC_ALPHA: 32971,
    BLEND_SRC_RGB: 32969,
    BLUE_BITS: 3412,
    BOOL: 35670,
    BOOL_VEC2: 35671,
    BOOL_VEC3: 35672,
    BOOL_VEC4: 35673,
    BROWSER_DEFAULT_WEBGL: 37444,
    BUFFER_SIZE: 34660,
    BUFFER_USAGE: 34661,
    BYTE: 5120,
    CCW: 2305,
    CLAMP_TO_EDGE: 33071,
    COLOR_ATTACHMENT0: 36064,
    COLOR_BUFFER_BIT: 16384,
    COLOR_CLEAR_VALUE: 3106,
    COLOR_WRITEMASK: 3107,
    COMPILE_STATUS: 35713,
    COMPRESSED_TEXTURE_FORMATS: 34467,
    CONSTANT_ALPHA: 32771,
    CONSTANT_COLOR: 32769,
    CONTEXT_LOST_WEBGL: 37442,
    CULL_FACE: 2884,
    CULL_FACE_MODE: 2885,
    CURRENT_PROGRAM: 35725,
    CURRENT_VERTEX_ATTRIB: 34342,
    CW: 2304,
    DECR: 7683,
    DECR_WRAP: 34056,
    DELETE_STATUS: 35712,
    DEPTH_ATTACHMENT: 36096,
    DEPTH_BITS: 3414,
    DEPTH_BUFFER_BIT: 256,
    DEPTH_CLEAR_VALUE: 2931,
    DEPTH_COMPONENT: 6402,
    DEPTH_COMPONENT16: 33189,
    DEPTH_FUNC: 2932,
    DEPTH_RANGE: 2928,
    DEPTH_STENCIL: 34041,
    DEPTH_STENCIL_ATTACHMENT: 33306,
    DEPTH_TEST: 2929,
    DEPTH_WRITEMASK: 2930,
    DITHER: 3024,
    DONT_CARE: 4352,
    DST_ALPHA: 772,
    DST_COLOR: 774,
    DYNAMIC_DRAW: 35048,
    ELEMENT_ARRAY_BUFFER: 34963,
    ELEMENT_ARRAY_BUFFER_BINDING: 34965,
    EQUAL: 514,
    FASTEST: 4353,
    FLOAT: 5126,
    FLOAT_MAT2: 35674,
    FLOAT_MAT3: 35675,
    FLOAT_MAT4: 35676,
    FLOAT_VEC2: 35664,
    FLOAT_VEC3: 35665,
    FLOAT_VEC4: 35666,
    FRAGMENT_SHADER: 35632,
    FRAMEBUFFER: 36160,
    FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: 36049,
    FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: 36048,
    FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: 36051,
    FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: 36050,
    FRAMEBUFFER_BINDING: 36006,
    FRAMEBUFFER_COMPLETE: 36053,
    FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 36054,
    FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 36057,
    FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 36055,
    FRAMEBUFFER_UNSUPPORTED: 36061,
    FRONT: 1028,
    FRONT_AND_BACK: 1032,
    FRONT_FACE: 2886,
    FUNC_ADD: 32774,
    FUNC_REVERSE_SUBTRACT: 32779,
    FUNC_SUBTRACT: 32778,
    GENERATE_MIPMAP_HINT: 33170,
    GEQUAL: 518,
    GREATER: 516,
    GREEN_BITS: 3411,
    HIGH_FLOAT: 36338,
    HIGH_INT: 36341,
    IMPLEMENTATION_COLOR_READ_FORMAT: 0x8B9B,
    IMPLEMENTATION_COLOR_READ_TYPE: 0x8B9A,
    INCR: 7682,
    INCR_WRAP: 34055,
    INT: 5124,
    INT_VEC2: 35667,
    INT_VEC3: 35668,
    INT_VEC4: 35669,
    INVALID_ENUM: 1280,
    INVALID_FRAMEBUFFER_OPERATION: 1286,
    INVALID_OPERATION: 1282,
    INVALID_VALUE: 1281,
    INVERT: 5386,
    KEEP: 7680,
    LEQUAL: 515,
    LESS: 513,
    LINEAR: 9729,
    LINEAR_MIPMAP_LINEAR: 9987,
    LINEAR_MIPMAP_NEAREST: 9985,
    LINES: 1,
    LINE_LOOP: 2,
    LINE_STRIP: 3,
    LINE_WIDTH: 2849,
    LINK_STATUS: 35714,
    LOW_FLOAT: 36336,
    LOW_INT: 36339,
    LUMINANCE: 6409,
    LUMINANCE_ALPHA: 6410,
    MAX_COMBINED_TEXTURE_IMAGE_UNITS: 35661,
    MAX_CUBE_MAP_TEXTURE_SIZE: 34076,
    MAX_FRAGMENT_UNIFORM_VECTORS: 36349,
    MAX_RENDERBUFFER_SIZE: 34024,
    MAX_TEXTURE_IMAGE_UNITS: 34930,
    MAX_TEXTURE_SIZE: 3379,
    MAX_VARYING_VECTORS: 36348,
    MAX_VERTEX_ATTRIBS: 34921,
    MAX_VERTEX_TEXTURE_IMAGE_UNITS: 35660,
    MAX_VERTEX_UNIFORM_VECTORS: 36347,
    MAX_VIEWPORT_DIMS: 3386,
    MEDIUM_FLOAT: 36337,
    MEDIUM_INT: 36340,
    MIRRORED_REPEAT: 33648,
    NEAREST: 9728,
    NEAREST_MIPMAP_LINEAR: 9986,
    NEAREST_MIPMAP_NEAREST: 9984,
    NEVER: 512,
    NICEST: 4354,
    NONE: 0,
    NOTEQUAL: 517,
    NO_ERROR: 0,
    ONE: 1,
    ONE_MINUS_CONSTANT_ALPHA: 32772,
    ONE_MINUS_CONSTANT_COLOR: 32770,
    ONE_MINUS_DST_ALPHA: 773,
    ONE_MINUS_DST_COLOR: 775,
    ONE_MINUS_SRC_ALPHA: 771,
    ONE_MINUS_SRC_COLOR: 769,
    OUT_OF_MEMORY: 1285,
    PACK_ALIGNMENT: 3333,
    POINTS: 0,
    POLYGON_OFFSET_FACTOR: 32824,
    POLYGON_OFFSET_FILL: 32823,
    POLYGON_OFFSET_UNITS: 10752,
    RED_BITS: 3410,
    RENDERBUFFER: 36161,
    RENDERBUFFER_ALPHA_SIZE: 36179,
    RENDERBUFFER_BINDING: 36007,
    RENDERBUFFER_BLUE_SIZE: 36178,
    RENDERBUFFER_DEPTH_SIZE: 36180,
    RENDERBUFFER_GREEN_SIZE: 36177,
    RENDERBUFFER_HEIGHT: 36163,
    RENDERBUFFER_INTERNAL_FORMAT: 36164,
    RENDERBUFFER_RED_SIZE: 36176,
    RENDERBUFFER_STENCIL_SIZE: 36181,
    RENDERBUFFER_WIDTH: 36162,
    RENDERER: 7937,
    REPEAT: 10497,
    REPLACE: 7681,
    RGB: 6407,
    RGB5_A1: 32855,
    RGB565: 36194,
    RGBA: 6408,
    RGBA4: 32854,
    SAMPLER_2D: 35678,
    SAMPLER_CUBE: 35680,
    SAMPLES: 32937,
    SAMPLE_ALPHA_TO_COVERAGE: 32926,
    SAMPLE_BUFFERS: 32936,
    SAMPLE_COVERAGE: 32928,
    SAMPLE_COVERAGE_INVERT: 32939,
    SAMPLE_COVERAGE_VALUE: 32938,
    SCISSOR_BOX: 3088,
    SCISSOR_TEST: 3089,
    SHADER_TYPE: 35663,
    SHADING_LANGUAGE_VERSION: 35724,
    SHORT: 5122,
    SRC_ALPHA: 770,
    SRC_ALPHA_SATURATE: 776,
    SRC_COLOR: 768,
    STATIC_DRAW: 35044,
    STENCIL_ATTACHMENT: 36128,
    STENCIL_BACK_FAIL: 34817,
    STENCIL_BACK_FUNC: 34816,
    STENCIL_BACK_PASS_DEPTH_FAIL: 34818,
    STENCIL_BACK_PASS_DEPTH_PASS: 34819,
    STENCIL_BACK_REF: 36003,
    STENCIL_BACK_VALUE_MASK: 36004,
    STENCIL_BACK_WRITEMASK: 36005,
    STENCIL_BITS: 3415,
    STENCIL_BUFFER_BIT: 1024,
    STENCIL_CLEAR_VALUE: 2961,
    STENCIL_FAIL: 2964,
    STENCIL_FUNC: 2962,
    STENCIL_INDEX: 6401,
    STENCIL_INDEX8: 36168,
    STENCIL_PASS_DEPTH_FAIL: 2965,
    STENCIL_PASS_DEPTH_PASS: 2966,
    STENCIL_REF: 2967,
    STENCIL_TEST: 2960,
    STENCIL_VALUE_MASK: 2963,
    STENCIL_WRITEMASK: 2968,
    STREAM_DRAW: 35040,
    SUBPIXEL_BITS: 3408,
    TEXTURE: 5890,
    TEXTURE0: 33984,
    TEXTURE1: 33985,
    TEXTURE2: 33986,
    TEXTURE3: 33987,
    TEXTURE4: 33988,
    TEXTURE5: 33989,
    TEXTURE6: 33990,
    TEXTURE7: 33991,
    TEXTURE8: 33992,
    TEXTURE9: 33993,
    TEXTURE10: 33994,
    TEXTURE11: 33995,
    TEXTURE12: 33996,
    TEXTURE13: 33997,
    TEXTURE14: 33998,
    TEXTURE15: 33999,
    TEXTURE16: 34000,
    TEXTURE17: 34001,
    TEXTURE18: 34002,
    TEXTURE19: 34003,
    TEXTURE20: 34004,
    TEXTURE21: 34005,
    TEXTURE22: 34006,
    TEXTURE23: 34007,
    TEXTURE24: 34008,
    TEXTURE25: 34009,
    TEXTURE26: 34010,
    TEXTURE27: 34011,
    TEXTURE28: 34012,
    TEXTURE29: 34013,
    TEXTURE30: 34014,
    TEXTURE31: 34015,
    TEXTURE_2D: 3553,
    TEXTURE_BINDING_2D: 32873,
    TEXTURE_BINDING_CUBE_MAP: 34068,
    TEXTURE_CUBE_MAP: 34067,
    TEXTURE_CUBE_MAP_NEGATIVE_X: 34070,
    TEXTURE_CUBE_MAP_NEGATIVE_Y: 34072,
    TEXTURE_CUBE_MAP_NEGATIVE_Z: 34074,
    TEXTURE_CUBE_MAP_POSITIVE_X: 34069,
    TEXTURE_CUBE_MAP_POSITIVE_Y: 34071,
    TEXTURE_CUBE_MAP_POSITIVE_Z: 34073,
    TEXTURE_MAG_FILTER: 10240,
    TEXTURE_MIN_FILTER: 10241,
    TEXTURE_WRAP_S: 10242,
    TEXTURE_WRAP_T: 10243,
    TRIANGLES: 4,
    TRIANGLE_FAN: 6,
    TRIANGLE_STRIP: 5,
    UNPACK_ALIGNMENT: 3317,
    UNPACK_COLORSPACE_CONVERSION_WEBGL: 37443,
    UNPACK_FLIP_Y_WEBGL: 37440,
    UNPACK_PREMULTIPLY_ALPHA_WEBGL: 37441,
    UNSIGNED_BYTE: 5121,
    UNSIGNED_INT: 5125,
    UNSIGNED_SHORT: 5123,
    UNSIGNED_SHORT_4_4_4_4: 32819,
    UNSIGNED_SHORT_5_5_5_1: 32820,
    UNSIGNED_SHORT_5_6_5: 33635,
    VALIDATE_STATUS: 35715,
    VENDOR: 7936,
    VERSION: 7938,
    VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: 34975,
    VERTEX_ATTRIB_ARRAY_ENABLED: 34338,
    VERTEX_ATTRIB_ARRAY_NORMALIZED: 34922,
    VERTEX_ATTRIB_ARRAY_POINTER: 34373,
    VERTEX_ATTRIB_ARRAY_SIZE: 34339,
    VERTEX_ATTRIB_ARRAY_STRIDE: 34340,
    VERTEX_ATTRIB_ARRAY_TYPE: 34341,
    VERTEX_SHADER: 35633,
    VIEWPORT: 2978,
    ZERO: 0
};
type gl = typeof gl

export function createGl(width: number, height: number, contextAttributes: WebGLContextAttributes): WebGLRenderingContext {
    const getNextId = idFactory();
    const items: { [k: number]: any } = {};
    const boundTextures: { [k: number]: any } = {};
    const viewport = { x: 0, y: 0, width, height };
    const depthRange = { zNear: 0, zFar: 1 };
    const colorClearValue = { r: 0, g: 0, b: 0, a: 0 };

    // let _activeFramebuffer: number
    // let _activeRenderbuffer: number

    return {
        ...gl,
        canvas: {
            width, height
        } as HTMLCanvasElement,
        getAttachedShaders: function (program: WebGLProgram) {
            return [] as WebGLShader[];
        },
        getBufferParameter: function (target: number, pname: number) {
            return 0;
        },
        getContextAttributes: function () { return contextAttributes; },
        getFramebufferAttachmentParameter: function () {},
        getProgramInfoLog: function () { return ''; },
        getShaderInfoLog: function () { return ''; },
        getRenderbufferParameter: function () {},
        getShaderPrecisionFormat: function (shadertype: number, precisiontype: number) {
            return {
                precision: 0,
                rangeMax: 0,
                rangeMin: 0
            };
        },
        getShaderSource: function (shader: WebGLShader | null) { return ''; },
        getTexParameter: function () {},
        getUniform: function () {},
        getVertexAttrib: function () {},
        getVertexAttribOffset: function (index: number, pname: number) { return 0; },
        hint: function (target: number, mode: number) {},
        isBuffer: function (buffer: WebGLBuffer | null) { return true; },
        isEnabled: function (cap: number) { return true; },
        isFramebuffer: function (framebuffer: WebGLFramebuffer | null) { return true; },
        isProgram: function (program: WebGLProgram | null) { return true; },
        isRenderbuffer: function (renderbuffer: WebGLRenderbuffer | null) { return true; },
        isShader: function (shader: WebGLShader | null) { return true; },
        isTexture: function (texture: WebGLTexture | null) { return true; },
        getExtension: function (extensionName: string): any {
            switch (extensionName) {
                case 'EXT_blend_minmax': return {
                    MAX_EXT: 0,
                    MIN_EXT: 0
                } as EXT_blend_minmax;
                case 'EXT_texture_filter_anisotropic': return {
                    MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0,
                    TEXTURE_MAX_ANISOTROPY_EXT: 0
                } as EXT_texture_filter_anisotropic;
                case 'EXT_frag_depth': return {} as EXT_frag_depth;
                case 'EXT_shader_texture_lod': return {} as EXT_shader_texture_lod;
                case 'EXT_sRGB': return {
                    FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT: 0,
                    SRGB8_ALPHA8_EXT: 0,
                    SRGB_ALPHA_EXT: 0,
                    SRGB_EXT: 0
                } as EXT_sRGB;
                case 'OES_vertex_array_object': return {
                    VERTEX_ARRAY_BINDING_OES: 0,
                    bindVertexArrayOES: function (arrayObject: WebGLVertexArrayObjectOES) { },
                    createVertexArrayOES: function (): WebGLVertexArrayObjectOES { return {}; },
                    deleteVertexArrayOES: function (arrayObject: WebGLVertexArrayObjectOES) { },
                    isVertexArrayOES: function (value: any) { return true; }
                } as OES_vertex_array_object;
                case 'WEBGL_color_buffer_float': return {
                    FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0,
                    RGB32F_EXT: 0,
                    RGBA32F_EXT: 0,
                    UNSIGNED_NORMALIZED_EXT: 0
                } as WEBGL_color_buffer_float;
                case 'WEBGL_compressed_texture_astc': return null;
                case 'WEBGL_compressed_texture_s3tc_srgb': return null;
                case 'WEBGL_debug_shaders': return {
                    getTranslatedShaderSource(shader: WebGLShader) { return ''; }
                } as WEBGL_debug_shaders;
                case 'WEBGL_draw_buffers': return null;
                case 'WEBGL_lose_context': return {
                    loseContext: function () { },
                    restoreContext: function () { },
                } as WEBGL_lose_context;
                case 'WEBGL_depth_texture': return {
                    UNSIGNED_INT_24_8_WEBGL: 0
                } as WEBGL_depth_texture;
                case 'WEBGL_debug_renderer_info': return {
                    UNMASKED_RENDERER_WEBGL: 0,
                    UNMASKED_VENDOR_WEBGL: 0
                } as WEBGL_debug_renderer_info;
                case 'WEBGL_compressed_texture_s3tc': return null;
                case 'OES_texture_half_float_linear': return {} as OES_texture_half_float_linear;
                case 'OES_texture_half_float': return {
                    HALF_FLOAT_OES: 0
                } as OES_texture_half_float;
                case 'OES_texture_float_linear': return {} as OES_texture_float_linear;
                case 'OES_texture_float': return {} as OES_texture_float;
                case 'OES_standard_derivatives': return {
                    FRAGMENT_SHADER_DERIVATIVE_HINT_OES: 0
                } as OES_standard_derivatives;
                case 'OES_element_index_uint': return {} as OES_element_index_uint;
                case 'ANGLE_instanced_arrays': return {
                    drawArraysInstancedANGLE: function (mode: number, first: number, count: number, primcount: number) {},
                    drawElementsInstancedANGLE: function (mode: number, count: number, type: number, offset: number, primcount: number) {},
                    vertexAttribDivisorANGLE: function (index: number, divisor: number) {},
                    VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE: 0
                } as ANGLE_instanced_arrays;
            }
            return null;
        },
        createBuffer: function () {
            const id = getNextId();
            items[id] = {
                which: 'buffer',
            };
            return id;
        },
        deleteBuffer: function () { },
        bindBuffer: function () { },
        bufferData: function () { },
        getParameter: function (pname: number) {
            switch (pname) {
                case c.MAX_TEXTURE_MAX_ANISOTROPY_EXT: return 16;
                case c.MAX_TEXTURE_IMAGE_UNITS_NV: return 16;

                case gl.ELEMENT_ARRAY_BUFFER_BINDING:
                case gl.ARRAY_BUFFER_BINDING:
                case gl.FRAMEBUFFER_BINDING:
                case gl.CURRENT_PROGRAM:
                case gl.RENDERBUFFER_BINDING:
                    return 0;
                    // return _activeFramebuffer
                    // return _activeRenderbuffer
                case gl.TEXTURE_BINDING_2D:
                case gl.TEXTURE_BINDING_CUBE_MAP:
                    return null;

                case gl.VERSION:
                    return '1.0.0';
                case gl.VENDOR:
                    return 'shim';
                case gl.RENDERER:
                    return 'shim-renderer';
                case gl.SHADING_LANGUAGE_VERSION:
                    return 'WebGL GLSL ES 1.0 shim';

                case gl.COMPRESSED_TEXTURE_FORMATS:
                    return new Uint32Array(0);

                // Int arrays
                case gl.MAX_VIEWPORT_DIMS:
                case gl.SCISSOR_BOX:
                    return new Int32Array([0, 0, 4096, 4096]);
                case gl.VIEWPORT:
                    const { x, y, width, height } = viewport;
                    return new Int32Array([x, y, width, height]);

                // Float arrays
                case gl.ALIASED_LINE_WIDTH_RANGE:
                    return new Float32Array([0, 1]);
                case gl.ALIASED_POINT_SIZE_RANGE:
                    return new Float32Array([0, 255]);
                case gl.DEPTH_RANGE:
                    return new Float32Array([depthRange.zNear, depthRange.zFar]);
                case gl.BLEND_COLOR:
                    return new Float32Array([0, 0, 0, 0]);
                case gl.COLOR_CLEAR_VALUE:
                    const { r, g, b, a } = colorClearValue;
                    return new Float32Array([r, g, b, a]);

                case gl.COLOR_WRITEMASK:
                    return 0;

                case gl.DEPTH_CLEAR_VALUE:
                case gl.LINE_WIDTH:
                case gl.POLYGON_OFFSET_FACTOR:
                case gl.POLYGON_OFFSET_UNITS:
                case gl.SAMPLE_COVERAGE_VALUE:
                    return 1;

                case gl.BLEND:
                case gl.CULL_FACE:
                case gl.DEPTH_TEST:
                case gl.DEPTH_WRITEMASK:
                case gl.DITHER:
                case gl.POLYGON_OFFSET_FILL:
                case gl.SAMPLE_COVERAGE_INVERT:
                case gl.SCISSOR_TEST:
                case gl.STENCIL_TEST:
                case gl.UNPACK_FLIP_Y_WEBGL:
                case gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL:
                    return false;

                case gl.MAX_TEXTURE_SIZE:
                case gl.MAX_CUBE_MAP_TEXTURE_SIZE:
                    return 16384;

                case gl.MAX_VERTEX_UNIFORM_VECTORS:
                case gl.MAX_FRAGMENT_UNIFORM_VECTORS:
                    return 4096;

                case gl.MAX_VARYING_VECTORS:
                case gl.MAX_TEXTURE_IMAGE_UNITS:
                case gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS:
                case gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS:
                    return 32;

                case gl.MAX_RENDERBUFFER_SIZE:
                    return 2048;

                case gl.ACTIVE_TEXTURE:
                case gl.ALPHA_BITS:
                case gl.BLEND_DST_ALPHA:
                case gl.BLEND_DST_RGB:
                case gl.BLEND_EQUATION_ALPHA:
                case gl.BLEND_EQUATION_RGB:
                case gl.BLEND_SRC_ALPHA:
                case gl.BLEND_SRC_RGB:
                case gl.BLUE_BITS:
                case gl.CULL_FACE_MODE:
                case gl.DEPTH_BITS:
                case gl.DEPTH_FUNC:
                case gl.FRONT_FACE:
                case gl.GENERATE_MIPMAP_HINT:
                case gl.GREEN_BITS:
                case gl.MAX_VERTEX_ATTRIBS:
                case gl.PACK_ALIGNMENT:
                case gl.RED_BITS:
                case gl.SAMPLE_BUFFERS:
                case gl.SAMPLES:
                case gl.STENCIL_BACK_FAIL:
                case gl.STENCIL_BACK_FUNC:
                case gl.STENCIL_BACK_PASS_DEPTH_FAIL:
                case gl.STENCIL_BACK_PASS_DEPTH_PASS:
                case gl.STENCIL_BACK_REF:
                case gl.STENCIL_BACK_VALUE_MASK:
                case gl.STENCIL_BACK_WRITEMASK:
                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_VALUE_MASK:
                case gl.STENCIL_WRITEMASK:
                case gl.SUBPIXEL_BITS:
                case gl.UNPACK_ALIGNMENT:
                case gl.UNPACK_COLORSPACE_CONVERSION_WEBGL:
                    return 0;

                default:
                    return 0;
            }
        },
        getSupportedExtensions: function () {
            return [
                'EXT_blend_minmax', 'EXT_texture_filter_anisotropic', 'EXT_frag_depth',
                'EXT_shader_texture_lod', 'EXT_sRGB', 'OES_vertex_array_object',
                'WEBGL_color_buffer_float', 'WEBGL_debug_shaders', 'WEBGL_lose_context',
                'WEBGL_depth_texture', 'WEBGL_debug_renderer_info', 'OES_texture_half_float_linear',
                'OES_texture_half_float', 'OES_texture_float_linear', 'OES_texture_float',
                'OES_standard_derivatives', 'OES_element_index_uint', 'ANGLE_instanced_arrays'
            ];
        },
        createShader: function (type: number) {
            const id = getNextId();
            items[id] = {
                which: 'shader',
                type: type,
            };
            return id as WebGLShader;
        },
        getShaderParameter: function (shader: WebGLShader, pname: number) {
            switch (pname) {
                case gl.SHADER_TYPE: return items[shader as number].type;
                case gl.COMPILE_STATUS: return true;
                default: throw new Error(`getShaderParameter ${pname}`);
            }
        },
        shaderSource: function () { },
        compileShader: function () { },
        createProgram: function () {
            const id = getNextId();
            items[id] = {
                which: 'program',
                shaders: [],
            };
            return id;
        },
        attachShader: function (program: WebGLProgram, shader: WebGLShader) {
            items[program as number].shaders.push(shader as number);
        },
        bindAttribLocation: function () { },
        linkProgram: function () { },
        getProgramParameter: function (program: number, pname: number) {
            switch (pname) {
                case gl.LINK_STATUS: return true;
                case gl.ACTIVE_UNIFORMS: return 4;
                case gl.ACTIVE_ATTRIBUTES: return 0;
                case gl.ACTIVE_UNIFORMS: return 0;
                case gl.DELETE_STATUS: return false;
                case gl.VALIDATE_STATUS: return true;
                case gl.ATTACHED_SHADERS: return 2;
                default: throw new Error(`getProgramParameter ${pname}`);
            }
        },
        deleteShader: function () { },
        deleteProgram: function () { },
        viewport: function (x: number, y: number, width: number, height: number) {
            viewport.x = x;
            viewport.y = y;
            viewport.width = width;
            viewport.height = height;
        },
        clearColor: function (red: number, green: number, blue: number, alpha: number) {
            colorClearValue.r = red;
            colorClearValue.g = green;
            colorClearValue.b = blue;
            colorClearValue.a = alpha;
        },
        clearDepth: function () { },
        depthFunc: function () { },
        enable: function () { },
        disable: function () { },
        frontFace: function () { },
        cullFace: function () { },
        activeTexture: function () { },
        createTexture: function () {
            const id = getNextId();
            items[id] = {
                which: 'texture',
            };
            return id;
        },
        deleteTexture: function () { },
        bindTexture: function (target: number, texture: WebGLTexture | null) {
            boundTextures[target as number] = texture;
        },
        texParameterf: function () { },
        texParameteri: function () { },
        pixelStorei: function () { },
        texImage2D: function () { },
        texSubImage2D: function () { },
        compressedTexImage2D: function () { },
        useProgram: function () { },
        getUniformLocation: function () {
            return 0;
        },
        getActiveUniform: function (program: WebGLProgram, index: number) {
            return {
                size: 1,
                type: gl.INT_VEC3,
                name: `__activeUniform${index}`,
            };
        },
        getActiveAttrib: function (program: WebGLProgram, index: number) {
            return {
                size: 1,
                type: gl.FLOAT,
                name: `__activeAttrib${index}`
            };
        },
        clear: function () { },
        uniform1f: function () { },
        uniform1fv: function () { },
        uniform1i: function () { },
        uniform1iv: function () { },
        uniform2f: function () { },
        uniform2fv: function () { },
        uniform2i: function () { },
        uniform2iv: function () { },
        uniform3f: function () { },
        uniform3fv: function () { },
        uniform3i: function () { },
        uniform3iv: function () { },
        uniform4f: function () { },
        uniform4fv: function () { },
        uniform4i: function () { },
        uniform4iv: function () { },
        uniformMatrix2fv: function () { },
        uniformMatrix3fv: function () { },
        uniformMatrix4fv: function () { },
        getAttribLocation: function () { return 1; },
        vertexAttribPointer: function () { },
        enableVertexAttribArray: function () { },
        disableVertexAttribArray: function () { },
        drawElements: function () { },
        drawArrays: function () { },
        depthMask: function () { },
        depthRange: function (zNear: number, zFar: number) {
            depthRange.zNear = zNear;
            depthRange.zFar = zFar;
        },
        bufferSubData: function () { },
        blendFunc: function () { },
        createFramebuffer: function () {
            const id = getNextId();
            items[id] = {
                which: 'framebuffer',
                shaders: [],
            };
            return id;
        },
        bindFramebuffer: function () { },
        framebufferTexture2D: function () { },
        checkFramebufferStatus: function () {
            return gl.FRAMEBUFFER_COMPLETE;
        },
        deleteFramebuffer: function () { },
        createRenderbuffer: function () {
            const id = getNextId();
            items[id] = {
                which: 'renderbuffer',
                shaders: [],
            };
            return id;
        },
        bindRenderbuffer: function () { },
        deleteRenderbuffer: function () { },
        renderbufferStorage: function () { },
        framebufferRenderbuffer: function () { },
        scissor: function () { },
        colorMask: function () { },
        lineWidth: function () { },
        vertexAttrib1f: function () { },
        vertexAttrib1fv: function () { },
        vertexAttrib2f: function () { },
        vertexAttrib2fv: function () { },
        vertexAttrib3f: function () { },
        vertexAttrib3fv: function () { },
        vertexAttrib4f: function () { },
        vertexAttrib4fv: function () { },
        validateProgram: function () { },
        generateMipmap: function () { },
        isContextLost: function () { return false; },
        drawingBufferWidth: width,
        drawingBufferHeight: height,
        blendColor: function () { },
        blendEquation: function () { },
        blendEquationSeparate: function () { },
        blendFuncSeparate: function () { },
        clearStencil: function () { },
        compressedTexSubImage2D: function () { },
        copyTexImage2D: function () { },
        copyTexSubImage2D: function () { },
        detachShader: function () { },
        finish: function () { },
        flush: function () { },
        getError: function () { return 0; },
        polygonOffset: function () { },
        readPixels: function () { },
        sampleCoverage: function () { },
        stencilFunc: function () { },
        stencilFuncSeparate: function () { },
        stencilMask: function () { },
        stencilMaskSeparate: function () { },
        stencilOp: function () { },
        stencilOpSeparate: function () { },
    };
}