import pygame
from pygame.locals import *
import random

pygame.init()

#implementing timer
clock = pygame.time.Clock()
fps = 60

#setting screen size based on image assets
screen_width = 864
screen_height = 786

#creating game window
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Flappy Bird')

#font
font = pygame.font.SysFont('Bauhaus 93', 60)

#color
white = (255, 255, 255)

#game variables
ground_scroll = 0
scroll_speed = 4 # 4 pixels
flying = False #required to determine the start of gameplay
game_over = False
pipe_gap = 150
pipe_frequency = 1500 #miliseconds
last_pipe = pygame.time.get_ticks() - pipe_frequency
score = 0
pass_pipe = False
#my addition
flappy_collided = False

#loading game assets
bg = pygame.image.load('img/bg.png')
ground_img = pygame.image.load('img/ground.png')
button_img = pygame.image.load('img/restart.png')
die_fx = pygame.mixer.Sound('sound_effects/die.mp3')
flap_fx = pygame.mixer.Sound('sound_effects/flap.mp3')
hit_fx = pygame.mixer.Sound('sound_effects/hit.mp3')
restart_fx = pygame.mixer.Sound('sound_effects/restart.mp3')
score_fx = pygame.mixer.Sound('sound_effects/score.mp3')


#function to handle the display of score onto the screen
def draw_text(text, font, text_col, x, y):
    img = font.render(text, True, text_col)
    screen.blit(img, (x, y))

def reset_game():
    pipe_group.empty()
    flappy.rect.x = 100
    flappy.rect.y = int(screen_height  /  2)
    score = 0 #this is local variable, wouldn't affect the global so we will
    #return the score so it can be used globally after
    return score

#using classes to create the bird
class Bird(pygame.sprite.Sprite):
    def __init__(self, x, y):
        #add a function to inheret update, draw, etc function from sprite classes
        pygame.sprite.Sprite.__init__(self)
        self.images = []
        self.index = 0
        self.counter = 0 #to control animation speed
        for num in range(1, 4):
            img = pygame.image.load(f'img/bird{num}.png')#load images for bird
            self.images.append(img)
        self.image = self.images[self.index]
        self.rect = self.image.get_rect() #create a rectangle bounding box around the image
        self.rectangles = []
        self.rect.center = [x, y] #setting the starting point of the rectangle
        self.vel = 0 #velocity of the bird (up and down motion)
        self.clicked = False #checking when the mouse is released
    
    #overwriting the sprite update function
    def update(self):
        
        if flying == True: #start movement on first mouse click
            #handle velocity/movemnet/gravity of the bird
            self.vel += 0.5 #increase velocity of bird at every iteration
            if self.vel > 8: #adding gravity
                self.vel = 8
            if self.rect.bottom < 618:
                self.rect.y += int(self.vel) # if the  bird is above ground it will increase in height
                #relative to the rate of increase in velocity for each mouse click
        
        if game_over == False:
            #jumping
            if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False: #returning a array of mouse buttons (selecting 1 for left mouse button)
                self.clicked = True
                self.vel = -8
                flap_fx.play()
            if pygame.mouse.get_pressed()[0] == 0:
                self.clicked = False

            #handle the animation (cycling of images to immitate bird flap)
            self.counter += 1 #increase image iteration
            flap_cooldown = 5

            if self.counter > flap_cooldown:
                self.counter = 0  #start loop counter
                self.index += 1 #increment through images
                
                if self.index >= len(self.images): #keep index to the max of images in array
                    self.index = 0
            self.image = self.images[self.index]

            #rotate the bird
            self.image = pygame.transform.rotate(self.images[self.index], self.vel * -2)

        else:

            #play hit sound on collision
            # if flappy_collided == True:
            #     self.rect.right = 0
                
                
                #hit_fx.play()
                

            #rotate the bird to the dead position
            self.image = pygame.transform.rotate(self.images[self.index], -90)

            if self.rect.bottom >= 618 and flying == True:
                die_fx.play()
        


class Pipe(pygame.sprite.Sprite):
    def __init__(self, x, y, position):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('img/pipe.png')
        self.rect = self.image.get_rect()
        #position 1 means top and -1 means bottom
        if position == 1:
            self.image = pygame.transform.flip(self.image, False, True)
            self.rect.bottomleft = [x, y - int(pipe_gap / 2 )]
        else:
            self.rect.topleft = [x, y + int(pipe_gap / 2 )]

    def update(self):
        self.rect.x -= scroll_speed #pipes moving at scroll speed
        if self.rect.right < 0:  #if right side of rect or x coord is less than zero
            self.kill()

