import numpy
from numpy import array
from OpenGL.GL import *
from OpenGL.GL import shaders
from OpenGL.arrays import vbo

from OpenGL._bytes import bytes, _NULL_8_BYTE

def compileShader( source, shaderType ):

    """Compile shader source of given type
        source -- GLSL source-code for the shader
    shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc,
        returns GLuint compiled shader reference
    raises RuntimeError when a compilation failure occurs
    """
    if isinstance(source, str):

        source = [source]
    elif isinstance(source, bytes):

        source = [source.decode('utf-8')]

    shader = glCreateShader(shaderType)
    glShaderSource(shader, source)
    glCompileShader(shader)
    result = glGetShaderiv(shader, GL_COMPILE_STATUS)

    if not(result):
        # TODO: this will be wrong if the user has
        # disabled traditional unpacking array support.
        raise RuntimeError(
            """Shader compile failure (%s): %s"""%(
                result,
                glGetShaderInfoLog( shader ),
            ),
            source,
            shaderType,
        )
    return shader


class shader:
    vertex = """#version 120
        //attributes in values
        attribute vec3 inPosition;
        attribute vec3 inColor;

        //varying sending to fragment shader
        varying vec4 outColor;
        void main(){
            vec4 fragmentPos = gl_ModelViewMatrix * gl_Vertex;
            gl_Position = (gl_ProjectionMatrix * fragmentPos);
            outColor = vec4(gl_Color.rgb, 1);
        }"""

    fragment = """#version 120
        varying vec4 outColor;
        void main(){
            gl_FragColor = outColor;
        }"""

    program = None
    def compile(self):
        self.program = shaders.compileProgram(
            compileShader(self.vertex, GL_VERTEX_SHADER),
            compileShader(self.fragment, GL_FRAGMENT_SHADER),)

class point:
    __slots__ = ['x', 'y', 'z', 'xyz', 'vertex']

    def __init__(self, p, c=(1, 0, 0)):
        """ Position in 3d space as a tuple or list, and colour in tuple or list format"""
        self.x, self.y, self.z = p
        self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f')


class cube:
    def __init__(self, p1, color, size=0.5):
        self.color = array([1, 0, 0], 'f')
        #self.xyz = p1
        self.points = (
            point((p1[0] - size, p1[1] + size, p1[2] - size), (color)),
            point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), 
            point((p1[0] + size, p1[1] + size, p1[2] + size), (color)),
            point((p1[0] + size, p1[1] + size, p1[2] - size), (color)),
            
            point((p1[0] - size, p1[1] - size, p1[2] - size), (color)),
            point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), 
            point((p1[0] + size, p1[1] - size, p1[2] + size), (color)),
            point((p1[0] + size, p1[1] - size, p1[2] - size), (color)),
            
            )

        self.vbuffer = vbo.VBO(array(self.get_data(), 'f'))
        self.vbuffer_size = len(self.get_data())

    def bind(self):
        self.vbuffer.bind()

    def unbind(self):
        self.vbuffer.unbind()

    def get_data(self):
        return (
            self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, 
            self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, 

            self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, 
            self.points[0].vertex, self.points[5].vertex, self.points[4].vertex,

            self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, 
            self.points[0].vertex, self.points[4].vertex, self.points[7].vertex,

            self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, 
            self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, 

            self.points[6].vertex, self.points[1].vertex, self.points[2].vertex,
            self.points[6].vertex, self.points[5].vertex, self.points[1].vertex,

            self.points[6].vertex, self.points[4].vertex, self.points[5].vertex,
            self.points[6].vertex, self.points[7].vertex, self.points[4].vertex,
        )



class cube:
    def __init__(self, p1, color, size=0.5):
        self.color = array([1, 0, 0], 'f')
        #self.xyz = p1
        self.points = (
            point((p1[0] - size, p1[1] + size, p1[2] - size), (color)),
            point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), 
            point((p1[0] + size, p1[1] + size, p1[2] + size), (color)),
            point((p1[0] + size, p1[1] + size, p1[2] - size), (color)),
            
            point((p1[0] - size, p1[1] - size, p1[2] - size), (color)),
            point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), 
            point((p1[0] + size, p1[1] - size, p1[2] + size), (color)),
            point((p1[0] + size, p1[1] - size, p1[2] - size), (color)),
            
            )

        self.vbuffer = vbo.VBO(array(self.get_data(), 'f'))
        self.vbuffer_size = len(self.get_data())

    def bind(self):
        self.vbuffer.bind()

    def unbind(self):
        self.vbuffer.unbind()

    def get_data(self):
        return (
            self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, 
            self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, 

            self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, 
            self.points[0].vertex, self.points[5].vertex, self.points[4].vertex,

            self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, 
            self.points[0].vertex, self.points[4].vertex, self.points[7].vertex,

            self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, 
            self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, 

            self.points[6].vertex, self.points[1].vertex, self.points[2].vertex,
            self.points[6].vertex, self.points[5].vertex, self.points[1].vertex,

            self.points[6].vertex, self.points[4].vertex, self.points[5].vertex,
            self.points[6].vertex, self.points[7].vertex, self.points[4].vertex,
        )

