import pygame, sys
from pygame.locals import *
import random
 
pygame.init()

FPS = 60
FramePerSec = pygame.time.Clock()

BLACK  = (0, 0, 0)
WHITE = (255, 255, 255)
YELLOW = (255, 204, 0)

# --------------------------------------------------------------------------------------------
# GAME MODES / GAME STATES / OTHER STUFF IN THAT DIRECTION
# --------------------------------------------------------------------------------------------

# game modes
MAIN_MENU : bool = True
DOODLE_JUMP : bool = False
GOOGLE_DINO : bool = False

HOME_SCREEN : bool = True
GAME_SELECTION : bool = False

# game states
GAME_RUNNING : bool = False
GAME_PAUSED : bool = False
GAME_OVER : bool = False
RESTARTING : bool = False

# --------------------------------------------------------------------------------------------
# OTHER VARS
# --------------------------------------------------------------------------------------------

# Screen information
RES : tuple = 1100, 619

# Game related
start_counter : int = 0
stop_counter : bool = False
dino_is_scrolling : bool = False
scrollable : bool = True
IMMORTAL : bool = False
reset_player : bool = False
SCORE : int = 0

# dino specific
cactus_type : int = 0

# Velocities
JUMP_SPEED : int = 20
FALL_SPEED : int = 2
DOODLE_BASE_TILE_SPEED : float = 15.0

#Setting up Fonts
font_score_in_game = pygame.font.SysFont("Determination Sans", 20)
font_score_game_over = pygame.font.SysFont("Determination Sans", 60)
font_distance = font_score_in_game.render("m", True, BLACK)
font_score_str = font_score_game_over.render("SCORE:", True, BLACK)
font_restart_game = font_score_game_over.render("Press R to restart", True, BLACK)

# setting up images
background_dino = pygame.image.load("bg_sky.png")
background_doodle = pygame.image.load("doodle_bg2.png")
background_dino_static = pygame.image.load("bg_static.png")
pause_screen = pygame.image.load("pause_screen_nobg.png")
game_over_font = pygame.image.load("gameove_red_big.png")
title_img = pygame.image.load("title_game.png")
title_bg = pygame.image.load("bg_static_no_shadow.png")

# --------------------------------------------------------------------------------------------
# MUSIC AND OTHER FUNCS
# --------------------------------------------------------------------------------------------

def play_music():
    pygame.mixer.music.load("mexican music loop.mp3")
    pygame.mixer.music.play(-1)

# --------------------------------------------------------------------------------------------
# BASIC GAME SETUP
# --------------------------------------------------------------------------------------------

DISPLAYSURF = pygame.display.set_mode(RES)
DISPLAYSURF.fill(WHITE)
pygame.display.set_caption("El Mexicano Y Su Bicicleta")

def load_image(name):
    image = pygame.image.load(name)
    return image

#---------------------------------------------------------------------------------------------
# CLASSES: BUTTON
#-----------------------------------------------------------------------------------------------

button_entities = []
button_entities_selection = []
button_entities_in_game = []

class Button():
    def __init__(self, POS_X_Y : tuple, SPRITE_NORMAL : str, SPRITE_HOVER : str, SPRITE_PRESSED : str, LIST_NUMBER : int, on_click_function = None, ONE_PRESS : bool = False):
        
        self.on_click_function = on_click_function
        self.one_press = ONE_PRESS

        self.sprites = []
        self.sprites.append(load_image(SPRITE_NORMAL))
        self.sprites.append(load_image(SPRITE_HOVER))
        self.sprites.append(load_image(SPRITE_PRESSED))

        self.index = 0
        self.image = self.sprites[self.index]
        self.rect = self.image.get_rect()

        self.rect.center = (POS_X_Y)

        self.already_pressed : bool = False
        if LIST_NUMBER == 1:
            button_entities.append(self)
        if LIST_NUMBER == 2:
            button_entities_selection.append(self)
        if LIST_NUMBER == 3:
            button_entities_in_game.append(self)
        

    def process(self):
        self.image = self.sprites[self.index]

        mousePos = pygame.mouse.get_pos()
        if self.rect.collidepoint(mousePos):
            self.index = 1
            if pygame.mouse.get_pressed(num_buttons=3)[0]:
                self.index = 2
                if self.one_press:
                    self.on_click_function()

                elif not self.already_pressed:
                    self.on_click_function()
                    self.already_pressed = True

            else:
                self.already_pressed = False
        else:
            self.index = 0

