/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(unused_variables)]

use gleam::gl::*;
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::str;

#[allow(unused)]
macro_rules! debug {
    ($($x:tt)*) => {};
}

#[repr(C)]
struct LockedTexture {
    _private: [u8; 0],
}

#[allow(dead_code)]
extern "C" {
    fn ActiveTexture(texture: GLenum);
    fn BindTexture(target: GLenum, texture: GLuint);
    fn BindBuffer(target: GLenum, buffer: GLuint);
    fn BindVertexArray(vao: GLuint);
    fn BindFramebuffer(target: GLenum, fb: GLuint);
    fn BindRenderbuffer(target: GLenum, rb: GLuint);
    fn BlendFunc(srgb: GLenum, drgb: GLenum, sa: GLenum, da: GLenum);
    fn BlendColor(r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat);
    fn BlendEquation(mode: GLenum);
    fn Enable(cap: GLenum);
    fn Disable(cap: GLenum);
    fn GenQueries(n: GLsizei, result: *mut GLuint);
    fn BeginQuery(target: GLenum, id: GLuint);
    fn EndQuery(target: GLenum);
    fn GetQueryObjectui64v(id: GLuint, pname: GLenum, params: *mut GLuint64);
    fn GenBuffers(n: i32, result: *mut GLuint);
    fn GenTextures(n: i32, result: *mut GLuint);
    fn GenFramebuffers(n: i32, result: *mut GLuint);
    fn GenRenderbuffers(n: i32, result: *mut GLuint);
    fn BufferData(target: GLenum, size: GLsizeiptr, data: *const GLvoid, usage: GLenum);
    fn BufferSubData(target: GLenum, offset: GLintptr, size: GLsizeiptr, data: *const GLvoid);
    fn MapBuffer(target: GLenum, access: GLbitfield) -> *mut c_void;
    fn MapBufferRange(
        target: GLenum,
        offset: GLintptr,
        length: GLsizeiptr,
        access: GLbitfield,
    ) -> *mut c_void;
    fn UnmapBuffer(target: GLenum) -> GLboolean;
    fn TexStorage2D(
        target: GLenum,
        levels: GLint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
    );
    fn FramebufferTexture2D(
        target: GLenum,
        attachment: GLenum,
        textarget: GLenum,
        texture: GLuint,
        level: GLint,
    );
    fn CheckFramebufferStatus(target: GLenum) -> GLenum;
    fn InvalidateFramebuffer(target: GLenum, num_attachments: GLsizei, attachments: *const GLenum);
    fn TexImage2D(
        target: GLenum,
        level: GLint,
        internal_format: GLint,
        width: GLsizei,
        height: GLsizei,
        border: GLint,
        format: GLenum,
        ty: GLenum,
        data: *const c_void,
    );
    fn TexSubImage2D(
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        ty: GLenum,
        data: *const c_void,
    );
    fn GenerateMipmap(target: GLenum);
    fn GetUniformLocation(program: GLuint, name: *const GLchar) -> GLint;
    fn BindAttribLocation(program: GLuint, index: GLuint, name: *const GLchar);
    fn GetAttribLocation(program: GLuint, name: *const GLchar) -> GLint;
    fn GenVertexArrays(n: i32, result: *mut GLuint);
    fn VertexAttribPointer(
        index: GLuint,
        size: GLint,
        type_: GLenum,
        normalized: GLboolean,
        stride: GLsizei,
        offset: *const GLvoid,
    );
    fn VertexAttribIPointer(
        index: GLuint,
        size: GLint,
        type_: GLenum,
        stride: GLsizei,
        offset: *const GLvoid,
    );
    fn CreateShader(shader_type: GLenum) -> GLuint;
    fn AttachShader(program: GLuint, shader: GLuint);
    fn CreateProgram() -> GLuint;
    fn Uniform1i(location: GLint, v0: GLint);
    fn Uniform4fv(location: GLint, count: GLsizei, value: *const GLfloat);
    fn UniformMatrix4fv(
        location: GLint,
        count: GLsizei,
        transpose: GLboolean,
        value: *const GLfloat,
    );
    fn DrawElementsInstanced(
        mode: GLenum,
        count: GLsizei,
        type_: GLenum,
        indices: GLintptr,
        instancecount: GLsizei,
    );
    fn EnableVertexAttribArray(index: GLuint);
    fn VertexAttribDivisor(index: GLuint, divisor: GLuint);
    fn LinkProgram(program: GLuint);
    fn GetLinkStatus(program: GLuint) -> GLint;
    fn UseProgram(program: GLuint);
    fn SetViewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei);
    fn FramebufferRenderbuffer(
        target: GLenum,
        attachment: GLenum,
        renderbuffertarget: GLenum,
        renderbuffer: GLuint,
    );
    fn RenderbufferStorage(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei);
    fn DepthMask(flag: GLboolean);
    fn DepthFunc(func: GLenum);
    fn SetScissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei);
    fn ClearColor(r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat);
    fn ClearDepth(depth: GLdouble);
    fn Clear(mask: GLbitfield);
    fn ClearTexSubImage(
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        zoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        format: GLenum,
        ty: GLenum,
        data: *const c_void,
    );
    fn ClearTexImage(target: GLenum, level: GLint, format: GLenum, ty: GLenum, data: *const c_void);
    fn ClearColorRect(
        fbo: GLuint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        r: GLfloat,
        g: GLfloat,
        b: GLfloat,
        a: GLfloat,
    );
    fn PixelStorei(name: GLenum, param: GLint);
    fn ReadPixels(
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        ty: GLenum,
        data: *mut c_void,
    );
    fn Finish();
    fn ShaderSourceByName(shader: GLuint, name: *const GLchar);
    fn TexParameteri(target: GLenum, pname: GLenum, param: GLint);
    fn CopyImageSubData(
        src_name: GLuint,
        src_target: GLenum,
        src_level: GLint,
        src_x: GLint,
        src_y: GLint,
        src_z: GLint,
        dst_name: GLuint,
        dst_target: GLenum,
        dst_level: GLint,
        dst_x: GLint,
        dst_y: GLint,
        dst_z: GLint,
        src_width: GLsizei,
        src_height: GLsizei,
        src_depth: GLsizei,
    );
    fn CopyTexSubImage2D(
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
    );
    fn BlitFramebuffer(
        src_x0: GLint,
        src_y0: GLint,
        src_x1: GLint,
        src_y1: GLint,
        dst_x0: GLint,
        dst_y0: GLint,
        dst_x1: GLint,
        dst_y1: GLint,
        mask: GLbitfield,
        filter: GLenum,
    );
    fn GetIntegerv(pname: GLenum, params: *mut GLint);
    fn GetBooleanv(pname: GLenum, params: *mut GLboolean);
    fn GetString(name: GLenum) -> *const c_char;
    fn GetStringi(name: GLenum, index: GLuint) -> *const c_char;
    fn GetError() -> GLenum;
    fn InitDefaultFramebuffer(
        x: i32,
        y: i32,
        width: i32,
        height: i32,
        stride: i32,
        buf: *mut c_void,
    );
    fn GetColorBuffer(
        fbo: GLuint,
        flush: GLboolean,
        width: *mut i32,
        height: *mut i32,
        stride: *mut i32,
    ) -> *mut c_void;
    fn ResolveFramebuffer(fbo: GLuint);
    fn SetTextureBuffer(
        tex: GLuint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
        stride: GLsizei,
        buf: *mut c_void,
        min_width: GLsizei,
        min_height: GLsizei,
    );
    fn SetTextureParameter(tex: GLuint, pname: GLenum, param: GLint);
    fn DeleteTexture(n: GLuint);
    fn DeleteRenderbuffer(n: GLuint);
    fn DeleteFramebuffer(n: GLuint);
    fn DeleteBuffer(n: GLuint);
    fn DeleteVertexArray(n: GLuint);
    fn DeleteQuery(n: GLuint);
    fn DeleteShader(shader: GLuint);
    fn DeleteProgram(program: GLuint);
    fn LockFramebuffer(fbo: GLuint) -> *mut LockedTexture;
    fn LockTexture(tex: GLuint) -> *mut LockedTexture;
    fn LockResource(resource: *mut LockedTexture);
    fn UnlockResource(resource: *mut LockedTexture) -> i32;
    fn GetResourceBuffer(
        resource: *mut LockedTexture,
        width: *mut i32,
        height: *mut i32,
        stride: *mut i32,
    ) -> *mut c_void;
    fn Composite(
        locked_dst: *mut LockedTexture,
        locked_src: *mut LockedTexture,
        src_x: GLint,
        src_y: GLint,
        src_width: GLsizei,
        src_height: GLsizei,
        dst_x: GLint,
        dst_y: GLint,
        dst_width: GLsizei,
        dst_height: GLsizei,
        opaque: GLboolean,
        flip_x: GLboolean,
        flip_y: GLboolean,
        filter: GLenum,
        clip_x: GLint,
        clip_y: GLint,
        clip_width: GLsizei,
        clip_height: GLsizei,
    );
    fn CompositeYUV(
        locked_dst: *mut LockedTexture,
        locked_y: *mut LockedTexture,
        locked_u: *mut LockedTexture,
        locked_v: *mut LockedTexture,
        color_space: YuvRangedColorSpace,
        color_depth: GLuint,
        src_x: GLint,
        src_y: GLint,
        src_width: GLsizei,
        src_height: GLsizei,
        dst_x: GLint,
        dst_y: GLint,
        dst_width: GLsizei,
        dst_height: GLsizei,
        flip_x: GLboolean,
        flip_y: GLboolean,
        clip_x: GLint,
        clip_y: GLint,
        clip_width: GLsizei,
        clip_height: GLsizei,
    );
    fn ApplyMask(
        locked_dst: *mut LockedTexture,
        locked_mask: *mut LockedTexture,
    );
    fn CreateContext() -> *mut c_void;
    fn ReferenceContext(ctx: *mut c_void);
    fn DestroyContext(ctx: *mut c_void);
    fn MakeCurrent(ctx: *mut c_void);
    fn ReportMemory(ctx: *mut c_void, size_of_op: unsafe extern "C" fn(ptr: *const c_void) -> usize) -> usize;
}

