class TargetPractice:
    """A class to manage all the assets and the game behavior """

    def __init__(self):
        """Initialize the game and create game ressources """
        pygame.init()

        self.settings = Settings()
        self.screen  = pygame.display.set_mode(
            (self.settings.screen_width,self.settings.screen_height))
        pygame.display.set_caption("J'y-rens Target Shoot 1.0")

        #Set the clock and time click
        self.clock = pygame.time.Clock()
        self.dt = 0

        # create an instance of the game stats class
        self.stats = GameStats(self)

        #Create an instance of the scoreboard class
        self.sb = Scoreboard(self)

        # Create an instance of the class ship
        self.ship = Ship(self)

        #create a list of bullets that hit the rectangle 
        self.bullets_hit_rectangle = []

        # create an instance of the class rectangle
        self.rectangle  = Rectangle(self)

        # create an instance of the group bullets
        self.bullets = pygame.sprite.Group()

        # create an instance of the button
        self.play_button= Button(self,"Play")
        
        # create an instance of the pause Button
        self.pause_button = Pause(self,"Pause")

    def run_game(self):
        """Start the main loop of the game """ 
        
        while True : 
            # watch for keyboard events
            self._check_events()
            
            if self.stats.game_active:
                # start the  timer on the screen
                self.start_timer()
                
                #update the ship position
                self.ship.update()

                # update the position of the bullet
                self._update_bullets()

                #update the rectangle
                self._update_rectangle()
             
            # Update aliens
            self._update_screen()

    def _check_events(self):
        """Respond to keyboard events """
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                # store the highest score before quiting
                self.write_high_score_to_folder()
                sys.exit()   
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                self._check_play_button(mouse_pos)
                self._check_pause_button(mouse_pos)
    
    def _check_pause_button(self, mouse_pos):
        """Lock the game when the player cliks the pause  button """
        button_clicked = self.pause_button.rect.collidepoint(mouse_pos)
        if button_clicked and self.stats.game_active:
            # pause the game .
            self.stats.game_active = False       
                
    def _check_play_button(self, mouse_pos):
        """Check the play button """
        button_clicked = self.play_button.button_rect.collidepoint(mouse_pos)

        if button_clicked and not self.stats.game_active:
            # Reset the game settings
            self.settings.initialize_dynamic_settings()
            
            # starts the game when the player clicks play and the game is inactive
            if self.stats.chances_left == 0 and self.sb.time == 0 or self.stats.score == 0:
                # Restart the game when the time is 0 seconds 
                # and all the chances are gone 
                self._start_game()

            self.stats.game_active = True
            self.sb.prep_score()
            self.sb.prep_level()
            self.sb.prep_timer()
            self.sb.prep_chances_left()
            
    def start_timer(self):
        """Start the timer when the game starts """
        self.stats.timer -= self.dt
        
        if self.stats.timer <= 0 :
            sleep(1)
            self.stats.resets_timer()
        
        self.sb.prep_timer()
        self.dt = self.clock.tick(200) / 1000  # / 1000 to convert to seconds.
              
    def _start_game(self):
        """Start the game """   
        # set the game to active state 
        self.stats.game_active = True

        # reset the stats 
        self.stats.reset_stats()

        # set the timer back to zero
        self.stats.resets_timer()

        # Empty the bullets 
        self.bullets.empty()

        # center the ship and the rectangle
        self.rectangle.center_rectangle()
        self.ship.center_ship()
    
    def _check_keydown_events(self,event):
        """Respond to keydown events """
        if event.key == pygame.K_UP:
            self.ship.moving_up = True
        elif event.key == pygame.K_DOWN:
            self.ship.moving_down = True
        elif event.key == pygame.K_q: 
            self.write_high_score_to_folder()
            sys.exit()
        elif event.key == pygame.K_SPACE:
            self._fire_bullets()
        elif event.key == pygame.K_p or pygame.K_KP_ENTER:
            if not self.stats.game_active:
                self._start_game()
    
    def write_high_score_to_folder(self):
        """write High score to folder"""
        with open(self.stats.filename, 'w') as f:
                    json.dump(self.stats.high_score, f)
    
    def _fire_bullets(self):
        """Draws bullet on the screen """
        if len(self.bullets) <= self.settings.bullets_allowed:
            new_bullet = Bullet(self)
            self.bullets.add(new_bullet)
            se.bullet_sound.play()

    def _update_bullets(self):
        """Update the position of the bullets """
        self.bullets.update()

        for bullet in self.bullets.copy():
            if bullet.rect.x >= self.settings.screen_width:
                self.bullets.remove(bullet)

        # check bullets and rectangle collisions and update bullets
        self._check_bullet_rectangle_collision()
                
    def _check_bullet_rectangle_collision(self):
        """Respond to bullets and rectangle collision """
        
        bullet = pygame.sprite.spritecollideany( self.rectangle ,self.bullets)
        if  bullet and self.stats.timer > 0:
            
            self.bullets_hit_rectangle.append(bullet)
            self.stats.score += self.settings.rectangle_points
            self.sb.prep_score()
            self.sb.check_high_score()
            
            # detect that the rectangle has been hit
            # and start a new level
            self._rectangle_hit()
            self.start_new_level()

        elif self.sb.time == 0 and not self.bullets_hit_rectangle :
            self._rectangle_not_hit() 
    
    def start_new_level(self):
        """Start a new level if the rectangle has been hit """
        self.settings.increase_speed()
        self.sb.prep_level()
        
    def _check_keyup_events(self,event):
        """Respond to keyup events """
        if event.key == pygame.K_UP:
            self.ship.moving_up = False
        elif event.key == pygame.K_DOWN:
            self.ship.moving_down = False
    
    def _change_movement_direction(self):
        """change the movememnt direction of the rectangle """
        self.settings.movement_direction *= -1

    def _check_rectangle_edges(self):
        """check rectangle edges and change the movement direction """
        if self.rectangle.check_edges():
            self._change_movement_direction()
            
    def _update_rectangle(self):
        """Update rectangle position before"""
        
        self._check_rectangle_edges()
        self.rectangle.update()
                    
    def _rectangle_not_hit(self):
        """Respond when the rectangle has not been hit and the timer has reached 0 """
        if self.stats.chances_left > 0:
            
            #Decrement the number of chances
            self.stats.chances_left -=1
            
            self.sb.prep_chances_left()

            se.miss_sound.play()

            self.bullets.empty()

            # Center the rectangle
            self.rectangle.center_rectangle()

            # resets timer to its initial value
            self.stats.resets_timer()

            sleep(0.5)
        else:
            self.end_of_game()
                    
    def _rectangle_hit(self):
        """Respond when the rectangle is hit """
        if self.stats.chances_left >= 0:

            # Empty the bullets_hit_rectangle list 
            self.bullets_hit_rectangle = []

            #Increment the level and empty the bullets 
            
            self.stats.level +=1
            se.level_increase_sound.play()
            self.bullets.empty()

            # resets the bullets to its orginal 15 seconds
            self.stats.resets_timer()

            # center the rectangle
            self.rectangle.center_rectangle()

            sleep(1)
        else:
            self.end_of_game()
            
    def end_of_game(self):
        """Play end of game sound """
        sleep(2)
        se.end_of_game_sound.play()
        self.stats.game_active = False

    def _update_screen(self):
        """Update the screen anytime an item position is modified on the screen """
        self.screen.fill(self.settings.bg_color)

        #Draw the ship of the screen
        self.ship.blitme()

        #Draw the rectangle to the screen
        self.rectangle.draw_rectangle()

        # Draw bullets on the screen
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()
        
        #Draw the score button
        self.sb.show_score()

        #Draw the play button if the game is not active
        if not self.stats.game_active:
            self.play_button.draw_button()
        else:
            self.pause_button.draw_button()

        pygame.display.flip()
