Fix all the bugs and return bug free code for this app: from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
import sys, numpy, time, math

class Material:
    def __init__(self):
        self.diffuse_color = [1.0, 1.0, 1.0, 1.0]
        self.shininess = 32.0

class Scene(QOpenGLWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.material = Material()
        self.shaders = {}  
        self.current_shader = None
        self.last_frame_time = time.time()
        self.frame_count = 0

    def initializeGL(self):
        glEnable(GL_DEPTH_TEST)
        glClearColor(0.0, 0.0, 0.0, 1.0)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45, (self.width() / self.height()), 0.1, 50.0)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        self.initShaders()
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update)
        self.timer.start(16)  # Aim for ~60 FPS

    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()
        glTranslatef(0.0, 0.0, -6.0)

        if self.current_shader:
            glUseProgram(self.current_shader)
            diffuse_color_loc = glGetUniformLocation(self.current_shader, "diffuseColor")
            shininess_loc = glGetUniformLocation(self.current_shader, "shininess")
            glUniform4fv(diffuse_color_loc, 1, numpy.array(self.material.diffuse_color))
            glUniform1f(shininess_loc, self.material.shininess)

        # Draw sphere using OpenGL primitives
        self.drawSphere(1.0, 20, 20)  

        glUseProgram(0)

        self.calculateFPS()

    def drawSphere(self, radius, stacks, sectors):
        """Draws a sphere using OpenGL primitives."""
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)

        for i in range(stacks):
            lat0 = math.pi * (-0.5 + (i / stacks))
            z0 = math.sin(lat0)
            zr0 = math.cos(lat0)

            lat1 = math.pi * (-0.5 + ((i + 1) / stacks))
            z1 = math.sin(lat1)
            zr1 = math.cos(lat1)

            glBegin(GL_TRIANGLE_STRIP)
            for j in range(sectors + 1):
                lng = 2 * math.pi * (j / sectors)
                x = math.cos(lng)
                y = math.sin(lng)

                glNormal3f(x * zr0, y * zr0, z0)
                glVertex3f(radius * x * zr0, radius * y * zr0, radius * z0)

                glNormal3f(x * zr1, y * zr1, z1)
                glVertex3f(radius * x * zr1, radius * y * zr1, radius * z1)
            glEnd()

        self.calculateFPS() 

    def initShaders(self):
        self.shaders["Basic"] = self.compile_shader_program(
            """
            varying vec3 normal;
            void main() {
                normal = normalize(gl_NormalMatrix * gl_Normal);
                gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
            }
            """,
            """
            uniform vec4 diffuseColor;
            varying vec3 normal;
            void main() {
                vec3 lightDir = normalize(vec3(0.0, 0.0, 1.0)); 
                float diff = max(dot(normal, lightDir), 0.0);
                gl_FragColor = diff * diffuseColor;
            }
            """
        )

        self.shaders["Phong"] = self.compile_shader_program( 
            """
            varying vec3 normal;
            varying vec3 viewDir;
            varying vec3 fragPos;

            void main() {
                normal = normalize(gl_NormalMatrix * gl_Normal);
                fragPos = vec3(gl_ModelViewMatrix * gl_Vertex);
                viewDir = normalize(vec3(0.0, 0.0, 1.0) - fragPos);
                gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
            }
            """,
            """
            uniform vec4 diffuseColor;
            uniform float shininess;

            varying vec3 normal;
            varying vec3 viewDir;
            varying vec3 fragPos;

            void main() {
                vec3 lightDir = normalize(vec3(0.0, 0.0, 1.0) - fragPos);
                vec3 reflectDir = reflect(-lightDir, normal);

                float diff = max(dot(normal, lightDir), 0.0);
                float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);

                vec3 diffuse = diff * vec3(diffuseColor);
                vec3 specular = spec * vec3(1.0, 1.0, 1.0);

                gl_FragColor = vec4(diffuse + specular, 1.0);
            }
            """
        )

        self.current_shader = self.shaders["Basic"]

    def compile_shader_program(self, vertex_source, fragment_source):
        """Compiles the provided vertex and fragment shaders into a shader program."""
        vertex_shader = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(vertex_shader, vertex_source)
        glCompileShader(vertex_shader)

        # Check for compilation errors (vertex shader)
        success = glGetShaderiv(vertex_shader, GL_COMPILE_STATUS)
        if not success:
            print("Vertex Shader compilation failed!")
            print(glGetShaderInfoLog(vertex_shader).decode())

        fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(fragment_shader, fragment_source)
        glCompileShader(fragment_shader)

        # Check for compilation errors (fragment shader)
        success = glGetShaderiv(fragment_shader, GL_COMPILE_STATUS)
        if not success:
            print("Fragment Shader compilation failed!")
            print(glGetShaderInfoLog(fragment_shader).decode())

        program = glCreateProgram()
        glAttachShader(program, vertex_shader)
        glAttachShader(program, fragment_shader)
        glLinkProgram(program)

        # Check for linking errors
        success = glGetProgramiv(program, GL_LINK_STATUS)
        if not success:
            print("Shader program linking failed!")
            print(glGetProgramInfoLog(program).decode())

        glDeleteShader(vertex_shader)
        glDeleteShader(fragment_shader)

        return program 

    def calculateFPS(self):
        self.frame_count += 1
        current_time = time.time()
        if current_time - self.last_frame_time >= 1.0:
            fps = self.frame_count / (current_time - self.last_frame_time)
            self.parent().statusBar().showMessage(f"FPS: {fps:.2f}")
            self.last_frame_time = current_time
            self.frame_count = 0

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("3D Editor")
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)

        self.scene = Scene()
        self.color_dialog = QColorDialog(self)

        # GUI for shader selection
        self.shader_combo = QComboBox(self)
        self.shader_combo.addItems(self.scene.shaders.keys())
        self.shader_combo.currentIndexChanged.connect(self.change_shader)

        # GUI for Color Control
        self.color_button = QPushButton("Change Color", self)
        self.color_button.clicked.connect(self.change_color)

        # GUI for Shininess Control
        self.shininess_slider = QSlider(Qt.Orientation.Horizontal, self)
        self.shininess_slider.setRange(1, 128)  
        self.shininess_slider.setValue(int(self.scene.material.shininess))
        self.shininess_slider.valueChanged.connect(self.change_shininess)

        # Layout
        layout = QVBoxLayout()
        controls_layout = QHBoxLayout()
        controls_layout.addWidget(self.shader_combo)
        controls_layout.addWidget(self.color_button)
        controls_layout.addWidget(self.shininess_slider)
        layout.addWidget(self.scene)
        layout.addLayout(controls_layout)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_scene)
        self.timer.start(16)

    def update_scene(self):
        self.scene.update() # update the scene
        self.calculateFPS() # update the FPS

    def calculateFPS(self):
        self.frame_count = 0
        self.frame_count += 1
        self.last_frame_time = 0
        current_time = time.time()
        if current_time - self.last_frame_time >= 1.0:
            fps = self.frame_count / (current_time - self.last_frame_time)
            self.statusBar().showMessage(f"FPS: {fps:.2f}") 
            self.last_frame_time = current_time
            self.frame_count = 0

    def change_shader(self, index):
        shader_name = self.shader_combo.currentText()
        self.scene.current_shader = self.scene.shaders[shader_name]
        self.scene.update()

    def change_color(self):
        color = self.color_dialog.getColor()
        if color.isValid():
            self.scene.material.diffuse_color = [color.redF(), color.greenF(), color.blueF(), 1.0]
            self.scene.update()

    def change_shininess(self):
        self.scene.material.shininess = self.shininess_slider.value()
        self.scene.update()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())