import my3dengine as m3d
from OpenGL.GL import *
import time
import numpy as np

class Game:
    def __init__(self):
        self.WIDTH, self.HEIGHT = m3d.get_fullscreen()
        self.window = m3d.Window("3D Game", self.WIDTH, self.HEIGHT)
        self.keys = m3d.Key
        self.escape_was_pressed = False

        self.sources = [
            m3d.AudioSource(position=[0, 0, 3], sound_path="assets/dark-atmosphere-background-009-312380.mp3", emit_radius=5, volume=1.0, loop=False, fade_duration=1.0),
        ]

        # FPS counter
        self.last_fps_time = time.time()
        self.frames = 0

        self.camera = m3d.Camera(
            position=(0, 0, 3),
            target=(0, 0, 0),
            up=(0, 1, 0),
            fov=60,
            aspect=self.WIDTH / self.HEIGHT,
        )

        self.listener = m3d.AudioListener(self.camera.position)

        self.ui = m3d.UI(self.WIDTH, self.HEIGHT)

        self.meshes = [
            m3d.Mesh.cube(),
            m3d.Mesh.sphere(),
            m3d.Mesh.capsule(),
            m3d.Mesh.cube(),
            m3d.Mesh.plane(),
            m3d.Mesh.from_obj("assets/untitled.obj"),
        ]

        # WODA
        self.water = m3d.Mesh.water(
            size=20.0,
            resolution=64,  # 🧠 Mniejsze resolution = lepsze FPS
            wave_speed=0.01,
            wave_height=0.5,
            wave_scale=(0.4, 0.6),
            second_wave=True,
            color=(0.0, 0.4, 0.8),
            backface=True
        )
        self.water.set_position(15, 0, 15)
        self.water.set_texture("assets/water.png")
        self.water.set_uv_transform((10, 10), (0, 0))

        # Pozycje
        self.meshes[0].set_position(0, 0, 0)
        self.meshes[1].set_position(0, 0, 5)
        self.meshes[2].set_position(2, 0, 0)
        self.meshes[3].set_position(-4, 0, 0)
        self.meshes[4].set_position(0, -4, 0)
        self.meshes[5].set_position(-8, 0, 0)

        # Skale
        self.meshes[2].scale_uniform(2)
        self.meshes[3].set_scale(1, 0.2, 1)
        self.meshes[4].scale_uniform(15)
        self.meshes[5].scale_uniform(0.5)

        # Tekstury
        for i in range(6):
            self.meshes[i].set_texture("assets/bricksx64.png")

        # UV
        self.meshes[5].set_uv_transform(tiling=(10, 10), offset=(0.1, 0.1))
        self.meshes[4].set_uv_transform(tiling=(10, 10), offset=(0.1, 0.1))
        self.meshes[1].set_uv_transform(tiling=(5, 5), offset=(0.1, 0.1))
        self.meshes[2].set_uv_transform(tiling=(5, 5), offset=(0.1, 0.1))

        # OpenGL ustawienia
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        glCullFace(GL_BACK)

    def update(self, dt, window):
        self.camera.handle_input(dt, window)

        glViewport(0, 0, self.WIDTH, self.HEIGHT)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        self.listener.position[:] = self.camera.position

        cam_pos = self.camera.position
        cam_dir = m3d.normalize(self.camera.target - cam_pos)
        view_proj = self.camera.view_proj_matrix

        m3d.update_audio_system(self.listener, self.sources)

        if (
            self.water.is_in_fov(cam_pos, cam_dir, self.camera.fov)
            and np.linalg.norm(self.water.position - cam_pos) < 150
        ):
            self.water.update_water()
            self.water.shader.use()
            self.camera.set_uniforms(self.water.shader)
            self.water.draw(
                cam_pos=cam_pos,
                cam_dir=cam_dir,
                fov_deg=self.camera.fov,
                view_proj_matrix=view_proj
            )

        # Rysuj inne meshe
        for mesh in self.meshes:
            if (
                not mesh.is_in_fov(cam_pos, cam_dir, self.camera.fov)
                or np.linalg.norm(mesh.position - cam_pos) > 150
            ):
                continue

            mesh.shader.use()
            self.camera.set_uniforms(mesh.shader)
            mesh.draw(
                cam_pos=cam_pos,
                cam_dir=cam_dir,
                fov_deg=self.camera.fov,
                view_proj_matrix=view_proj,
                debug=False
            )

        # FPS tracking
        self.frames += 1
        current_time = time.time()
        if current_time - self.last_fps_time >= 1.0:
            print(f"FPS: {self.frames}")
            self.frames = 0
            self.last_fps_time = current_time

    def run(self):
        try:
            m3d.init()
            self.window.run(self.update)
        finally:
            self.water.destroy()
            for mesh in self.meshes:
                mesh.destroy()

if __name__ == "__main__":
    app = Game()
    app.run()
