from OpenGL.GL import *
import numpy as np
from OpenGL.GL.ARB.vertex_array_object import glDeleteVertexArrays
from OpenGL.GL.framebufferobjects import glBindRenderbuffer
from OpenGL.GLUT import *
import OpenGL.GLUT.freeglut
from OpenGL.GLU import *
from OpenGL.GL import *
from ctypes import sizeof, c_float, c_void_p, c_uint
from MatrixStuff.Transformations import *


class Light:
    programId = {}
    depthshaderId = -1

    def getDepthProgram(self, vertexshader=-1, geometryshader=-1):
        if ((
        vertexshader, geometryshader) not in self.programId.keys() and vertexshader != -1 and geometryshader != -1):
            if self.depthshaderId == -1:
                with open('./Lights/depthfragment.glsl', 'r') as f:
                    fragment_shader_string = f.read()
                fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER)
                glShaderSource(fragment_shader_id, fragment_shader_string)
                glCompileShader(fragment_shader_id)
                if glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
                    raise RuntimeError(glGetShaderInfoLog(fragment_shader_id))

            program_id = glCreateProgram()
            glAttachShader(program_id, vertexshader)
            glAttachShader(program_id, geometryshader)
            glAttachShader(program_id, fragment_shader_id)
            glLinkProgram(program_id)
            self.programId[(vertexshader, geometryshader)] = program_id
            return program_id
        else:
            if (vertexshader, geometryshader) not in self.programId.keys():
                return -1
            return self.programId[(vertexshader, geometryshader)]

    def __init__(self):
        self._ModelviewProjectionMatrix = np.identity(4)
        self._pos = [0, 0, 0]
        self._lightColor = [1, 1, 1]
        self.FramebufferId = -1
        self.DepthBuffer = -1
        self.map_size = 1024

    @property
    def lightColor(self):
        return self._lightColor

    @lightColor.setter
    def lightColor(self, value):
        self._lightColor = value

    @property
    def ModelviewProjectionMatrix(self):
        return self._ModelviewProjectionMatrix

    @ModelviewProjectionMatrix.setter
    def ModelviewProjectionMatrix(self, value):
        self._ModelviewProjectionMatrix = np.matrix(value)

    @property
    def pos(self):
        return self._pos

    @pos.setter
    def pos(self, value):
        self._pos = value

    def prepareForDepthMapping(self):
        new = False
        if self.FramebufferId == -1:
            self.FramebufferId = glGenFramebuffers(1)
            new = True
        glClearColor(1.0, 1.0, 1.0, 1.0)
        glBindFramebuffer(GL_FRAMEBUFFER, self.FramebufferId)
        glCullFace(GL_FRONT)
        glViewport(0, 0, self.map_size, self.map_size)
        if new:
            if self.DepthBuffer == -1:
                self.DepthBuffer = glGenTextures(1)
            glBindTexture(GL_TEXTURE_2D, self.DepthBuffer)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, self.map_size, self.map_size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, None)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
            glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, np.array([0, 0, 0], dtype=np.float32))

            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.FramebufferId, 0)

            DrawBuffers = [GL_NONE]
            glDrawBuffers(DrawBuffers)
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE):
            return False

    def finishDepthMapping(self):
        glCullFace(GL_BACK)
        DrawBuffers = [GL_COLOR_ATTACHMENT0]
        glDrawBuffers(DrawBuffers)
        glClearColor(0.0, 0.0, 0.0, 1.0)
        glBindFramebuffer(GL_FRAMEBUFFER, 0)