#[derive(Clone, Copy)]
pub struct Context(*mut c_void);

impl Context {
    pub fn create() -> Self {
        Context(unsafe { CreateContext() })
    }

    pub fn reference(&self) {
        unsafe {
            ReferenceContext(self.0);
        }
    }

    pub fn destroy(&self) {
        unsafe {
            DestroyContext(self.0);
        }
    }

    pub fn make_current(&self) {
        unsafe {
            MakeCurrent(self.0);
        }
    }

    pub fn init_default_framebuffer(
        &self,
        x: i32,
        y: i32,
        width: i32,
        height: i32,
        stride: i32,
        buf: *mut c_void,
    ) {
        unsafe {
            InitDefaultFramebuffer(x, y, width, height, stride, buf);
        }
    }

    pub fn get_color_buffer(&self, fbo: GLuint, flush: bool) -> (*mut c_void, i32, i32, i32) {
        unsafe {
            let mut width: i32 = 0;
            let mut height: i32 = 0;
            let mut stride: i32 = 0;
            let data_ptr = GetColorBuffer(
                fbo,
                flush as GLboolean,
                &mut width,
                &mut height,
                &mut stride,
            );
            (data_ptr, width, height, stride)
        }
    }

    pub fn resolve_framebuffer(&self, fbo: GLuint) {
        unsafe {
            ResolveFramebuffer(fbo);
        }
    }

    pub fn clear_color_rect(
        &self,
        fbo: GLuint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        r: f32,
        g: f32,
        b: f32,
        a: f32,
    ) {
        unsafe {
            ClearColorRect(fbo, xoffset, yoffset, width, height, r, g, b, a);
        }
    }

    pub fn set_texture_buffer(
        &self,
        tex: GLuint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
        stride: GLsizei,
        buf: *mut c_void,
        min_width: GLsizei,
        min_height: GLsizei,
    ) {
        unsafe {
            SetTextureBuffer(
                tex,
                internal_format,
                width,
                height,
                stride,
                buf,
                min_width,
                min_height,
            );
        }
    }

    pub fn set_texture_parameter(&self, tex: GLuint, pname: GLenum, param: GLint) {
        unsafe {
            SetTextureParameter(tex, pname, param);
        }
    }

    pub fn lock_framebuffer(&self, fbo: GLuint) -> Option<LockedResource> {
        unsafe {
            let resource = LockFramebuffer(fbo);
            if !resource.is_null() {
                Some(LockedResource(resource))
            } else {
                None
            }
        }
    }

    pub fn lock_texture(&self, tex: GLuint) -> Option<LockedResource> {
        unsafe {
            let resource = LockTexture(tex);
            if !resource.is_null() {
                Some(LockedResource(resource))
            } else {
                None
            }
        }
    }

    pub fn report_memory(&self, size_of_op: unsafe extern "C" fn(ptr: *const c_void) -> usize) -> usize {
        unsafe { ReportMemory(self.0, size_of_op) }
    }
}

impl From<*mut c_void> for Context {
    fn from(ptr: *mut c_void) -> Self {
        Context(ptr)
    }
}

impl From<Context> for *mut c_void {
    fn from(ctx: Context) -> Self {
        ctx.0
    }
}

fn calculate_length(width: GLsizei, height: GLsizei, format: GLenum, pixel_type: GLenum) -> usize {
    let colors = match format {
        RED => 1,
        RGB => 3,
        BGR => 3,

        RGBA => 4,
        BGRA => 4,

        ALPHA => 1,
        R16 => 1,
        LUMINANCE => 1,
        DEPTH_COMPONENT => 1,
        _ => panic!("unsupported format for read_pixels: {:?}", format),
    };
    let depth = match pixel_type {
        UNSIGNED_BYTE => 1,
        UNSIGNED_SHORT => 2,
        SHORT => 2,
        FLOAT => 4,
        UNSIGNED_INT_8_8_8_8_REV => 1,
        _ => panic!("unsupported pixel_type for read_pixels: {:?}", pixel_type),
    };

    (width * height * colors * depth) as usize
}

impl Gl for Context {
    fn get_type(&self) -> GlType {
        GlType::Gl
    }

    fn buffer_data_untyped(
        &self,
        target: GLenum,
        size: GLsizeiptr,
        data: *const GLvoid,
        usage: GLenum,
    ) {
        debug!(
            "buffer_data_untyped {} {} {:?} {}",
            target, size, data, usage
        );
        //panic!();
        unsafe {
            BufferData(target, size, data, usage);
        }
    }

    fn buffer_sub_data_untyped(
        &self,
        target: GLenum,
        offset: isize,
        size: GLsizeiptr,
        data: *const GLvoid,
    ) {
        debug!(
            "buffer_sub_data_untyped {} {} {} {:?}",
            target, offset, size, data
        );
        //panic!();
        unsafe {
            BufferSubData(target, offset, size, data);
        }
    }

    fn map_buffer(&self, target: GLenum, access: GLbitfield) -> *mut c_void {
        unsafe { MapBuffer(target, access) }
    }

    fn map_buffer_range(
        &self,
        target: GLenum,
        offset: GLintptr,
        length: GLsizeiptr,
        access: GLbitfield,
    ) -> *mut c_void {
        unsafe { MapBufferRange(target, offset, length, access) }
    }

