#heroes.py
#import the libraries needed
import pygame
import random
from settings import * #imported the settings from the settings python file

class Hero: #define a class could Hero, and all heroes will share attributes and methods (behaviors) here
    def __init__(self, color): #this is a function that initializes a hero 

        self.radius = 12 #each hero has a default radius of 12 units
        
        #basically generates random float for the x position of the hero inside the safezone. I started at self.radius
        #and ended at safezone_width - radius because I want the hero to be fully inside the safezone.
        x = random.uniform(self.radius, SAFEZONE_WIDTH - self.radius) 

        #this line is same as above, generates a random y position for the hero that's fully inside the safezone.
        y = random.uniform(WALL_THICKNESS + self.radius,
                       HEIGHT - WALL_THICKNESS - self.radius)
        
        self.pos = pygame.Vector2(x, y) #initial position saved as an instance variable pos, pygame vector2 is basically just a tuple with 2 numbers, 
        #but it supports array arithmatics which might have benefits so I used it
        
        self.color = color #sets hero color the the inputted color
        
        self.move_timer = 0 #the move timer is a timer that controls how long the hero moves in one direction (i should have called it change_direction_timer)
        #this was a result of my attempts to make the movement took more "random" than just the hero jiggling in 
        #a straight line going to the right because otherwise it changes a random direction every frame

        self.alive = True #alive means the hero is alive

        self.downed = False #downed means the hero has been hit, but not fully dead yet and can still be revived
        
        self.downed_timer = DOWNED_TIME #this aspect of how long heroes are down didn't really matter in the end
        #but this instance variable keeps track of how many seconds the hero has left before it fully dies

        self.vel = pygame.Vector2(0, 0) #similar to the position 2-number vector, this is for the original velocity of the hero


    def update(self, dt): #this function will update each hero's position every frame

        if self.downed or not self.alive: #checks if the hero is able to move, which means it's alive and not downed
            return #return would just skip this function and not update hero if its not able to move
        
        self.move_timer -= dt #the number of seconds the hero has to move in the same direction is subtracted by dt
        #dt is created in main.py(), which calls this update function, dt is the number of seconds that passed since
        #the last frame. dt is slightly different everytime cuz my computer does not run 60 fps consistently in the simulation
        #thus dt basically keeps track of the REAL time because frame rate can't be trusted to calculate real time.

        #----------------------------------------------------------------
        #THE FOLLOWING LINES IS WHERE RANDOM WALK IS IMPLEMENTED, I KIND OF USED MY OWN VERSION THAT WAS MORE COMPLICATED
        #THAN JUST 20% chance go backward, instead direction and speed are a spectrum from minimum to maximum and any
        #direction or speed is possible. Omnidirectional might be the word here

        if self.move_timer <= 0: #check if move_timer is less than or equal to 0, which means hero is allowed to change direction
            self.move_timer = random.uniform(0.05, 0.4) #sets the new move timer to a random float between 0.05 to 0.4 seconds
            self.vel = pygame.Vector2(0, 0) #reset velocity

            forward_prob = random.random() #random value between 0 and 1 to decide how much left/right the hero's new velocity should be (left/right direction (right is forward))
            up_down_prob = random.random() #random value between 0 and 1 to decide how much up or down the hero's new velocity should be (up/down direction)
            speed_prob = random.random() #random value between 0 and 1 to decide how fast the hero should be this time

            speed_prob *= 100 #now speed_prob is between 0 and 100, which is a more appropriate speed
            new_speed = speed_prob + HERO_SPEED #the new speed becomes base speed + the randomly generated speed. 
            #If base speed is 200, the new speed can be any value between 200 and 300.
            #However this is not the final x or y speed of the hero, because this "new_speed" is multiplied into
            #up/down and left/right probabilities below to get the final x and y velocity. 
            #true x velocity would only be at maximum if the left/right probabiltiy below is at maximum


            #I originally used a simple percentage method for random speed, which I wanted the heros to be able to have any
            #speed possible within a certain range, so the code here is commented out.

            # if speed_prob < 0.49:
            #     new_speed = PLAYER_SPEED
            # elif speed_prob < 0.69:
            #     new_speed = PLAYER_SPEED * 2
            # else:
            #     new_speed = PLAYER_SPEED * 4



            #forward probability is now between -0.6 and 1.4. Each number is a multipler for the new speed
            #except a negative multipler means the hero goes backward, so in a way this means that the hero has
            #30% chance of going backward and 70% chance of going forward, except how fast it goes forward/backward also depends on this percentage
            forward_prob = forward_prob * 2 - 0.6
            self.vel.x = new_speed * forward_prob
            #example: if forward_prob is 0, that means the hero will not be moving for the next move_timer seconds because 0
            # multipled by new_speed is 0. 


            #below is commented out old code using a simple version of random walks

            # if forward_prob < 0.7:
            #     self.vel.x =  new_speed
            # else:
            #     self.vel.x = -new_speed

            #The 2 lines here achieve the same purpose as left/right (forward) probability. It essentially means there is a
            #50% chance the hero moves up, and a 50% chance the hero moves down, but how fast it moves in either direction 
            #also depends on the multiplier (up_down_prob), where negative multipliers mean moving up. 
            up_down_prob = up_down_prob * 2 - 1
            self.vel.y = new_speed * up_down_prob

            #below is commented out old code using a simple version of random walks

            # if up_down_prob < 0.33:
            #     self.vel.y = -new_speed
            # elif up_down_prob < 0.67:
            #     self.vel.y = 0
            # else:
            #     self.vel.y = new_speed




        #set the hero's new position be velocity, with how much it moves depending on dt, this new position
        #will be shown in the next frame. 
        
        self.pos += self.vel * dt
        
        #code below will clamp heroes to the walls
        #and make sure they don't go outside the walls. 
        
        #checks if y position is outside top wall, if it is, set y to fully within top wall
        if self.pos.y - self.radius < WALL_THICKNESS:
            self.pos.y = WALL_THICKNESS + self.radius
        
        #checks if y position is outside bottom wall, if it is, set y to fully within bottom wall
        if self.pos.y + self.radius > HEIGHT - WALL_THICKNESS:
            self.pos.y = HEIGHT - WALL_THICKNESS - self.radius

        

    def draw(self, screen): #this function actually draws the hero onto the screen
        if not self.downed: #check if hero is downed
            color = self.color #heroes that are alive are its alive color
        else:
            color = (255, 150, 150) #downed heroes are light pink

        pygame.draw.circle(screen, color, self.pos, self.radius) #pygame command to draw circles (the heroes) onto the screen
        #this command takes in the screen, color of the circle, position of the circle (x, y), and radius of the circle (12)

    def take_hit(self): #this function is called when the hero gets hit by an enemy, in which case its self.downed
        #variable will be changed to true. If the hero is already down, then this function gets ignored.
        if not self.downed:
            self.downed = True
            self.downed_timer = DOWNED_TIME

    #this function updates the hero's downed timer, heroes that reach 0 seconds left are fully dead
    def tick_downed(self, dt):
        if self.downed: #check if hero is downed
            self.downed_timer -= dt #downed hero timer decreases by dt (time that passed since last frame)
            if self.downed_timer <= 0: #is downed hero timer less than or equal to 0?
                self.alive = False #hero fully dies 

    #this function lets heroes try to revive other heroes
    def try_revive(self, others):
        if not self.alive or self.downed: #if hero is dead or hero is downed, then this is skipped cuz it can't revive others
            return

        for other in others: #for loop to loop through every other hero
            if other is self: #can not revive itself
                continue

            if other.downed: #check if each "other" hero is downed
                if self.pos.distance_to(other.pos) <= REVIVE_RADIUS: #check if the downed "other" hero is within revival distance
                    other.downed = False #if statment above is true, "other" gets revived, which means other.downed = false
                    other.alive = True #gets revived, so other.alive = true
                    other.downed_timer = DOWNED_TIME #downed timer of "other" gets reset because it's no longer downed