def exit_button_func():
    pygame.quit()
    sys.exit()

def exit_game_button_func():
    global GAME_RUNNING
    global GAME_OVER
    global MAIN_MENU
    global GOOGLE_DINO
    global DOODLE_JUMP
    global HOME_SCREEN
    global GAME_SELECTION
    global RESTARTING

    RESTARTING = True
    GAME_OVER = False
    GAME_RUNNING = False
    GOOGLE_DINO = False
    DOODLE_JUMP = False
    MAIN_MENU = True
    GAME_SELECTION = False
    HOME_SCREEN = True


def start_button_func():
    global HOME_SCREEN
    global GAME_SELECTION

    HOME_SCREEN = False
    GAME_SELECTION = True

def doodle_game_mode_func():
    global HOME_SCREEN
    global GAME_SELECTION
    global GAME_RUNNING
    global MAIN_MENU
    global GOOGLE_DINO
    global DOODLE_JUMP
    global RESTARTING
    
    RESTARTING = True
    HOME_SCREEN = True
    GAME_SELECTION = False
    MAIN_MENU = False
    GAME_RUNNING = True
    GOOGLE_DINO = False
    DOODLE_JUMP = True

def dino_game_mode_func():
    global HOME_SCREEN
    global GAME_SELECTION
    global GAME_RUNNING
    global MAIN_MENU
    global GOOGLE_DINO
    global DOODLE_JUMP
    global RESTARTING

    RESTARTING = True
    HOME_SCREEN = True
    GAME_SELECTION = False
    MAIN_MENU = False
    GAME_RUNNING = True
    GOOGLE_DINO = True
    DOODLE_JUMP = False


B_EXIT = Button((550, 500), "exit_button.png", "exit_button_hover.png", "exit_button_pressed.png", 1, exit_button_func)
B_START = Button((550, 370), "start_button.png", "start_button_hover.png", "start_button_pressed.png", 1, start_button_func)
B_EXIT_GAME = Button((323, 500), "exit_button.png", "exit_button_hover.png", "exit_button_pressed.png", 3, exit_game_button_func)
B_DOODLE = Button((800, 400), "doodle_button.png", "doodle_button_hover.png", "doodle_button_pressed.png", 2, doodle_game_mode_func)
B_DINO = Button((300, 400), "dino_button.png", "dino_button_hover.png", "dino_button_pressed.png", 2, dino_game_mode_func)

# --------------------------------------------------------------------------------------------
# CLASSES: SCROLLABLE ELEMENTS
# --------------------------------------------------------------------------------------------

cactus_objects = []
cactus_objects_hazards = []

class Scrollable_Elements(pygame.sprite.Sprite):
    def __init__(self, POS_X_Y : tuple, SPRITE_NAME : str, HAZARD : bool, SPEED : float, WAIT_TIME : int):
        super().__init__() 
        self.image = pygame.image.load(SPRITE_NAME)
        self.rect = self.image.get_rect()
        self.rect.center = (POS_X_Y)
        if HAZARD == True:
            self.rect.left = (1100)
            cactus_objects_hazards.append(self)
        else:
            cactus_objects.append(self)
        self.counter = 0
        self.scroll_speed : float = SPEED
        self.starting_speed : float = SPEED
        self.sprite : str = SPRITE_NAME
        self.is_hazard : bool = HAZARD
        self.wait_time = WAIT_TIME
    
    def handle_reset_dino(self):
        global SCORE
        self.scroll_speed = self.starting_speed
        SCORE = 0
        if self.is_hazard == True:
            self.rect.left = 1100
        
    def handle_scroll_h(self):
        global scrollable
        self.counter += 1
        if self.counter >= self.wait_time:
            self.counter = 0
            self.rect.move_ip(-self.scroll_speed, 0)
        if self.rect.right < 0:
            self.rect.left = (1100)
            scrollable = True
            if self.is_hazard == True:
                if self.scroll_speed < 20.0:
                    self.scroll_speed += 0.5
                    print(self.scroll_speed)

