import importlib.util
import os
from OpenGL.GL import *
from OpenGL.GLU import *


class GameObject:
    def __init__(self, object_name):
        self.object_name = object_name
        self.model = None
        self.texture = None
        self.visible = True
        self.script = None
        self.script_module = None
        self.position = [0.0, 0.0, 0.0]
        self.rotation = [0.0, 0.0, 0.0]
        self.scale = [1.0, 1.0, 1.0]
    
    def set_model(self, model_data):
        self.model = model_data
    
    def add_script(self, script_path):
        if not os.path.exists(script_path):
            raise FileNotFoundError(f"Script file not found: {script_path}")
        
        self.script = script_path
        spec = importlib.util.spec_from_file_location("script", script_path)
        self.script_module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(self.script_module)
    
    def set_texture(self, texture_path):
        if not os.path.exists(texture_path):
            raise FileNotFoundError(f"Texture file not found: {texture_path}")
        self.texture = texture_path
    
    def set_visible(self, visible):
        self.visible = visible
    
    def set_position(self, x, y, z):
        self.position = [x, y, z]
    
    def set_rotation(self, x, y, z):
        self.rotation = [x, y, z]
    
    def set_scale(self, x, y, z):
        self.scale = [x, y, z]
    
    def get_name(self):
        return self.object_name
    
    def render(self):
        if not self.visible or not self.model:
            return
        
        glPushMatrix()
        
        # Apply transformations
        glTranslatef(self.position[0], self.position[1], self.position[2])
        glRotatef(self.rotation[0], 1, 0, 0)
        glRotatef(self.rotation[1], 0, 1, 0)
        glRotatef(self.rotation[2], 0, 0, 1)
        glScalef(self.scale[0], self.scale[1], self.scale[2])
        
        # Render the model
        self.render_model()
        
        glPopMatrix()
    
    def render_model(self):
        if self.model:
            self.model.render()
    
    def start(self):
        if self.script_module and hasattr(self.script_module, 'start'):
            self.script_module.start()
    
    def update(self):
        if self.script_module and hasattr(self.script_module, 'update'):
            self.script_module.update()
    
    def finish(self):
        if self.script_module and hasattr(self.script_module, 'finish'):
            self.script_module.finish()


import math


class CameraObject:
    def __init__(self, camera_name):
        self.camera_name = camera_name
        self.position = [0.0, 0.0, 5.0]
        self.target = [0.0, 0.0, 0.0]
        self.up = [0.0, 1.0, 0.0]
        self.active = False
        self.script = None
        self.script_module = None
        self.rotation = [0.0, 0.0, 0.0]  # pitch, yaw, roll in degrees
    
    def set_position(self, x, y, z):
        self.position = [x, y, z]
        self.update_target_from_rotation()
    
    def set_target(self, x, y, z):
        self.target = [x, y, z]
    
    def set_up(self, x, y, z):
        self.up = [x, y, z]
    
    def set_rotation(self, pitch, yaw, roll):
        self.rotation = [pitch, yaw, roll]
        self.update_target_from_rotation()
    
    def update_target_from_rotation(self):
        # Convert rotation from degrees to radians
        pitch_rad = math.radians(self.rotation[0])
        yaw_rad = math.radians(self.rotation[1])
        
        # Calculate forward vector from rotation
        forward_x = math.sin(yaw_rad) * math.cos(pitch_rad)
        forward_y = -math.sin(pitch_rad)
        forward_z = -math.cos(yaw_rad) * math.cos(pitch_rad)
        
        # Set target based on position and forward direction
        self.target = [
            self.position[0] + forward_x,
            self.position[1] + forward_y,
            self.position[2] + forward_z
        ]
    
    def set_active(self, active):
        self.active = active
    
    def is_active(self):
        return self.active
    
    def get_name(self):
        return self.camera_name
    
    def add_script(self, script_path):
        if not os.path.exists(script_path):
            raise FileNotFoundError(f"Script file not found: {script_path}")
        
        self.script = script_path
        spec = importlib.util.spec_from_file_location("script", script_path)
        self.script_module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(self.script_module)
    
    def apply_view(self):
        if self.active:
            gluLookAt(
                self.position[0], self.position[1], self.position[2],
                self.target[0], self.target[1], self.target[2],
                self.up[0], self.up[1], self.up[2]
            )
    
    def start(self):
        if self.script_module and hasattr(self.script_module, 'start'):
            self.script_module.start()
    
    def update(self):
        if self.script_module and hasattr(self.script_module, 'update'):
            self.script_module.update()
    
    def finish(self):
        if self.script_module and hasattr(self.script_module, 'finish'):
            self.script_module.finish()


_game_objects = {}
_cameras = {}


def GetObject(object_name):
    return _game_objects.get(object_name)


def GetCamera(camera_name):
    return _cameras.get(camera_name)


def RegisterGameObject(game_object):
    _game_objects[game_object.get_name()] = game_object


def RegisterCamera(camera_object):
    _cameras[camera_object.get_name()] = camera_object


def RenderAllGameObjects():
    for obj in _game_objects.values():
        obj.render()


def ApplyActiveCamera():
    for camera in _cameras.values():
        if camera.is_active():
            camera.apply_view()
            break