    fn unmap_buffer(&self, target: GLenum) -> GLboolean {
        unsafe { UnmapBuffer(target) }
    }

    fn shader_source(&self, shader: GLuint, strings: &[&[u8]]) {
        //panic!();
        debug!("shader_source {}", shader);
        //for s in strings {
        //    debug!("{}", str::from_utf8(s).unwrap());
        //}
        //panic!();
        for s in strings {
            let u = str::from_utf8(s).unwrap();
            const PREFIX: &str = "// shader: ";
            if let Some(start) = u.find(PREFIX) {
                if let Some(end) = u[start..].find('\n') {
                    let name = u[start + PREFIX.len()..start + end].trim();
                    debug!("shader name: {}", name);
                    unsafe {
                        let c_string = CString::new(name).unwrap();
                        ShaderSourceByName(shader, c_string.as_ptr());
                        return;
                    }
                }
            }
        }
        panic!("unknown shader");
    }

    fn tex_buffer(&self, target: GLenum, internal_format: GLenum, buffer: GLuint) {
        panic!();
    }

    fn read_buffer(&self, mode: GLenum) {
        panic!();
    }

    fn read_pixels_into_buffer(
        &self,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        pixel_type: GLenum,
        dst_buffer: &mut [u8],
    ) {
        // Assumes that the user properly allocated the size for dst_buffer.
        assert!(calculate_length(width, height, format, pixel_type) == dst_buffer.len());

        unsafe {
            ReadPixels(
                x,
                y,
                width,
                height,
                format,
                pixel_type,
                dst_buffer.as_mut_ptr() as *mut c_void,
            );
        }
    }

    fn read_pixels(
        &self,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        pixel_type: GLenum,
    ) -> Vec<u8> {
        let len = calculate_length(width, height, format, pixel_type);
        let mut pixels: Vec<u8> = Vec::new();
        pixels.reserve(len);
        unsafe {
            pixels.set_len(len);
        }

        self.read_pixels_into_buffer(
            x,
            y,
            width,
            height,
            format,
            pixel_type,
            pixels.as_mut_slice(),
        );

        pixels
    }

    unsafe fn read_pixels_into_pbo(
        &self,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        pixel_type: GLenum,
    ) {
        ReadPixels(x, y, width, height, format, pixel_type, ptr::null_mut());
    }

    fn sample_coverage(&self, value: GLclampf, invert: bool) {
        panic!();
    }

    fn polygon_offset(&self, factor: GLfloat, units: GLfloat) {
        panic!();
    }

    fn pixel_store_i(&self, name: GLenum, param: GLint) {
        //panic!();
        debug!("pixel_store_i {:x} {}", name, param);
        unsafe {
            PixelStorei(name, param);
        }
    }

    fn gen_buffers(&self, n: GLsizei) -> Vec<GLuint> {
        //panic!();
        let mut result = vec![0 as GLuint; n as usize];
        unsafe {
            GenBuffers(n, result.as_mut_ptr());
        }
        result
    }

    fn gen_renderbuffers(&self, n: GLsizei) -> Vec<GLuint> {
        debug!("gen_renderbuffers {}", n);
        //panic!();
        let mut result = vec![0 as GLuint; n as usize];
        unsafe {
            GenRenderbuffers(n, result.as_mut_ptr());
        }
        result
    }

    fn gen_framebuffers(&self, n: GLsizei) -> Vec<GLuint> {
        //panic!();
        debug!("gen_framebuffers {}", n);
        let mut result = vec![0 as GLuint; n as usize];
        unsafe {
            GenFramebuffers(n, result.as_mut_ptr());
        }
        result
    }

    fn gen_textures(&self, n: GLsizei) -> Vec<GLuint> {
        //panic!();
        let mut result = vec![0 as GLuint; n as usize];
        unsafe {
            GenTextures(n, result.as_mut_ptr());
        }
        result
    }

    fn gen_vertex_arrays(&self, n: GLsizei) -> Vec<GLuint> {
        //panic!();
        let mut result = vec![0 as GLuint; n as usize];
        unsafe {
            GenVertexArrays(n, result.as_mut_ptr());
        }
        result
    }

    fn gen_vertex_arrays_apple(&self, n: GLsizei) -> Vec<GLuint> {
        self.gen_vertex_arrays(n)
    }

    fn gen_queries(&self, n: GLsizei) -> Vec<GLuint> {
        let mut result = vec![0 as GLuint; n as usize];
        unsafe {
            GenQueries(n, result.as_mut_ptr());
        }
        result
    }

    fn begin_query(&self, target: GLenum, id: GLuint) {
        unsafe {
            BeginQuery(target, id);
        }
    }

    fn end_query(&self, target: GLenum) {
        unsafe {
            EndQuery(target);
        }
    }

    fn query_counter(&self, id: GLuint, target: GLenum) {
        panic!();
    }

    fn get_query_object_iv(&self, id: GLuint, pname: GLenum) -> i32 {
        panic!();
        //0
    }

    fn get_query_object_uiv(&self, id: GLuint, pname: GLenum) -> u32 {
        panic!();
        //0
    }

    fn get_query_object_i64v(&self, id: GLuint, pname: GLenum) -> i64 {
        panic!();
        //0
    }

    fn get_query_object_ui64v(&self, id: GLuint, pname: GLenum) -> u64 {
        let mut result = 0;
        unsafe {
            GetQueryObjectui64v(id, pname, &mut result);
        }
        result
    }

    fn delete_queries(&self, queries: &[GLuint]) {
        unsafe {
            for q in queries {
                DeleteQuery(*q);
            }
        }
    }

    fn delete_vertex_arrays(&self, vertex_arrays: &[GLuint]) {
        unsafe {
            for v in vertex_arrays {
                DeleteVertexArray(*v);
            }
        }
    }

    fn delete_vertex_arrays_apple(&self, vertex_arrays: &[GLuint]) {
        self.delete_vertex_arrays(vertex_arrays)
    }

    fn delete_buffers(&self, buffers: &[GLuint]) {
        unsafe {
            for b in buffers {
                DeleteBuffer(*b);
            }
        }
    }

    fn delete_renderbuffers(&self, renderbuffers: &[GLuint]) {
        unsafe {
            for r in renderbuffers {
                DeleteRenderbuffer(*r);
            }
        }
    }

    fn delete_framebuffers(&self, framebuffers: &[GLuint]) {
        unsafe {
            for f in framebuffers {
                DeleteFramebuffer(*f);
            }
        }
    }

    fn delete_textures(&self, textures: &[GLuint]) {
        unsafe {
            for t in textures {
                DeleteTexture(*t);
            }
        }
    }

    fn framebuffer_renderbuffer(
        &self,
        target: GLenum,
        attachment: GLenum,
        renderbuffertarget: GLenum,
        renderbuffer: GLuint,
    ) {
        debug!(
            "framebufer_renderbuffer {} {} {} {}",
            target, attachment, renderbuffertarget, renderbuffer
        );
        //panic!();
        unsafe {
            FramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
        }
    }

    fn renderbuffer_storage(
        &self,
        target: GLenum,
        internalformat: GLenum,
        width: GLsizei,
        height: GLsizei,
    ) {
        debug!(
            "renderbuffer_storage {} {} {} {}",
            target, internalformat, width, height
        );
        //panic!();
        unsafe {
            RenderbufferStorage(target, internalformat, width, height);
        }
    }

    fn depth_func(&self, func: GLenum) {
        debug!("depth_func {}", func);
        //panic!();
        unsafe {
            DepthFunc(func);
        }
    }

    fn active_texture(&self, texture: GLenum) {
        //panic!();
        unsafe {
            ActiveTexture(texture);
        }
    }