# --------------------------------------------------------------------------------------------
# CLASSES: DOODLE TILES
# --------------------------------------------------------------------------------------------

tile_moving_down : bool = False
tile_moving_up : bool = True
tile_colliding : bool = False
player_fall : bool = False

off_screen_counter : int = 0

doodle_objects = []

class Doodle_Tiles(pygame.sprite.Sprite):
    def __init__(self, POS_X_Y : tuple, O_NAME : str, SPEED : float=15.0, COLLISION_TILE : bool=True):
        super().__init__() 
        self.image = pygame.image.load("TILE.png")
        self.rect = self.image.get_rect()
        self.rect.center = (POS_X_Y)
        self.name : str = O_NAME
        self.interactable : bool = COLLISION_TILE
        if COLLISION_TILE == True:
            doodle_objects.append(self)
        self.jump_speed : float = SPEED
        self.counter : int = 0

    def reset_speed(self):
        self.jump_speed = 15.0

    def handle_move_tiles(self):
        global tile_moving_down
        global tile_moving_up
        global tile_colliding
        global off_screen_counter
        global player_fall
        global SCORE

        if tile_moving_up == True:
            tile_moving_down = False
            self.rect.move_ip(0, -self.jump_speed)
            off_screen_counter += 1
            if self.jump_speed <= 20.0:
                self.jump_speed *= 1.2
        
        if tile_moving_down == True:
            if self.jump_speed > 1:
                self.rect.move_ip(0, self.jump_speed)
                self.jump_speed *= 0.93
                if self.jump_speed < 1:
                    self.jump_speed = 1
                    tile_moving_up = True
                    tile_moving_down = False
                
                if self.rect.top > 700:
                    self.rect.center = (550 + random.randint(-550, 550), -10)
                    SCORE += 5
            
        if off_screen_counter >= 550:
            player_fall = True

    def handle_reset_doodle(self):
        if self.name == "T1":
            self.rect.center = (300, 500)
        if self.name == "T2":
            self.rect.center = (800, 400)
        if self.name == "T3":
            self.rect.center = (500, 300)
        if self.name == "T4":
            self.rect.center = (700, 200)
        if self.name == "T5":
            self.rect.center = (400, 100)
        if self.name == "T6":
            self.rect.center = (300, 0)
        if self.name == "T7":
            self.rect.center = (0, 600)

# --------------------------------------------------------------------------------------------
# CLASSES: PLAYER
# --------------------------------------------------------------------------------------------

# states and what not
IS_JUMPING : bool = False
MOVE_UP : bool = False
MOVE_DOWN : bool = False

stop_game : bool = False