class Button():
    def __init__(self, x, y, image):
        self.image = image
        self.rect = self.image.get_rect()
        self.rect.topleft = (x, y)

    def draw(self):

        action = False

        #get mouse position
        pos = pygame.mouse.get_pos()

        #check if mouse is over the button by checking for collision
        if self.rect.collidepoint(pos):
            if pygame.mouse.get_pressed()[0] == 1: #checking that mouse was clicked
                action = True

        #draw button
        screen.blit(self.image, (self.rect.x, self.rect.y))

        return action #returning a confirmation that the mouse was clicked to restart the game

#group to keep track of the sprites being added to it
#its like a python list
bird_group = pygame.sprite.Group()
pipe_group = pygame.sprite.Group()

flappy = Bird(100, int(screen_height / 2))
bird_group.add(flappy)

#dealing with the removal of the rectangle on the bird
#to prevent looping of the hit sound
rect1 = flappy.rect
rectangles = [rect1]
removed_rect1 = None

#create restart button instance
button = Button(screen_width // 2 - 50, screen_height // 2 - 100, button_img)

#play sound on collision
if flappy_collided == True:
    hit_fx.play()


#game play loop  starts  here
run = True

while run:

    #telling the clock what speed to tick at in order
    #to slow the scroll speed
    clock.tick(fps)

    #blit function is being called to load the bg image to screen
    screen.blit(bg, (0,-150))

    #add bird to screen
    bird_group.draw(screen)
    bird_group.update()

    #add pipe to screen
    pipe_group.draw(screen)

    #draw ground
    screen.blit(ground_img, (ground_scroll, 618))

    #checking score  by measuring the bird's travel through each pipe
    #by sensing when its bounding rectangle passes the right and left bounding
    #rectangle  of the pipes and incrementing the score variable by 1 each time

    if len(pipe_group) > 0: #ensuring that pipes have been create before starting
        if bird_group.sprites()[0].rect.left > pipe_group.sprites()[0].rect.left\
            and bird_group.sprites()[0].rect.right < pipe_group.sprites()[0].rect.right\
            and pass_pipe == False:
            pass_pipe = True
        if pass_pipe == True:
            if bird_group.sprites()[0].rect.left > pipe_group.sprites()[0].rect.right:
                score += 1
                score_fx.play()
                pass_pipe = False

    draw_text(str(score), font, white, int(screen_width/2), 20)
        

    #collision - checking
    if pygame.sprite.groupcollide(bird_group, pipe_group, False, False) or flappy.rect.top < 0:
        flappy_collided = True

        for r1 in rectangles[:]:
            hit_fx.play()
            removed_rect1 = r1 #sending this value to the reset process
            rectangles.remove(rect1)
            break
        
        game_over = True
    
   
    #check if the bird hit the ground
    if flappy.rect.bottom >= 618:
        game_over = True
        flying = False
       
        

    #stop scrolling the ground if game is over
    if game_over == False and flying == True:

        #generate new pipes
        time_now = pygame.time.get_ticks()
        if time_now - last_pipe > pipe_frequency:
            pipe_height = random.randint(-100, 100)
            btm_pipe = Pipe(screen_width, int(screen_height/2) + pipe_height, -1)
            top_pipe = Pipe(screen_width, int(screen_height/2) + pipe_height, 1 )
            pipe_group.add(btm_pipe)
            pipe_group.add(top_pipe)
            last_pipe = time_now  #set last pipe to now (being the last time a pipe was created on each iteration)

        #scoll ground
        ground_scroll -= scroll_speed

        #repeating the ground image when it scrolls out of view
        if abs(ground_scroll) > 35: #negative value converted to absolute
            ground_scroll = 0

        pipe_group.update()
        
        

    #check for game over and reset
    if game_over == True:
        if button.draw() == True:
            restart_fx.play()
            game_over = False
            rectangles.append(removed_rect1) #replacing the rectangle on the bird
            score = reset_game() #calling the reset function to reset gameplay and return the score value
            #which is being saved to the global score varibale using this same expression

    #looking for game events to determin gameplay
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.MOUSEBUTTONDOWN and flying == False and game_over == False: #determing game should start on first mouse click
            flying = True

    #update function called to update entire UI is everything in
    #the loop above it
    pygame.display.update()

pygame.quit()
    