    fn attach_shader(&self, program: GLuint, shader: GLuint) {
        debug!("attach shader {} {}", program, shader);
        //panic!();
        unsafe {
            AttachShader(program, shader);
        }
    }

    fn bind_attrib_location(&self, program: GLuint, index: GLuint, name: &str) {
        debug!("bind_attrib_location {} {} {}", program, index, name);
        //panic!();
        let c_string = CString::new(name).unwrap();
        unsafe { BindAttribLocation(program, index, c_string.as_ptr()) }
    }

    // https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetUniform.xml
    unsafe fn get_uniform_iv(&self, program: GLuint, location: GLint, result: &mut [GLint]) {
        panic!();
        //assert!(!result.is_empty());
    }

    // https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetUniform.xml
    unsafe fn get_uniform_fv(&self, program: GLuint, location: GLint, result: &mut [GLfloat]) {
        panic!();
        //assert!(!result.is_empty());
    }

    fn get_uniform_block_index(&self, program: GLuint, name: &str) -> GLuint {
        panic!();
        //0
    }

    fn get_uniform_indices(&self, program: GLuint, names: &[&str]) -> Vec<GLuint> {
        panic!();
        //Vec::new()
    }

    fn bind_buffer_base(&self, target: GLenum, index: GLuint, buffer: GLuint) {
        panic!();
    }

    fn bind_buffer_range(
        &self,
        target: GLenum,
        index: GLuint,
        buffer: GLuint,
        offset: GLintptr,
        size: GLsizeiptr,
    ) {
        panic!();
    }

    fn uniform_block_binding(
        &self,
        program: GLuint,
        uniform_block_index: GLuint,
        uniform_block_binding: GLuint,
    ) {
        panic!();
    }

    fn bind_buffer(&self, target: GLenum, buffer: GLuint) {
        //panic!();
        unsafe {
            BindBuffer(target, buffer);
        }
    }

    fn bind_vertex_array(&self, vao: GLuint) {
        //panic!();
        unsafe {
            BindVertexArray(vao);
        }
    }

    fn bind_vertex_array_apple(&self, vao: GLuint) {
        self.bind_vertex_array(vao)
    }

    fn bind_renderbuffer(&self, target: GLenum, renderbuffer: GLuint) {
        debug!("bind_renderbuffer {} {}", target, renderbuffer);
        //panic!();
        unsafe {
            BindRenderbuffer(target, renderbuffer);
        }
    }

    fn bind_framebuffer(&self, target: GLenum, framebuffer: GLuint) {
        debug!("bind_framebuffer {} {}", target, framebuffer);
        //panic!();
        unsafe {
            BindFramebuffer(target, framebuffer);
        }
    }

    fn bind_vertex_buffer(
        &self,
        binding_index: GLuint,
        buffer: GLuint,
        offset: GLintptr,
        stride: GLint,
    ) {
        unimplemented!("Not supported by SWGL");
    }

    fn bind_texture(&self, target: GLenum, texture: GLuint) {
        //panic!();
        unsafe {
            BindTexture(target, texture);
        }
    }

    fn draw_buffers(&self, bufs: &[GLenum]) {
        panic!();
        //unsafe {}
    }