player = []

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        global GOOGLE_DINO
        global DOODLE_JUMP 

        self.sprite = []
        self.sprite.append(load_image("Player1.png"))
        self.sprite.append(load_image("Player2.png"))
        self.sprite.append(load_image("Player3.png"))
        self.sprite.append(load_image("Player4.png"))
        self.counter = 0
        self.score_countdown = 0

        self.index = 0
        self.image = self.sprite[self.index]
        self.rect = self.image.get_rect()
        if GOOGLE_DINO == True:
            self.hitbox = pygame.Rect(1, 1, 238, 110)
            self.hitbox.center = (-100, -100)
            self.rect.center = (160, 380)
        if DOODLE_JUMP == True:
            self.hitbox = pygame.Rect(1, 1, 180, 334)
            self.rect.center = (500, 350)

        player.append(self)

    def handle_reset(self):
        global GOOGLE_DINO
        global DOODLE_JUMP

        if GOOGLE_DINO == True:
            self.rect.center = (160, 380)
        if DOODLE_JUMP == True:
            self.rect.center = (500, 350)

    def handle_jump(self):
        global reset_player
        global IS_JUMPING
        global JUMP_SPEED
        global FALL_SPEED
        if IS_JUMPING == False:
            pressed_keys = pygame.key.get_pressed()
            if pressed_keys [K_SPACE]:
                if JUMP_SPEED > 1:
                    self.rect.move_ip(0, -JUMP_SPEED)
                    JUMP_SPEED *= 0.93
                if JUMP_SPEED < 1:
                    IS_JUMPING = True
            else:
                IS_JUMPING = True
        else:
            if self.rect.bottom < 540:
                self.rect.move_ip(0, FALL_SPEED)
                FALL_SPEED *= 1.1
            if self.rect.bottom > 539:
                self.rect.center = (160, 380)
                IS_JUMPING = False
                JUMP_SPEED = 20
                FALL_SPEED = 2

    def handle_left_right(self, MOVE_SPEED : int):
        pressed_keys = pygame.key.get_pressed()
        if pressed_keys [K_a]:
            self.rect.move_ip(-MOVE_SPEED, 0)
            if self.rect.right < (0):
                self.rect.left = (1100)
        if pressed_keys [K_d]:
            self.rect.move_ip(MOVE_SPEED , 0)
            if self.rect.left > (1100):
                self.rect.right = (0)

    def handle_animation(self):
        self.counter += 1
        if self.counter >= 4:
            self.counter = 0
            self.index += 1
        if self.index >= len(self.sprite):
            self.index = 0
        self.image = self.sprite[self.index]

    def handle_doodle_game_over(self):
        global stop_game
        self.rect.move_ip(0, 25)
        if self.rect.top >= 1200:
            stop_game = True

    def handle_score_dino(self):
        global SCORE
        self.score_countdown += 1
        if self.score_countdown >= 7:
            SCORE += 1
            self.score_countdown = 0

# --------------------------------------------------------------------------------------------
# CREATING OBJECTS
# --------------------------------------------------------------------------------------------

HBG3_1 = Scrollable_Elements((1100, 347), "dune.png", False, 1, 0) #dune layer
HBG3_2 = Scrollable_Elements((1800, 267), "dune2.png", False, 1, 0)
HBG2_1 = Scrollable_Elements((1100, 315), "smallest_cactus.png", False, 2, 0) # small cactus
HBG2_2 = Scrollable_Elements((1400, 320), "bg_cactus.png", False, 2, 1) # small cactus
HBG1_1 = Scrollable_Elements((1100, 380), "middle_ground_cactus.png", False, 3, 0) # big cactus
BG_CLOUDS1 = Scrollable_Elements((550, 309), "bg_clouds.png", False, 1, 2)
BG_CLOUDS2 = Scrollable_Elements((1650, 309), "bg_clouds.png", False, 1, 2)
P1 = Player()
C1 = Scrollable_Elements((1100, 460), "default_cactus.png", True, 10, 0)
C2 = Scrollable_Elements((1100, 500), "flat_wide_cactus.png", True, 10, 0)
C3 = Scrollable_Elements((1100, 520), "small_round_cactus.png", True, 10, 0)
C4 = Scrollable_Elements((1100, 460), "tall_slim_cactus.png", True, 10, 0)
T1 = Doodle_Tiles((300, 500), "T1")
T2 = Doodle_Tiles((800, 400), "T2")
T3 = Doodle_Tiles((500, 300), "T3")
T4 = Doodle_Tiles((700, 200), "T4")
T5 = Doodle_Tiles((100, 100), "T5")
T6 = Doodle_Tiles((100, 0), "T6")
T7 = Doodle_Tiles((100, 600), "T7")

# --------------------------------------------------------------------------------------------
# GROUPS
# --------------------------------------------------------------------------------------------

