import random
import math
from Entities.entity import Entity

class Marine(Entity):

    
    movement = 0
    view = 3
    

    def update(self):
        self.centered_position = (self.iso_x, self.iso_y)
        # Aggiorna il campo visivo dell'unità (fog of war)
        self.cast_rays_to_distance(7)
        
        self.move()
        super().update()
    
    def set_visibility(self):
        # Set visibility based on the distance to the cursor
        distance = self.graphics.get_distance((self.x, self.y), self.engine.cursor_pos)
        if distance < self.view:
            self.graphics.set_opacity(self.frame, 1.0)
        else:
            self.graphics.set_opacity(self.frame, 0.5)
    
    def select_unit(self):
        self.selected = True
        # Play a random voice response when selected
        sound_file = f"marine/tmawht0{random.randint(0, 3)}.wav"
        if not self.game.cmd_sound_effects:
            self.graphics.play_sound(sound_file)
            self.game.cmd_sound_effects = True

    def move(self):
        # Aquisisci la posizione dell'unità
        self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y)
        self.position = (self.x, self.y)
        
        # Controlla se l'unità èha raggiunto la cella di destinazione
        if self.target_cell != (self.x, self.y):
            # Contolla che la cella target non sia occupata da una unità che abbia quella cella come target
            if self.game.entities_positions.get(self.target_cell):
                if self.game.entities_positions.get(self.target_cell).target_cell == self.target_cell and self.game.entities_positions.get(self.target_cell) != self:
                    #calcola un nuovo target
                    self.target_cell = self.graphics.get_next_cell(self.target_cell, self.position, ignore_units=True)
                    if not self.target_cell:
                        self.target_cell = self.position
                    # Se non ci sono celle disponibili, ferma l'unità
                    return
            # Aquisisci la posizione a schermo della cella di destinazione e disegna lì un quadrato rosso
            target_iso_x, target_iso_y = self.graphics.iso_transform(self.target_cell[0], self.target_cell[1])
            self.graphics.draw_square(target_iso_x, target_iso_y, color=(255, 0, 0, 255), margin=6)
            
            # Se la cella successiva è la stessa posizione dell'unità, calcola la prossima cella 
            # (in caso contrario sarebbe ancora in movimento parziale)
            if self.position == self.next_cell:
                self.next_cell = self.graphics.get_next_cell(self.next_cell, self.target_cell)
                # Se l'algoritmo ha efettivamente trovato una cella, occupa la posizione
                if self.next_cell:
                    self.game.entities_positions[self.next_cell] = self
                # Se la cella di destinazione è occupata da un'altra entità, fermati
                else:
                    self.action = "idle"
                    self.next_cell = self.position
                    return
                    
            # Set walking animation and direction
            self.action = "walk"
            self.direction = self.graphics.get_direction((self.x, self.y), self.next_cell)
            self.moving = True
            
            # Calculate target coordinates
            target_x, target_y = self.graphics.iso_transform(self.next_cell[0], self.next_cell[1])
            
            # Increment movement counter
            self.movement += 0.1
            # Calculate how far we've moved (0.0 to 1.0)
            move_progress = min(self.movement, 1.0)
            
            # Calculate new position based on progress between cells
            self.iso_x = self.iso_x + (target_x - self.iso_x) * move_progress
            self.iso_y = self.iso_y + (target_y - self.iso_y) * move_progress
            if self.movement >= 1.0:
                # Reset movement and set to idle
                self.movement = 0
                self.x, self.y = self.next_cell
                self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y)
        else:
            self.action = "idle"
    
    def set_target_cell(self, target_cell):
        # Prima verifica che le coordinate siano all'interno dei limiti della mappa
        map_height = len(self.engine.map)
        map_width = len(self.engine.map[0])
        
        x, y = target_cell
        
        # Verifica che le coordinate siano valide
        if not (0 <= y < map_height and 0 <= x < map_width):
            if not self.game.cmd_sound_effects:
                self.graphics.play_sound("sounds/perror.wav")
                self.game.cmd_sound_effects = True
            return
            
        # Ora puoi controllare il contenuto della cella in sicurezza
        if self.engine.map[y][x]["wall"]:
            if not self.game.cmd_sound_effects:
                self.graphics.play_sound("sounds/perror.wav")
                self.game.cmd_sound_effects = True
            return
        
        self.target_cell = target_cell
        if not self.game.cmd_sound_effects:
            self.graphics.play_sound(f"marine/tmayes0{random.randint(0, 3)}.wav")
            self.game.cmd_sound_effects = True

    def cast_rays_to_distance(self, view_distance):
        """
        Rivela tutte le celle nel raggio di vista dell'unità usando ray casting.
        Le celle vengono illuminate indipendentemente dagli ostacoli,
        ma quelle più lontane hanno maggiore ombreggiatura.
        """
        # Pre-calcolo dei valori trigonometrici per ottimizzazione
        if not hasattr(self, '_precomputed_angles'):
            self._precompute_ray_angles()
        
        revealed_cells = {}
        
        # Lancia raggi in 18 direzioni (ogni 20 gradi)
        for angle_idx, (sin_val, cos_val) in enumerate(self._ray_directions):
            if angle_idx == 0:
                self._cast_visibility_ray(angle_idx, sin_val, cos_val, view_distance+1, revealed_cells)
            else:
                self._cast_visibility_ray(angle_idx, sin_val, cos_val, view_distance, revealed_cells)

    def _precompute_ray_angles(self):
        """Pre-calcola i valori sin/cos per evitare calcoli ripetuti ogni frame"""
        self._ray_directions = []
        for angle in range(0, 360, 20):
            radians = math.radians(angle)
            self._ray_directions.append((math.sin(radians), math.cos(radians)))
        self._precomputed_angles = True

    def _cast_visibility_ray(self, angle_idx, sin_val, cos_val, max_distance, revealed_cells):
        """
        Lancia un singolo raggio di visibilità dalla posizione dell'unità.
        Rivela tutte le celle lungo il percorso, applicando ombreggiatura crescente con la distanza.
        """
        cell_size_x = int(self.engine.graphics.cell_size/3)
        cell_size_y = int(self.engine.graphics.cell_size/6)
        
        # Calcola le posizioni lungo il raggio partendo dalla distanza massima verso il centro
        for step in range(max_distance, 0, -1):
            current_distance = max_distance - step
            
            # Calcola la posizione del punto sul raggio
            ray_x = int(self.centered_position[0] + current_distance * cell_size_x * sin_val)
            ray_y = int(self.centered_position[1] - current_distance * cell_size_y * cos_val)
            
            # Converte le coordinate schermo in coordinate griglia
            grid_x, grid_y = self.graphics.inv_iso_transform(ray_x, ray_y)
            
            # Verifica che le coordinate siano valide
            if self._is_valid_grid_position(grid_x, grid_y):
                self._reveal_cell(grid_x, grid_y, step, revealed_cells)
                
                # Disegna il raggio solo per il punto più distante (visualizzazione debug)
                # if step == 1:
                #     self.graphics.draw_line(
                #         (self.centered_position[0], self.centered_position[1], ray_x, ray_y), 
                #         color=(0, 255, 0, 255)
                #     )

    def _is_valid_grid_position(self, grid_x, grid_y):
        """Controlla se le coordinate della griglia sono valide"""
        map_width = len(self.engine.map[0])
        map_height = len(self.engine.map)
        return 0 <= grid_x < map_width and 0 <= grid_y < map_height

    def _reveal_cell(self, grid_x, grid_y, distance_step, revealed_cells):
        """
        Rivela una singola cella della mappa e applica l'ombreggiatura appropriata.
        Le celle più vicine (distance_step == 1) hanno ombra ridotta.
        """
        cell_key = (grid_x, grid_y)
        
        # Salva il valore originale dell'ombra solo se non già processata
        if cell_key not in revealed_cells:
            revealed_cells[cell_key] = self.game.map_shadow[grid_y][grid_x]
            # Marca la cella come visitata e rimuovi l'ombra base
            self.engine.map[grid_y][grid_x]["visited"] = True
            self.game.map_shadow[grid_y][grid_x] = 0
        
        # Applica ombreggiatura speciale per le celle ai bordi del campo visivo
        if distance_step == 1:
            original_shadow = revealed_cells.get(cell_key, 0)
            self.game.map_shadow[int(grid_y)][int(grid_x)] = max(original_shadow - 0.5, 0)
