import struct

import ModernGL
from PyQt5 import QtOpenGL, QtWidgets


class QGLControllerWidget(QtOpenGL.QGLWidget):
    def __init__(self):
        fmt = QtOpenGL.QGLFormat()
        fmt.setVersion(3, 3)
        fmt.setProfile(QtOpenGL.QGLFormat.CoreProfile)
        fmt.setSampleBuffers(True)
        super(QGLControllerWidget, self).__init__(fmt, None)

    def initializeGL(self):
        self.ctx = ModernGL.create_context()

        prog = self.ctx.program(
            vertex_shader='''
    #version 330

    in vec3 vert;

    uniform float znear;
    uniform float zfar;
    uniform float fovy;
    uniform float ratio;

    uniform vec3 center;
    uniform vec3 eye;
    uniform vec3 up;

    mat4 perspective() {
        float zmul = (-2.0 * znear * zfar) / (zfar - znear);
        float ymul = 1.0 / tan(fovy * 3.14159265 / 360);
        float xmul = ymul / ratio;

        return mat4(
            xmul, 0.0, 0.0, 0.0,
            0.0, ymul, 0.0, 0.0,
            0.0, 0.0, -1.0, -1.0,
            0.0, 0.0, zmul, 0.0
        );
    }

    mat4 lookat() {
        vec3 forward = normalize(center - eye);
        vec3 side = normalize(cross(forward, up));
        vec3 upward = cross(side, forward);
        return mat4(
            side.x, upward.x, -forward.x, 0,
            side.y, upward.y, -forward.y, 0,
            side.z, upward.z, -forward.z, 0,
            -dot(eye, side), -dot(eye, upward), dot(eye, forward), 1
        );
    }

    void main() {
        gl_Position = perspective() * lookat() * vec4(vert, 1.0);
    }
'''),
            fragment_shader='''
    #version 330

    out vec4 color;

    void main() {
        color = vec4(0.04, 0.04, 0.04, 1.0);
    }
'''),
        ])

        self.znear = prog.uniforms['znear']
        self.znear.value = 0.1
        self.zfar = prog.uniforms['zfar']
        self.zfar.value = 1000.0
        self.ratio = prog.uniforms['ratio']
        self.ratio.value = self.width() / self.height()
        self.fovy = prog.uniforms['fovy']
        self.fovy.value = 60

        self.eye = prog.uniforms['eye']
        self.eye.value = (3, 3, 3)
        self.center = prog.uniforms['center']
        self.center.value = (0, 0, 0)
        self.up = prog.uniforms['up']
        self.up.value = (0, 0, 1)

        grid = bytes()

        for i in range(0, 65):
            grid += struct.pack('6f', i - 32, -32.0, 0.0, i - 32, 32.0, 0.0)
            grid += struct.pack('6f', -32.0, i - 32, 0.0, 32.0, i - 32, 0.0)

        self.vbo = self.ctx.buffer(grid)
        self.vao = self.ctx.simple_vertex_array(prog, self.vbo, ['vert'])

    def paintGL(self):

        self.ctx.viewport = (0, 0, self.width(), self.height())
        self.ratio.value = self.width() / self.height()
        self.ctx.clear(0.9, 0.9, 0.9)
        self.vao.render(ModernGL.LINES, 65 * 4)
        self.ctx.finish()


app = QtWidgets.QApplication([])
window = QGLControllerWidget()
window.move(QtWidgets.QDesktopWidget().rect().center() - window.rect().center())
window.show()
app.exec_()