players = pygame.sprite.Group()
players.add(P1)

# collision group
hazards = pygame.sprite.Group()
hazards.add(C1)
hazards.add(C2)
hazards.add(C3)
hazards.add(C4)

# draw group
bg_objects = pygame.sprite.Group()
bg_objects.add(BG_CLOUDS1)
bg_objects.add(BG_CLOUDS2)

# draw and collision group
doodle_tile = pygame.sprite.Group()
doodle_tile.add(T1)
doodle_tile.add(T2)
doodle_tile.add(T3)
doodle_tile.add(T4)
doodle_tile.add(T5)
doodle_tile.add(T6)
doodle_tile.add(T7)

# draw group
main_menu_background = pygame.sprite.Group()
main_menu_background.add(HBG3_1)
main_menu_background.add(HBG3_2)
main_menu_background.add(HBG2_1)
main_menu_background.add(HBG2_2)
main_menu_background.add(HBG1_1)

# draw group
dino_sprites = pygame.sprite.Group()
dino_sprites.add(HBG3_1)
dino_sprites.add(HBG3_2)
dino_sprites.add(HBG2_1)
dino_sprites.add(HBG2_2)
dino_sprites.add(HBG1_1)
dino_sprites.add(C1)
dino_sprites.add(C2)
dino_sprites.add(C3)
dino_sprites.add(C4)

# --------------------------------------------------------------------------------------------
# GENERAL FUNCS
# --------------------------------------------------------------------------------------------

def reset_doodle_speed_all():
    for entity in doodle_objects:
        entity.reset_speed()

    if RESTARTING == True:
        for entity in doodle_objects:
            entity.handle_reset_doodle()

def handle_collision_detection():
    global tile_colliding
    global tile_moving_down
    global tile_moving_up
    global off_screen_counter

    if tile_moving_up == True:
        if pygame.sprite.spritecollideany(P1, doodle_tile):
            tile_colliding = True
            reset_doodle_speed_all()
            tile_moving_down = True
            tile_moving_up = False
            off_screen_counter = 0

def handle_invulnerability():
    global IMMORTAL
    pressed_keys = pygame.key.get_pressed()
    if IMMORTAL == False:
        if pressed_keys [K_1]:
            IMMORTAL = True
            print("Immortal")
    if IMMORTAL == True:
        if pressed_keys [K_2]:
            IMMORTAL = False
            print("mortal")


# --------------------------------------------------------------------------------------------
# GAME PROCESS GENERAL
# --------------------------------------------------------------------------------------------
play_music()
while True:
    pressed_keys = pygame.key.get_pressed()
    handle_invulnerability()
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

    if MAIN_MENU == True:
        DISPLAYSURF.blit(background_dino, (0,0))
        for entity in bg_objects:
            DISPLAYSURF.blit(entity.image, entity.rect)
        DISPLAYSURF.blit(title_bg, (0,0))
        for entity in cactus_objects:
            entity.handle_scroll_h()
        for entity in main_menu_background:
            DISPLAYSURF.blit(entity.image, entity.rect)
        if HOME_SCREEN == True:
            DISPLAYSURF.blit(title_img, (150, 20))
            for entity in button_entities:
                entity.process()
                DISPLAYSURF.blit(entity.image, entity.rect)
        if GAME_SELECTION == True:
            for entity in button_entities_selection:
                entity.process()
                DISPLAYSURF.blit(entity.image, entity.rect)
        
    if GAME_OVER == True:
        font_score_num_game_over = font_score_game_over.render(str(SCORE), True, BLACK)
        DISPLAYSURF.fill(YELLOW)
        DISPLAYSURF.blit(game_over_font, (150,100))
        DISPLAYSURF.blit(font_score_str, (220, 280))
        DISPLAYSURF.blit(font_restart_game, (220, 350))
        DISPLAYSURF.blit(font_score_num_game_over, (420, 280))
        for entity in button_entities_in_game:
            DISPLAYSURF.blit(entity.image, entity.rect)
            entity.process()
        
        pygame.display.update()
        if pressed_keys [K_r]:
            GAME_RUNNING = True
            GAME_OVER = False
            RESTARTING = True
    
    if GAME_PAUSED == True:
        GAME_RUNNING = False
        DISPLAYSURF.blit(pause_screen, (0,0))
        if pressed_keys [K_e]:
            GAME_PAUSED = False
            GAME_RUNNING = True