class AlienInvasion:
    """Overall class to manage game assets and behavior."""
    def __init__(self):
        """Initialize the game, and create game resources """
        pygame.init()
        self.settings = Settings()

        self.screen = pygame.display.set_mode(
            (self.settings.screen_width, self.settings.screen_height))
        #self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
        #self.settings.screen_width = self.screen.get_rect().width
        #self.settings.screen_height = self.screen.get_rect().height
        self.caption = "J'y-rens Alien Invasion 1.0"
        pygame.display.set_caption(self.caption)

        #Create an instance to store game statistics.
        # and create a scoreboar.
        self.stats = GameStats(self)
        self.sb = Scoreboard(self)

        self.ship = Ship(self)

        #bullet groups
        self.bullets = pygame.sprite.Group()

        #Alien Groups
        self.aliens = pygame.sprite.Group()

        # create fleet of aliens
        self._create_fleet()

        #Make the Play Button.
        self.play_button = Button(self, "Play")
        self.pause_button = Pause(self, "Pause")

    def run_game(self):
        """Start the main loop for the game."""
        while True:
            #Watch for keyboard and mouse events.
            self._check_events()

            if self.stats.game_active and self.caption == "J'y-rens Alien Invasion 1.0":
                # Update the ship position
                self.ship.update()

                # Update bullets and get rid of old bullets
                self._update_bullets()

                #Update the position of each Alien
                self._update_aliens()

            # Redraw the screen during each pass through the loop and flip to the new screen.
            self._update_screen()

    def _check_events(self):
        """ Respond to keypresses and mouse events """
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.write_high_score_to_folder()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                self._check_play_button(mouse_pos)
                self._check_pause_button(mouse_pos)

    def _check_pause_button(self, mouse_pos):
        """Lock the game when the player cliks the pause  button """
        button_clicked = self.pause_button.rect.collidepoint(mouse_pos)
        if button_clicked and self.stats.game_active:
            # Reset the game settings.
            self.stats.game_active = False
            #self.play_button.draw_button()

    def _check_play_button(self, mouse_pos):
        """Starts a new game when the player clicks Play """
        button_clicked = self.play_button.rect.collidepoint(mouse_pos)
        if button_clicked and not self.stats.game_active:
            # Reset the game settings.
            self.settings.initialize_dynamic_settings()
            # starts the game when the player clicks play and the game is inactive
            if self.stats.score == 0:
                self._start_game()
            self.stats.game_active = True
            self.sb.prep_score()
            self.sb.prep_level()
            self.sb.prep_ships()

    def _check_keydown_events(self, event):
        """ Respond to keypresses """
        if event.key == pygame.K_RIGHT:
            #Move the ship  to the right
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            #Move the ship  to the left
            self.ship.moving_left = True
        elif event.key == pygame.K_q:
            self.write_high_score_to_folder()
            sys.exit()
        elif event.key == pygame.K_SPACE:
            self._fire_bullet()
        elif event.key == pygame.K_p or pygame.K_KP_ENTER:
            if not self.stats.game_active:
                self._start_game()

    def write_high_score_to_folder(self):
        """write High score to folder"""
        with open(self.stats.filename, 'w') as f:
            json.dump(self.stats.high_score, f)

    def _start_game(self):
        """Start the game when the player presses the button"""
        # Reset the game statistics.
        self.stats.reset_stats()
        self.stats.game_active = True

        # Get rid of any remaining aliens and bullets
        self.aliens.empty()
        self.bullets.empty()

        #Create a new fleet and center the ship.
        self._create_fleet()
        self.ship.center_ship()

        # Hide the mouse cursor.
        pygame.mouse.set_visible(True)

    def _check_keyup_events(self, event):
        """ Respond to key releases """
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _fire_bullet(self):
        """Create a new bullet and add it to the bullets group."""
        if len(self.bullets) < self.settings.bullets_allowed:
            new_bullet = Bullet(self)
            self.bullets.add(new_bullet)
            se.bullet_sound.play()

    def _update_bullets(self):
        """Update position of bullets and get rid of old bullets """
        # update the bullets
        self.bullets.update()

        # Get rid of old bullets that have disappeared
        for bullet in self.bullets.copy():
            if bullet.rect.bottom <= 0:
                self.bullets.remove(bullet)

        # Check for any bullets that have hit aliens.
        # If so , get rid of the alien and the bullet.
        self._check_bullet_allien_collisions()

    def _check_bullet_allien_collisions(self):
        """ Respond to bullet alien collisions """
        # Remove any bullets an allien that have collided
        collisions = pygame.sprite.groupcollide(self.bullets, self.aliens,
                                                True, True)
        if collisions:
            for aliens in collisions.values():
                self.stats.score += self.settings.alien_points * len(aliens)
            self.sb.prep_score()
            self.sb.check_high_score()
            se.alien_sound.play()

        if not self.aliens:
            # Destroy existing bullets and create new fleet
            self.start_new_level()

    def start_new_level(self):
        """Start a new level if aliens have been destroyed  """
        self.bullets.empty()
        self._create_fleet()
        self.settings.increase_speed()

        # Increase level.
        self.stats.level += 1
        self.sb.prep_level()

    def _create_fleet(self):
        """ Create fleet of Aliens """
        # create an Alien and find the number of aliens in a row
        # Spacing between each alien is equal to one alien width
        alien = Alien(self)

        alien_width = alien.rect.width
        alien_height = alien.rect.height

        # Determine the number columns that fit the aliens on the screen
        available_space_x = self.settings.screen_width - (2 * alien_width)
        number_aliens_x = available_space_x // (2 * alien_width)

        # Determine the number of rows that fit the Alien on the screen
        ship_height = self.ship.rect.height
        available_space_y = (self.settings.screen_height - (3 * alien_height) -
                             ship_height)
        number_rows = available_space_y // (2 * alien_height)

        # create the full feet of aliens
        for row_number in range(number_rows):
            for alien_number in range(number_aliens_x):
                self._create_alien(alien_number, row_number)

    def _create_alien(self, alien_number, row_number):
        """ Create an Instance of an Alien """
        #Create an alien and place it in the row  .
        alien = Alien(self)
        alien_width = alien.rect.width
        alien_height = alien.rect.height
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.y = alien_height + 2 * alien_height * row_number
        alien.rect.x = alien.x
        alien.rect.y = alien.y
        self.aliens.add(alien)

    def _check_fleet_edges(self):
        """Respond Appropriatly  if any aliens have reached the edge """
        for alien in self.aliens.sprites():
            if alien.check_edges():
                self._change_fleet_direction()
                break

    def _change_fleet_direction(self):
        """Drop the entire fleet and change the fleet's direction """
        for alien in self.aliens.sprites():
            alien.rect.y += self.settings.fleet_drop_speed
        self.settings.fleet_direction *= -1

    def _ship_hit(self):
        """Respond to the ship being hit by an alien """

        if self.stats.ships_left > 0:
            se.ship_hit_sound.play()

            # Decrement ships_left and update scoreboard
            self.stats.ships_left -= 1
            self.sb.prep_ships()

            # Get rid of any remaning aliens and bullets
            self.aliens.empty()
            self.bullets.empty()

            #Create a new fleet and center the ship
            self._create_fleet()
            self.ship.center_ship()

            # Pause.
            sleep(0.5)
        else:
            self.stats.game_active = False
            pygame.mouse.set_visible(True)

    def _check_aliens_bottom(self):
        """Check if aliens have reached the bottom of the screen """
        screen_rect = self.screen.get_rect()
        for alien in self.aliens.sprites():
            if alien.rect.bottom >= screen_rect.bottom:
                # Treat it the same way as if a ship got hit
                self._ship_hit()
                break

    def _update_aliens(self):
        """Update the positions of all aliens in the fleet """

        self._check_fleet_edges()
        self.aliens.update()

        # Look for alien-ship collisions .
        if pygame.sprite.spritecollideany(self.ship, self.aliens):
            self._ship_hit()

        #Look for aliens hitting the bottom of the screen
        self._check_aliens_bottom()

    def _update_screen(self):
        """ Update images on the screen, and flip to the new screen."""
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()
        # Make the most recently drawn screen visible.
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()
        self.aliens.draw(self.screen)

        # Draw the score information
        self.sb.show_score()

        # Draw the play button if the game is inactive
        if not self.stats.game_active:
            self.play_button.draw_button()
        else:
            self.pause_button.draw_button()

        pygame.display.flip()