    // FIXME: Does not verify buffer size -- unsafe!
    fn tex_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        internal_format: GLint,
        width: GLsizei,
        height: GLsizei,
        border: GLint,
        format: GLenum,
        ty: GLenum,
        opt_data: Option<&[u8]>,
    ) {
        unsafe {
            let pdata = match opt_data {
                Some(data) => data.as_ptr() as *const GLvoid,
                None => ptr::null(),
            };
            TexImage2D(
                target,
                level,
                internal_format,
                width,
                height,
                border,
                format,
                ty,
                pdata,
            );
        }
    }

    fn compressed_tex_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
        border: GLint,
        data: &[u8],
    ) {
        panic!();
    }

    fn compressed_tex_sub_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        data: &[u8],
    ) {
        panic!();
    }

    fn tex_image_3d(
        &self,
        target: GLenum,
        level: GLint,
        internal_format: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        border: GLint,
        format: GLenum,
        ty: GLenum,
        opt_data: Option<&[u8]>,
    ) {
        panic!();
    }

    fn copy_tex_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        internal_format: GLenum,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        border: GLint,
    ) {
        panic!();
    }

    fn copy_tex_sub_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
    ) {
        unsafe {
            CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
        }
    }

    fn copy_tex_sub_image_3d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        zoffset: GLint,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
    ) {
        panic!();
    }

    fn tex_sub_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        ty: GLenum,
        data: &[u8],
    ) {
        debug!(
            "tex_sub_image_2d {} {} {} {} {} {} {} {}",
            target, level, xoffset, yoffset, width, height, format, ty
        );
        //panic!();
        unsafe {
            TexSubImage2D(
                target,
                level,
                xoffset,
                yoffset,
                width,
                height,
                format,
                ty,
                data.as_ptr() as *const c_void,
            );
        }
    }

    fn tex_sub_image_2d_pbo(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        ty: GLenum,
        offset: usize,
    ) {
        debug!(
            "tex_sub_image_2d_pbo {} {} {} {} {} {} {} {} {}",
            target, level, xoffset, yoffset, width, height, format, ty, offset
        );
        //panic!();
        unsafe {
            TexSubImage2D(
                target,
                level,
                xoffset,
                yoffset,
                width,
                height,
                format,
                ty,
                offset as *const c_void,
            );
        }
    }

    fn tex_sub_image_3d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        zoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        format: GLenum,
        ty: GLenum,
        data: &[u8],
    ) {
        debug!("tex_sub_image_3d");
        panic!();
    }

    fn tex_sub_image_3d_pbo(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        zoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        format: GLenum,
        ty: GLenum,
        offset: usize,
    ) {
        panic!();
    }

    fn tex_storage_2d(
        &self,
        target: GLenum,
        levels: GLint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
    ) {
        //panic!();
        unsafe {
            TexStorage2D(target, levels, internal_format, width, height);
        }
    }

    fn tex_storage_3d(
        &self,
        target: GLenum,
        levels: GLint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
    ) {
        panic!();
    }

    fn get_tex_image_into_buffer(
        &self,
        target: GLenum,
        level: GLint,
        format: GLenum,
        ty: GLenum,
        output: &mut [u8],
    ) {
        panic!();
    }

    unsafe fn copy_image_sub_data(
        &self,
        src_name: GLuint,
        src_target: GLenum,
        src_level: GLint,
        src_x: GLint,
        src_y: GLint,
        src_z: GLint,
        dst_name: GLuint,
        dst_target: GLenum,
        dst_level: GLint,
        dst_x: GLint,
        dst_y: GLint,
        dst_z: GLint,
        src_width: GLsizei,
        src_height: GLsizei,
        src_depth: GLsizei,
    ) {
        CopyImageSubData(
            src_name, src_target, src_level, src_x, src_y, src_z, dst_name, dst_target, dst_level,
            dst_x, dst_y, dst_z, src_width, src_height, src_depth,
        );
    }

    fn invalidate_framebuffer(&self, target: GLenum, attachments: &[GLenum]) {
        unsafe {
            InvalidateFramebuffer(target, attachments.len() as GLsizei, attachments.as_ptr());
        }
    }

    fn invalidate_sub_framebuffer(
        &self,
        target: GLenum,
        attachments: &[GLenum],
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
    ) {
    }

    #[inline]
    unsafe fn get_integer_v(&self, name: GLenum, result: &mut [GLint]) {
        //panic!();
        assert!(!result.is_empty());
        GetIntegerv(name, result.as_mut_ptr());
    }

    #[inline]
    unsafe fn get_integer_64v(&self, name: GLenum, result: &mut [GLint64]) {
        panic!();
        //assert!(!result.is_empty());
    }

    #[inline]
    unsafe fn get_integer_iv(&self, name: GLenum, index: GLuint, result: &mut [GLint]) {
        panic!();
        //assert!(!result.is_empty());
    }

    #[inline]
    unsafe fn get_integer_64iv(&self, name: GLenum, index: GLuint, result: &mut [GLint64]) {
        panic!();
        //assert!(!result.is_empty());
    }

    #[inline]
    unsafe fn get_boolean_v(&self, name: GLenum, result: &mut [GLboolean]) {
        debug!("get_boolean_v {}", name);
        //panic!();
        assert!(!result.is_empty());
        GetBooleanv(name, result.as_mut_ptr());
    }

    #[inline]
    unsafe fn get_float_v(&self, name: GLenum, result: &mut [GLfloat]) {
        panic!();
        //assert!(!result.is_empty());
    }

    fn get_framebuffer_attachment_parameter_iv(
        &self,
        target: GLenum,
        attachment: GLenum,
        pname: GLenum,
    ) -> GLint {
        panic!();
        //0
    }

    fn get_renderbuffer_parameter_iv(&self, target: GLenum, pname: GLenum) -> GLint {
        panic!();
        //0
    }

    fn get_tex_parameter_iv(&self, target: GLenum, pname: GLenum) -> GLint {
        panic!();
        //0
    }

    fn get_tex_parameter_fv(&self, target: GLenum, pname: GLenum) -> GLfloat {
        panic!();
        //0.0
    }

    fn tex_parameter_i(&self, target: GLenum, pname: GLenum, param: GLint) {
        //panic!();
        unsafe {
            TexParameteri(target, pname, param);
        }
    }

    fn tex_parameter_f(&self, target: GLenum, pname: GLenum, param: GLfloat) {
        panic!();
    }

    fn framebuffer_texture_2d(
        &self,
        target: GLenum,
        attachment: GLenum,
        textarget: GLenum,
        texture: GLuint,
        level: GLint,
    ) {
        debug!(
            "framebuffer_texture_2d {} {} {} {} {}",
            target, attachment, textarget, texture, level
        );
        //panic!();
        unsafe {
            FramebufferTexture2D(target, attachment, textarget, texture, level);
        }
    }

    fn framebuffer_texture_layer(
        &self,
        target: GLenum,
        attachment: GLenum,
        texture: GLuint,
        level: GLint,
        layer: GLint,
    ) {
        debug!(
            "framebuffer_texture_layer {} {} {} {} {}",
            target, attachment, texture, level, layer
        );
        panic!();
    }

    fn blit_framebuffer(
        &self,
        src_x0: GLint,
        src_y0: GLint,
        src_x1: GLint,
        src_y1: GLint,
        dst_x0: GLint,
        dst_y0: GLint,
        dst_x1: GLint,
        dst_y1: GLint,
        mask: GLbitfield,
        filter: GLenum,
    ) {
        unsafe {
            BlitFramebuffer(
                src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
            );
        }
    }

    fn vertex_attrib_4f(&self, index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {
        panic!();
    }

    fn vertex_attrib_binding(&self, attrib_index: GLuint, binding_index: GLuint) {
        unimplemented!("Not supported by SWGL");
    }

    fn vertex_attrib_pointer_f32(
        &self,
        index: GLuint,
        size: GLint,
        normalized: bool,
        stride: GLsizei,
        offset: GLuint,
    ) {
        panic!();
    }

    fn vertex_attrib_pointer(
        &self,
        index: GLuint,
        size: GLint,
        type_: GLenum,
        normalized: bool,
        stride: GLsizei,
        offset: GLuint,
    ) {
        debug!(
            "vertex_attrib_pointer {} {} {} {} {} {}",
            index, size, type_, normalized, stride, offset
        );
        //panic!();
        unsafe {
            VertexAttribPointer(
                index,
                size,
                type_,
                normalized as GLboolean,
                stride,
                offset as *const GLvoid,
            );
        }
    }

    fn vertex_attrib_i_pointer(
        &self,
        index: GLuint,
        size: GLint,
        type_: GLenum,
        stride: GLsizei,
        offset: GLuint,
    ) {
        debug!(
            "vertex_attrib_i_pointer {} {} {} {} {}",
            index, size, type_, stride, offset
        );
        //panic!();
        unsafe {
            VertexAttribIPointer(index, size, type_, stride, offset as *const GLvoid);
        }
    }

    fn vertex_attrib_divisor(&self, index: GLuint, divisor: GLuint) {
        debug!("vertex_attrib_divisor {} {}", index, divisor);
        //assert!(index == 0 && divisor == 0);
        //panic!();
        unsafe {
            VertexAttribDivisor(index, divisor);
        }
    }

    fn vertex_attrib_format(
        &self,
        attrib_index: GLuint,
        size: GLint,
        type_: GLenum,
        normalized: bool,
        relative_offset: GLuint,
    ) {
        unimplemented!("Not supported by SWGL");
    }

    fn vertex_attrib_i_format(
        &self,
        attrib_index: GLuint,
        size: GLint,
        type_: GLenum,
        relative_offset: GLuint,
    ) {
        unimplemented!("Not supported by SWGL");
    }

    fn vertex_binding_divisor(&self, binding_index: GLuint, divisor: GLuint) {
        unimplemented!("Not supported by SWGL");
    }

    fn viewport(&self, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {
        debug!("viewport {} {} {} {}", x, y, width, height);
        //panic!();
        unsafe {
            SetViewport(x, y, width, height);
        }
    }

    fn scissor(&self, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {
        //panic!();
        unsafe {
            SetScissor(x, y, width, height);
        }
    }

    fn line_width(&self, width: GLfloat) {
        panic!();
    }

    fn use_program(&self, program: GLuint) {
        //panic!();
        unsafe {
            UseProgram(program);
        }
    }

    fn validate_program(&self, program: GLuint) {
        panic!();
    }

    fn draw_arrays(&self, mode: GLenum, first: GLint, count: GLsizei) {
        unsafe {
            DrawElementsInstanced(mode, count, NONE, first as GLintptr, 1);
        }
    }

    fn draw_arrays_instanced(
        &self,
        mode: GLenum,
        first: GLint,
        count: GLsizei,
        primcount: GLsizei,
    ) {
        unsafe {
            DrawElementsInstanced(mode, count, NONE, first as GLintptr, primcount);
        }
    }

    fn draw_elements(
        &self,
        mode: GLenum,
        count: GLsizei,
        element_type: GLenum,
        indices_offset: GLuint,
    ) {
        debug!(
            "draw_elements {} {} {} {} {}",
            mode, count, element_type, indices_offset
        );
        //panic!();
        unsafe {
            DrawElementsInstanced(mode, count, element_type, indices_offset as GLintptr, 1);
        }
    }

    fn draw_elements_instanced(
        &self,
        mode: GLenum,
        count: GLsizei,
        element_type: GLenum,
        indices_offset: GLuint,
        primcount: GLsizei,
    ) {
        debug!(
            "draw_elements_instanced {} {} {} {} {}",
            mode, count, element_type, indices_offset, primcount
        );
        //panic!();
        unsafe {
            DrawElementsInstanced(
                mode,
                count,
                element_type,
                indices_offset as GLintptr,
                primcount,
            );
        }
    }

    fn blend_color(&self, r: f32, g: f32, b: f32, a: f32) {
        unsafe {
            BlendColor(r, g, b, a);
        }
    }

    fn blend_func(&self, sfactor: GLenum, dfactor: GLenum) {
        unsafe {
            BlendFunc(sfactor, dfactor, sfactor, dfactor);
        }
    }

    fn blend_func_separate(
        &self,
        src_rgb: GLenum,
        dest_rgb: GLenum,
        src_alpha: GLenum,
        dest_alpha: GLenum,
    ) {
        unsafe {
            BlendFunc(src_rgb, dest_rgb, src_alpha, dest_alpha);
        }
    }

    fn blend_equation(&self, mode: GLenum) {
        unsafe {
            BlendEquation(mode);
        }
    }

    fn blend_equation_separate(&self, mode_rgb: GLenum, mode_alpha: GLenum) {
        panic!();
    }

    fn color_mask(&self, r: bool, g: bool, b: bool, a: bool) {
        panic!();
    }

    fn cull_face(&self, mode: GLenum) {
        panic!();
    }

    fn front_face(&self, mode: GLenum) {
        panic!();
    }

    fn enable(&self, cap: GLenum) {
        debug!("enable {}", cap);
        //panic!();
        unsafe {
            Enable(cap);
        }
    }

    fn disable(&self, cap: GLenum) {
        debug!("disable {}", cap);
        //panic!();
        unsafe {
            Disable(cap);
        }
    }

    fn hint(&self, param_name: GLenum, param_val: GLenum) {
        panic!();
    }

    fn is_enabled(&self, cap: GLenum) -> GLboolean {
        panic!();
        //0
    }

    fn is_shader(&self, shader: GLuint) -> GLboolean {
        panic!();
        //0
    }

    fn is_texture(&self, texture: GLenum) -> GLboolean {
        panic!();
        //0
    }

    fn is_framebuffer(&self, framebuffer: GLenum) -> GLboolean {
        panic!();
        //0
    }

    fn is_renderbuffer(&self, renderbuffer: GLenum) -> GLboolean {
        panic!();
        //0
    }

    fn check_frame_buffer_status(&self, target: GLenum) -> GLenum {
        debug!("check_frame_buffer_status {}", target);
        //panic!();
        unsafe { CheckFramebufferStatus(target) }
    }

    fn enable_vertex_attrib_array(&self, index: GLuint) {
        //panic!();
        debug!("enable_vertex_attrib_array {}", index);
        unsafe {
            EnableVertexAttribArray(index);
            //assert_eq!(index, 0);
        }
    }

    fn disable_vertex_attrib_array(&self, index: GLuint) {
        panic!();
    }

    fn uniform_1f(&self, location: GLint, v0: GLfloat) {
        panic!();
    }

    fn uniform_1fv(&self, location: GLint, values: &[f32]) {
        panic!();
    }

    fn uniform_1i(&self, location: GLint, v0: GLint) {
        debug!("uniform_1i {} {}", location, v0);
        //panic!();
        unsafe {
            Uniform1i(location, v0);
        }
    }

    fn uniform_1iv(&self, location: GLint, values: &[i32]) {
        panic!();
    }

    fn uniform_1ui(&self, location: GLint, v0: GLuint) {
        panic!();
    }

    fn uniform_2f(&self, location: GLint, v0: GLfloat, v1: GLfloat) {
        panic!();
    }

    fn uniform_2fv(&self, location: GLint, values: &[f32]) {
        panic!();
    }

    fn uniform_2i(&self, location: GLint, v0: GLint, v1: GLint) {
        panic!();
    }

    fn uniform_2iv(&self, location: GLint, values: &[i32]) {
        panic!();
    }

    fn uniform_2ui(&self, location: GLint, v0: GLuint, v1: GLuint) {
        panic!();
    }

    fn uniform_3f(&self, location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat) {
        panic!();
    }

    fn uniform_3fv(&self, location: GLint, values: &[f32]) {
        panic!();
    }

    fn uniform_3i(&self, location: GLint, v0: GLint, v1: GLint, v2: GLint) {
        panic!();
    }

    fn uniform_3iv(&self, location: GLint, values: &[i32]) {
        panic!();
    }

    fn uniform_3ui(&self, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint) {
        panic!();
    }

    fn uniform_4f(&self, location: GLint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {
        panic!();
    }

    fn uniform_4i(&self, location: GLint, x: GLint, y: GLint, z: GLint, w: GLint) {
        panic!();
    }

    fn uniform_4iv(&self, location: GLint, values: &[i32]) {
        panic!();
    }

    fn uniform_4ui(&self, location: GLint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) {
        panic!();
    }

    fn uniform_4fv(&self, location: GLint, values: &[f32]) {
        unsafe {
            Uniform4fv(location, (values.len() / 4) as GLsizei, values.as_ptr());
        }
    }

    fn uniform_matrix_2fv(&self, location: GLint, transpose: bool, value: &[f32]) {
        panic!();
    }

    fn uniform_matrix_3fv(&self, location: GLint, transpose: bool, value: &[f32]) {
        panic!();
    }

    fn uniform_matrix_4fv(&self, location: GLint, transpose: bool, value: &[f32]) {
        debug!("uniform_matrix_4fv {} {} {:?}", location, transpose, value);
        //panic!();
        unsafe {
            UniformMatrix4fv(
                location,
                (value.len() / 16) as GLsizei,
                transpose as GLboolean,
                value.as_ptr(),
            );
        }
    }

    fn depth_mask(&self, flag: bool) {
        debug!("depth_mask {}", flag);
        //panic!();
        unsafe {
            DepthMask(flag as GLboolean);
        }
    }

    fn depth_range(&self, near: f64, far: f64) {
        panic!();
    }

    fn get_active_attrib(&self, program: GLuint, index: GLuint) -> (i32, u32, String) {
        panic!();
        //(0, 0, String::new())
    }

    fn get_active_uniform(&self, program: GLuint, index: GLuint) -> (i32, u32, String) {
        panic!();
        //(0, 0, String::new())
    }

    fn get_active_uniforms_iv(
        &self,
        program: GLuint,
        indices: Vec<GLuint>,
        pname: GLenum,
    ) -> Vec<GLint> {
        panic!();
        //Vec::new()
    }

    fn get_active_uniform_block_i(&self, program: GLuint, index: GLuint, pname: GLenum) -> GLint {
        panic!();
        //0
    }

    fn get_active_uniform_block_iv(
        &self,
        program: GLuint,
        index: GLuint,
        pname: GLenum,
    ) -> Vec<GLint> {
        panic!();
        //Vec::new()
    }

    fn get_active_uniform_block_name(&self, program: GLuint, index: GLuint) -> String {
        panic!();
        //String::new()
    }

    fn get_attrib_location(&self, program: GLuint, name: &str) -> c_int {
        let name = CString::new(name).unwrap();
        unsafe { GetAttribLocation(program, name.as_ptr()) }
    }

    fn get_frag_data_location(&self, program: GLuint, name: &str) -> c_int {
        panic!();
        //0
    }

    fn get_uniform_location(&self, program: GLuint, name: &str) -> c_int {
        debug!("get_uniform_location {} {}", program, name);
        //panic!();
        let name = CString::new(name).unwrap();
        unsafe { GetUniformLocation(program, name.as_ptr()) }
    }

    fn get_program_info_log(&self, program: GLuint) -> String {
        debug!("get_program_info_log {}", program);
        String::new()
    }

    #[inline]
    unsafe fn get_program_iv(&self, program: GLuint, pname: GLenum, result: &mut [GLint]) {
        debug!("get_program_iv {}", pname);
        //panic!();
        assert!(!result.is_empty());
        //#define GL_LINK_STATUS                    0x8B82
        if pname == 0x8b82 {
            result[0] = GetLinkStatus(program);
        }
    }

    fn get_program_binary(&self, program: GLuint) -> (Vec<u8>, GLenum) {
        panic!();
        //(Vec::new(), NONE)
    }

    fn program_binary(&self, program: GLuint, format: GLenum, binary: &[u8]) {
        panic!();
    }

    fn program_parameter_i(&self, program: GLuint, pname: GLenum, value: GLint) {
        panic!();
    }

    #[inline]
    unsafe fn get_vertex_attrib_iv(&self, index: GLuint, pname: GLenum, result: &mut [GLint]) {
        panic!();
        //assert!(!result.is_empty());
    }

    #[inline]
    unsafe fn get_vertex_attrib_fv(&self, index: GLuint, pname: GLenum, result: &mut [GLfloat]) {
        panic!();
        //assert!(!result.is_empty());
    }

    fn get_vertex_attrib_pointer_v(&self, index: GLuint, pname: GLenum) -> GLsizeiptr {
        panic!();
        //0
    }

    fn get_buffer_parameter_iv(&self, target: GLuint, pname: GLenum) -> GLint {
        panic!();
        //0
    }

    fn get_shader_info_log(&self, shader: GLuint) -> String {
        debug!("get_shader_info_log {}", shader);
        //panic!();
        String::new()
    }

    fn get_string(&self, which: GLenum) -> String {
        // panic!();
        unsafe {
            let llstr = GetString(which);
            if !llstr.is_null() {
                str::from_utf8_unchecked(CStr::from_ptr(llstr).to_bytes()).to_string()
            } else {
                "".to_string()
            }
        }
    }

    fn get_string_i(&self, which: GLenum, index: GLuint) -> String {
        //panic!();
        unsafe {
            let llstr = GetStringi(which, index);
            if !llstr.is_null() {
                str::from_utf8_unchecked(CStr::from_ptr(llstr).to_bytes()).to_string()
            } else {
                "".to_string()
            }
        }
    }

    unsafe fn get_shader_iv(&self, shader: GLuint, pname: GLenum, result: &mut [GLint]) {
        debug!("get_shader_iv");
        //panic!();
        assert!(!result.is_empty());
        if pname == 0x8B81
        /*gl::COMPILE_STATUS*/
        {
            result[0] = 1;
        }
    }

    fn get_shader_precision_format(
        &self,
        _shader_type: GLuint,
        precision_type: GLuint,
    ) -> (GLint, GLint, GLint) {
        // gl.GetShaderPrecisionFormat is not available until OpenGL 4.1.
        // Fallback to OpenGL standard precissions that most desktop hardware support.
        match precision_type {
            LOW_FLOAT | MEDIUM_FLOAT | HIGH_FLOAT => {
                // Fallback to IEEE 754 single precision
                // Range: from -2^127 to 2^127
                // Significand precision: 23 bits
                (127, 127, 23)
            }
            LOW_INT | MEDIUM_INT | HIGH_INT => {
                // Fallback to single precision integer
                // Range: from -2^24 to 2^24
                // Precision: For integer formats this value is always 0
                (24, 24, 0)
            }
            _ => (0, 0, 0),
        }
    }

    fn compile_shader(&self, shader: GLuint) {
        debug!("compile_shader {}", shader);
        //panic!();
    }

    fn create_program(&self) -> GLuint {
        debug!("create_program");
        //panic!();
        unsafe { CreateProgram() }
    }

    fn delete_program(&self, program: GLuint) {
        unsafe {
            DeleteProgram(program);
        }
    }

    fn create_shader(&self, shader_type: GLenum) -> GLuint {
        debug!("create_shader {}", shader_type);
        //panic!();
        unsafe { CreateShader(shader_type) }
    }

    fn delete_shader(&self, shader: GLuint) {
        debug!("delete_shader {}", shader);
        //panic!();
        unsafe {
            DeleteShader(shader);
        }
    }

    fn detach_shader(&self, program: GLuint, shader: GLuint) {
        debug!("detach_shader {} {}", program, shader);
        //panic!();
    }

    fn link_program(&self, program: GLuint) {
        debug!("link_program {}", program);
        //panic!();
        unsafe {
            LinkProgram(program);
        }
    }

    fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
        //panic!();
        unsafe {
            ClearColor(r, g, b, a);
        }
    }

    fn clear(&self, buffer_mask: GLbitfield) {
        debug!("clear {}", buffer_mask);
        //panic!();
        unsafe {
            Clear(buffer_mask);
        }
    }

    fn clear_depth(&self, depth: f64) {
        debug!("clear_depth {}", depth);
        //panic!();
        unsafe {
            ClearDepth(depth as GLclampd);
        }
    }

    fn clear_stencil(&self, s: GLint) {
        panic!();
    }

    fn flush(&self) {}

    fn finish(&self) {
        unsafe {
            Finish();
        }
    }

    fn get_error(&self) -> GLenum {
        //panic!();
        unsafe { GetError() }
    }

    fn stencil_mask(&self, mask: GLuint) {
        panic!();
    }

    fn stencil_mask_separate(&self, face: GLenum, mask: GLuint) {
        panic!();
    }

    fn stencil_func(&self, func: GLenum, ref_: GLint, mask: GLuint) {
        panic!();
    }

    fn stencil_func_separate(&self, face: GLenum, func: GLenum, ref_: GLint, mask: GLuint) {
        panic!();
    }

    fn stencil_op(&self, sfail: GLenum, dpfail: GLenum, dppass: GLenum) {
        panic!();
    }

    fn stencil_op_separate(&self, face: GLenum, sfail: GLenum, dpfail: GLenum, dppass: GLenum) {
        panic!();
    }

    fn egl_image_target_texture2d_oes(&self, target: GLenum, image: GLeglImageOES) {
        panic!("not supported")
    }

    fn egl_image_target_renderbuffer_storage_oes(&self, target: GLenum, image: GLeglImageOES) {
        panic!("not supported")
    }

    fn generate_mipmap(&self, target: GLenum) {
        unsafe {
            GenerateMipmap(target);
        }
    }

    fn insert_event_marker_ext(&self, message: &str) {
        panic!();
    }

    fn push_group_marker_ext(&self, message: &str) {
        debug!("push group {}", message);
        panic!();
    }

    fn pop_group_marker_ext(&self) {
        debug!("pop group");
        panic!();
    }

    fn debug_message_insert_khr(
        &self,
        source: GLenum,
        type_: GLenum,
        id: GLuint,
        severity: GLenum,
        message: &str,
    ) {
        panic!();
    }

    fn push_debug_group_khr(&self, source: GLenum, id: GLuint, message: &str) {
        panic!();
    }

    fn pop_debug_group_khr(&self) {
        panic!();
    }

    fn fence_sync(&self, condition: GLenum, flags: GLbitfield) -> GLsync {
        panic!();
        //ptr::null()
    }

    fn client_wait_sync(&self, sync: GLsync, flags: GLbitfield, timeout: GLuint64) -> GLenum {
        panic!();
    }

    fn wait_sync(&self, sync: GLsync, flags: GLbitfield, timeout: GLuint64) {
        panic!();
    }

    fn texture_range_apple(&self, target: GLenum, data: &[u8]) {
        panic!();
    }

    fn delete_sync(&self, sync: GLsync) {
        panic!();
    }

    fn gen_fences_apple(&self, n: GLsizei) -> Vec<GLuint> {
        panic!();
        //Vec::new()
    }

    fn delete_fences_apple(&self, fences: &[GLuint]) {
        panic!();
    }

    fn set_fence_apple(&self, fence: GLuint) {
        panic!();
    }

    fn finish_fence_apple(&self, fence: GLuint) {
        panic!();
    }

    fn test_fence_apple(&self, fence: GLuint) {
        panic!();
    }

    fn test_object_apple(&self, object: GLenum, name: GLuint) -> GLboolean {
        panic!();
        //0
    }

    fn finish_object_apple(&self, object: GLenum, name: GLuint) {
        panic!();
    }

    // GL_ARB_blend_func_extended
    fn bind_frag_data_location_indexed(
        &self,
        program: GLuint,
        color_number: GLuint,
        index: GLuint,
        name: &str,
    ) {
        panic!();
    }

    fn get_frag_data_index(&self, program: GLuint, name: &str) -> GLint {
        panic!();
        //-1
    }

    // GL_KHR_debug
    fn get_debug_messages(&self) -> Vec<DebugMessage> {
        Vec::new()
    }

    fn provoking_vertex_angle(&self, _mode: GLenum) {
        unimplemented!("This extension is GLES only");
    }

    // GL_KHR_blend_equation_advanced
    fn blend_barrier_khr(&self) {
        // No barrier required, so nothing to do
    }

    // GL_CHROMIUM_copy_texture
    fn copy_texture_chromium(
        &self,
        _source_id: GLuint,
        _source_level: GLint,
        _dest_target: GLenum,
        _dest_id: GLuint,
        _dest_level: GLint,
        _internal_format: GLint,
        _dest_type: GLenum,
        _unpack_flip_y: GLboolean,
        _unpack_premultiply_alpha: GLboolean,
        _unpack_unmultiply_alpha: GLboolean,
    ) {
        unimplemented!("This extension is GLES only");
    }
    fn copy_sub_texture_chromium(
        &self,
        _source_id: GLuint,
        _source_level: GLint,
        _dest_target: GLenum,
        _dest_id: GLuint,
        _dest_level: GLint,
        _x_offset: GLint,
        _y_offset: GLint,
        _x: GLint,
        _y: GLint,
        _width: GLsizei,
        _height: GLsizei,
        _unpack_flip_y: GLboolean,
        _unpack_premultiply_alpha: GLboolean,
        _unpack_unmultiply_alpha: GLboolean,
    ) {
        unimplemented!("This extension is GLES only");
    }

    // GL_ANGLE_copy_texture_3d
    fn copy_texture_3d_angle(
        &self,
        _source_id: GLuint,
        _source_level: GLint,
        _dest_target: GLenum,
        _dest_id: GLuint,
        _dest_level: GLint,
        _internal_format: GLint,
        _dest_type: GLenum,
        _unpack_flip_y: GLboolean,
        _unpack_premultiply_alpha: GLboolean,
        _unpack_unmultiply_alpha: GLboolean,
    ) {
        unimplemented!("Not supported by SWGL");
    }

    fn copy_sub_texture_3d_angle(
        &self,
        _source_id: GLuint,
        _source_level: GLint,
        _dest_target: GLenum,
        _dest_id: GLuint,
        _dest_level: GLint,
        _x_offset: GLint,
        _y_offset: GLint,
        _z_offset: GLint,
        _x: GLint,
        _y: GLint,
        _z: GLint,
        _width: GLsizei,
        _height: GLsizei,
        _depth: GLsizei,
        _unpack_flip_y: GLboolean,
        _unpack_premultiply_alpha: GLboolean,
        _unpack_unmultiply_alpha: GLboolean,
    ) {
        unimplemented!("Not supported by SWGL");
    }

    fn buffer_storage(
        &self,
        target: GLenum,
        size: GLsizeiptr,
        data: *const GLvoid,
        flags: GLbitfield,
    ) {
        unimplemented!("Not supported by SWGL");
    }

    fn flush_mapped_buffer_range(&self, target: GLenum, offset: GLintptr, length: GLsizeiptr) {
        unimplemented!("Not supported by SWGL");
    }

    fn start_tiling_qcom(
        &self,
        x: GLuint,
        y: GLuint,
        width: GLuint,
        height: GLuint,
        preserve_mask: GLbitfield,
    ) {
        unimplemented!("Not supported by SWGL");
    }

    fn end_tiling_qcom(&self, preserve_mask: GLbitfield) {
        unimplemented!("Not supported by SWGL");
    }
}

/// A resource that is intended for sharing between threads.
/// Locked resources such as textures or framebuffers will
/// not allow any further modifications while it remains
/// locked. The resource will be unlocked when LockedResource
/// is dropped.
pub struct LockedResource(*mut LockedTexture);

unsafe impl Send for LockedResource {}
unsafe impl Sync for LockedResource {}

#[repr(u8)]
pub enum YuvRangedColorSpace {
    Rec601Narrow = 0,
    Rec601Full,
    Rec709Narrow,
    Rec709Full,
    Rec2020Narrow,
    Rec2020Full,
    GbrIdentity,
}

impl LockedResource {
    /// Composites from a locked resource to another locked resource. The band
    /// offset and height are relative to the destination rectangle and specify
    /// how to clip the composition into appropriate range for this band.
    pub fn composite(
        &self,
        locked_src: &LockedResource,
        src_x: GLint,
        src_y: GLint,
        src_width: GLsizei,
        src_height: GLsizei,
        dst_x: GLint,
        dst_y: GLint,
        dst_width: GLsizei,
        dst_height: GLsizei,
        opaque: bool,
        flip_x: bool,
        flip_y: bool,
        filter: GLenum,
        clip_x: GLint,
        clip_y: GLint,
        clip_width: GLsizei,
        clip_height: GLsizei,
    ) {
        unsafe {
            Composite(
                self.0,
                locked_src.0,
                src_x,
                src_y,
                src_width,
                src_height,
                dst_x,
                dst_y,
                dst_width,
                dst_height,
                opaque as GLboolean,
                flip_x as GLboolean,
                flip_y as GLboolean,
                filter,
                clip_x,
                clip_y,
                clip_width,
                clip_height,
            );
        }
    }

    /// Composites from locked resources representing YUV planes
    pub fn composite_yuv(
        &self,
        locked_y: &LockedResource,
        locked_u: &LockedResource,
        locked_v: &LockedResource,
        color_space: YuvRangedColorSpace,
        color_depth: GLuint,
        src_x: GLint,
        src_y: GLint,
        src_width: GLsizei,
        src_height: GLsizei,
        dst_x: GLint,
        dst_y: GLint,
        dst_width: GLsizei,
        dst_height: GLsizei,
        flip_x: bool,
        flip_y: bool,
        clip_x: GLint,
        clip_y: GLint,
        clip_width: GLsizei,
        clip_height: GLsizei,
    ) {
        unsafe {
            CompositeYUV(
                self.0,
                locked_y.0,
                locked_u.0,
                locked_v.0,
                color_space,
                color_depth,
                src_x,
                src_y,
                src_width,
                src_height,
                dst_x,
                dst_y,
                dst_width,
                dst_height,
                flip_x as GLboolean,
                flip_y as GLboolean,
                clip_x,
                clip_y,
                clip_width,
                clip_height,
            );
        }
    }

    /// Apply an R8 alpha mask to this surface
    pub fn apply_mask(
        &self,
        mask: &LockedResource,
    ) {
        unsafe {
            ApplyMask(
                self.0,
                mask.0,
            );
        }
    }

    /// Get the underlying buffer for a locked resource
    pub fn get_buffer(&self) -> (*mut c_void, i32, i32, i32) {
        unsafe {
            let mut width: i32 = 0;
            let mut height: i32 = 0;
            let mut stride: i32 = 0;
            let data_ptr = GetResourceBuffer(self.0, &mut width, &mut height, &mut stride);
            (data_ptr, width, height, stride)
        }
    }
}

impl Clone for LockedResource {
    fn clone(&self) -> Self {
        unsafe {
            LockResource(self.0);
        }
        LockedResource(self.0)
    }
}

impl Drop for LockedResource {
    fn drop(&mut self) {
        unsafe {
            UnlockResource(self.0);
        }
    }
}