# --------------------------------------------------------------------------------------------
# GAME RUNNING
# --------------------------------------------------------------------------------------------

    if GAME_RUNNING == True:
        if pressed_keys [K_ESCAPE]:
            GAME_PAUSED = True

        if RESTARTING == True:
            start_counter = 0
            stop_counter = False
            dino_is_scrolling = False
            scrollable = False

            stop_game = False
            player_fall = False
            off_screen_counter = 0
            SCORE = 0
            P1.handle_reset()
            if GOOGLE_DINO == True:
                for entity in cactus_objects_hazards:
                    entity.handle_reset_dino()
                    
            if DOODLE_JUMP == True:
                reset_doodle_speed_all()
            RESTARTING = False

# --------------------------------------------------------------------------------------------
# DOODLE JUMP GAME
# --------------------------------------------------------------------------------------------

        if DOODLE_JUMP == True:
            score_number = font_score_in_game.render(str(SCORE), True, BLACK)
            DISPLAYSURF.blit(background_doodle, (0,0))
            for entity in doodle_tile:
                DISPLAYSURF.blit(entity.image, entity.rect)

            DISPLAYSURF.blit(score_number, (1000, 10))
            DISPLAYSURF.blit(font_distance, (1080, 10))
            handle_collision_detection()
            for entity in player:
                entity.handle_left_right(15)
                entity.handle_animation()
                DISPLAYSURF.blit(entity.image, entity.rect)

            for entity in doodle_objects:
                entity.handle_move_tiles()


            if player_fall == True:
                P1.handle_doodle_game_over()
                if stop_game == True:
                    GAME_RUNNING = False
                    GAME_OVER = True

# --------------------------------------------------------------------------------------------
# GOOGLE DINO GAME
# --------------------------------------------------------------------------------------------

        if GOOGLE_DINO == True:
            score_number = font_score_in_game.render(str(SCORE), True, BLACK)
            if scrollable == True:
                cactus_type = random.randint(1, 4)

            for entity in cactus_objects:
                entity.handle_scroll_h()

            P1.handle_jump()
            P1.handle_score_dino()

            if stop_counter == False:
                if start_counter < 80:
                    start_counter += 1
                if start_counter >= 80:
                    dino_is_scrolling = True
                    stop_counter = True
                    scrollable = True
            if dino_is_scrolling == True:
                if cactus_type == 1:
                    scrollable = False
                    C1.handle_scroll_h()
                if cactus_type == 2:
                    scrollable = False
                    C2.handle_scroll_h()
                if cactus_type == 3:
                    scrollable = False
                    C3.handle_scroll_h()
                if cactus_type == 4:
                    scrollable = False
                    C4.handle_scroll_h()

            DISPLAYSURF.blit(background_dino, (0,0))
            for entity in bg_objects:
                DISPLAYSURF.blit(entity.image, entity.rect)
            DISPLAYSURF.blit(background_dino_static, (0,0))
            for entity in dino_sprites:
                DISPLAYSURF.blit(entity.image, entity.rect)
            
            for entity in players:
                DISPLAYSURF.blit(entity.image, entity.rect)
            
            DISPLAYSURF.blit(score_number, (1000, 10))
            DISPLAYSURF.blit(font_distance, (1080, 10))

            P1.handle_animation()
            if IMMORTAL == False:
                if pygame.sprite.spritecollideany(P1, hazards):
                    reset_player = True
                    GAME_OVER = True
                    GAME_RUNNING = False                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

# --------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------

    pygame.display.update()
    FramePerSec.tick(FPS)

# .\virt_env\Scripts\activate