import pygame
import numpy as np
import math

class Graphic:
    def __init__(self, map_png, screen_width, screen_height, height, distance, overlay, steering_wheel, stick_shift):
        pygame.init()
        self._screen = pygame.display.set_mode((screen_width, screen_height))
        
        map_img = pygame.image.load(map_png).convert()
        self._mw, self._mh = map_img.get_width(), map_img.get_height()
        self._map_array = pygame.surfarray.array3d(map_img)
        
        self._car_overlay = pygame.image.load(overlay).convert_alpha()
        self._steering_wheel = pygame.image.load(steering_wheel.img_path).convert_alpha()
        self._stick_shift = pygame.image.load(stick_shift.img_path).convert_alpha()
        self._steering_wheel_pos = steering_wheel.position_list
        self._stick_shift_pos = stick_shift.position_list
        
        self.px, self.py = 0.0, 0.0
        self.angle = 0.0
        self._height = height
        self._distance = distance
        self.rpm = 0
        self.speed = 0
        self.steering = 0
        self.gear = 1
        self.time = 0
        
        self._valueFont = pygame.font.Font(None, 30)
        self._eventFont = pygame.font.Font(None, 40)
        self.event = ""

        self._w, self._h = self._screen.get_size()
        self._horizon = self._h // 2
        
        self._t_line = np.linspace(0, 1, self._w)
        
    def update(self):
        # --- Frame-Buffer als NumPy Array ---
        buffer = np.zeros((self._w, self._h, 3), dtype=np.uint8)
        buffer[:, :self._horizon] = (120, 150, 180)   # Himmel

        dx = math.cos(self.angle)
        dy = math.sin(self.angle)
        perpx = -dy
        perpy = dx

        for sy in range(self._horizon, self._h):

            # Entfernung
            p = (self._height * self._distance) / (sy - self._horizon + 0.1)

            # Start+Endpunkte der Linie (links→rechts)
            start_x = self.px + dx * p - perpx * p
            start_y = self.py + dy * p - perpy * p
            end_x   = self.px + dx * p + perpx * p
            end_y   = self.py + dy * p + perpy * p

            # Interpolation in NumPy Vektoren
            fx = start_x + (end_x - start_x) * self._t_line
            fy = start_y + (end_y - start_y) * self._t_line

            # integer Koordinaten
            tx = fx.astype(int)
            ty = fy.astype(int)

            # Maske für gültige Koordinaten
            valid = (tx >= 0) & (tx < self._mw) & (ty >= 0) & (ty < self._mh)

            # Alle nicht-validen als „grüne Wiese“
            row = np.zeros((self._w, 3), dtype=np.uint8)
            row[:] = (10, 100, 50)

            # gültige Pixel aus map holen
            row[valid] = self._map_array[tx[valid], ty[valid]]

            # Zeile in Buffer schreiben
            buffer[:, sy] = row

        # NumPy-Buffer auf Surface blitten
        pygame.surfarray.blit_array(self._screen, buffer)
        
        #Car-Overlay
        self._screen.blit(self._car_overlay, (0,0))
        rotate = pygame.transform.rotate(self._steering_wheel, -self.steering*5)
        self._screen.blit(rotate, rotate.get_rect(center=self._steering_wheel_pos[0]).topleft)
        self._screen.blit(self._stick_shift, self._stick_shift_pos[self.gear-1])
        
        #Geschwindigkeit, Drehzahl, Event und Zeit
        rpm_text = self._valueFont.render(f"{int(self.rpm)} rpm", True, (255,0,0), (0,0,0))
        self._screen.blit(rpm_text, (self._w-100, self._h-100))
        speed_text = self._valueFont.render(f"{int(self.speed)} km/h", True, (255,0,0), (0,0,0))
        self._screen.blit(speed_text, (self._w-100, self._h-50))
        time_text = self._valueFont.render(f"{int(self.time)} s", True, (255,0,0), (0,0,0))
        self._screen.blit(time_text, (self._w-100, self._h-150))
        event_text = self._eventFont.render(self.event, True, (255,0,0), (0,0,0))
        self._screen.blit(event_text, (100, 100))
        
        pygame.display.flip()
        
    def quit(self):
        pygame.quit()
        
    def IsQuit(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT: return True
        return False