import arcade, arcade.math, random, math

class Boid(arcade.Sprite):
    def __init__(self, boid_num, x, y):
        super().__init__(arcade.load_texture("assets/graphics/boid.png"), center_x=x, center_y=y)

        random_angle = random.randint(0, 361)

        self.boid_num = boid_num
        self.direction = arcade.math.Vec2(math.cos(random_angle), math.sin(random_angle))
        
        self.velocity = 5
        self.radius = 10
        
        self.w_separation = 1.0
        self.w_cohesion = 1.0
        self.w_alignment = 1.0
        self.small_radius = 100
        self.large_radius = 250

    def calculate_separation(self, neighbours):
        steeraway_vectors = [arcade.math.Vec2(*self.position) - neighbour[2] for neighbour in neighbours]

        if not steeraway_vectors:
            return self.direction

        return (sum(steeraway_vectors) / len(steeraway_vectors)).normalize()

    def calculate_alignment(self, neighbours):
        directions = [neighbour[1] for neighbour in neighbours] 

        if not directions:
            return self.direction

        return (sum(directions) / len(directions)).normalize()
    
    def calculate_cohesion(self, neighbours):
        positions = [neighbour[2] for neighbour in neighbours]

        if not positions:
            return self.direction

        return ((sum(positions) / len(positions)) - self.position).normalize()
    
    def update(self, window_width, window_height, boids):
        small_radius_neighbours, large_radius_neighbours = [], []
        for boid_data in boids:
            distance = boid_data[2].distance(arcade.math.Vec2(*self.position))
            if boid_data[0] == self.boid_num or distance > self.large_radius:
                continue
            
            if distance <= self.small_radius:
                small_radius_neighbours.append(boid_data)

            large_radius_neighbours.append(boid_data)

        self.direction = self.w_separation * self.calculate_separation(small_radius_neighbours) + self.w_alignment * self.calculate_alignment(large_radius_neighbours) + self.w_cohesion * self.calculate_cohesion(large_radius_neighbours)
        
        if self.direction.length() > 1:
            self.direction = self.direction.normalize()

        self.position += self.direction * self.velocity
        self.angle = 90 - math.degrees(self.direction.heading())

        if self.center_x <= self.radius:
            self.center_x = self.radius
            self.direction = self.direction.reflect(arcade.math.Vec2(1, 0))
        elif self.center_x >= (window_width * 0.8) - self.radius:
            self.center_x = (window_width * 0.8) - self.radius
            self.direction = self.direction.reflect(arcade.math.Vec2(-1, 0))

        if self.center_y <= self.radius:
            self.center_y = self.radius
            self.direction = self.direction.reflect(arcade.math.Vec2(0, 1))
        elif self.center_y >= window_height - self.radius:
            self.center_y = window_height - self.radius
            self.direction = self.direction.reflect(arcade.math.Vec2(0, -1))
