# Imports
import pygame
import pygame.locals
import pygame_textbox
import os
from os import path
from threading import Thread
import pickle
import time
import random
# Set Up 
pygame.init()
tile_size = 50
screen_width = tile_size * 35
screen_height = (900)
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Unexpected Adventure")
fps = 60
clock = pygame.time.Clock()
level = 1
main_menu = True
game_over = False
white = (255, 255, 255)
font = pygame.font.SysFont('Futura', 24)
doesnt_exist = False
scroll = [0,0]
# Load Images
background1 = pygame.image.load("images/background4.png").convert_alpha()
background2 = pygame.image.load("images/background3.png").convert_alpha()
background3 = pygame.image.load("images/background2.png").convert_alpha()
background4 = pygame.image.load("images/background1.png").convert_alpha()
left = pygame.image.load("images/left.png").convert_alpha()
middle = pygame.image.load("images/middle.png").convert()
right = pygame.image.load("images/right.png").convert_alpha()
door = pygame.image.load("images/door.png").convert_alpha()
button_edit = pygame.image.load("images/button_edit.png").convert()
button_custom = pygame.image.load("images/button_custom.png").convert()
button_start = pygame.image.load("images/button_start.png").convert()
button_exit = pygame.image.load("images/button_exit.png").convert()
button_music = pygame.image.load("images/button_music.png").convert()
button_restart = pygame.image.load("images/button_restart.png").convert()
# Sprite Groups
water_group = pygame.sprite.Group()
enemy_group = pygame.sprite.Group()
door_group = pygame.sprite.Group()
# World Class
class World():
    # Init
    def __init__(self, data):
        self.tile_list = []
        row_count = 0
        for row in data:
            column_count = 0
            for tile in row:
                if tile == 1:
                    tile = pygame.transform.scale(left, (tile_size, tile_size))
                    tile_rect = tile.get_rect()
                    tile_rect.x = column_count * tile_size
                    tile_rect.y = row_count * tile_size
                    tile = (tile, tile_rect)
                    self.tile_list.append(tile)
                elif tile == 2:
                    tile = pygame.transform.scale(middle, (tile_size, tile_size))
                    tile_rect = tile.get_rect()
                    tile_rect.x = column_count * tile_size
                    tile_rect.y = row_count * tile_size
                    tile = (tile, tile_rect)
                    self.tile_list.append(tile)
                elif tile == 3:
                    tile = pygame.transform.scale(right, (tile_size, tile_size))
                    tile_rect = tile.get_rect()
                    tile_rect.x = column_count * tile_size
                    tile_rect.y = row_count * tile_size
                    tile = (tile, tile_rect)
                    self.tile_list.append(tile)
                elif tile == 4:
                    water = Water(column_count * tile_size, row_count * tile_size + (tile_size // 2))
                    water_group.add(water)
                elif tile == 5:
                    enemy = Enemy(column_count * tile_size, row_count * tile_size + 15, row_count, column_count, data)
                    enemy_group.add(enemy)
                elif tile == 6:
                    door = Door(column_count * tile_size, row_count * tile_size - (tile_size // 2))
                    door_group.add(door)
                column_count += 1
            row_count +=1
    # Draw World
    def draw(self):
        for tile in self.tile_list:
            screen.blit(tile[0], (tile[1].x - scroll[0] + screen_width // 2, tile[1].y - scroll[1] + screen_height // 2))
# Water
class Water(pygame.sprite.Sprite):
    # Init
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        image = pygame.image.load("images/water1.png").convert_alpha()
        self.image = pygame.transform.scale(image, (tile_size, tile_size))
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y -25
        self.images = []
        self.image_index = 1
        for number in range(1, 17):
            img = pygame.image.load(f"images/water{number}.png").convert_alpha()
            img = pygame.transform.scale(img, (tile_size, tile_size))
            self.images.append(img)
        self.image = self.images[self.image_index]
        self.counter = 0
    # Update
    def update(self):
        self.image_index += 1
        if self.image_index < len(self.images):
            self.image = self.images[self.image_index]
        else:
            self.image_index = 0
            self.image = self.images[self.image_index]
    def render(self, screen):
        screen.blit(self.image, (self.rect.x - scroll[0] + screen_width // 2, self.rect.y - scroll[1] + screen_height // 2))
# Enemy Class
class Enemy(pygame.sprite.Sprite):
    # Init
    def __init__(self, x, y, row_count, column_count, data):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("images/enemy.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.move_direction = 5
        self.move_counter = 0
        self.row_count = row_count
        self.column_count = column_count
        self.data = data
    # Update
    def update(self):
        if self.data[self.row_count + 1][self.column_count] == 2:

            self.rect.x += self.move_direction
            self.move_counter += 1
            if self.move_counter > 10:
                self.move_direction *= - 1
                self.move_counter*= -1
    def render(self, screen):
        screen.blit(self.image, (self.rect.x - scroll[0] + screen_width // 2, self.rect.y - scroll[1] + screen_height // 2))
# Door
class Door(pygame.sprite.Sprite):
    # Init
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        image = pygame.image.load("images/door.png").convert_alpha()
        self.image = pygame.transform.scale(image, (tile_size, tile_size + 26))
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
    def render(self, screen):
        screen.blit(self.image, (self.rect.x - scroll[0] + screen_width // 2, self.rect.y - scroll[1] + screen_height // 2))
# Button Class
class Button():
    # Init
    def __init__(self, x, y, image):
        self.image = image
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
    # Draw Button
    def draw(self):
        mouse_position = pygame.mouse.get_pos()
        clicked = False
        if self.rect.collidepoint(mouse_position):
            if pygame.mouse.get_pressed()[0] == 1:
                clicked = True
                time.sleep(0.2)
        screen.blit(self.image, self.rect)
        return clicked
# Player Class
class Player():
    # Init
    def __init__(self, x, y):
        self.reset(x, y)
        self.game_over = False
        self.image_index = 0
    # Move Player
    def update(self, game_over , main_menu):
        # Frame Rate
        clock.tick(fps)
        # Move Player
        dx = 0
        dy = 0
        if game_over == False:
            key = pygame.key.get_pressed()
            # Jump
            if key[pygame.K_w] and self.jumped == False and self.in_air == False:
                self.vel_y = -15
                self.jumped = True
            elif key[pygame.K_w] == False:
                self.jumped = False
            # Left
            if key[pygame.K_a]:
                dx -= 7
                self.direction = "left"
            # Right
            elif key[pygame.K_d]:
                dx += 7
                self.direction = "right"
            # Animations
            if key[pygame.K_a] == False and key[pygame.K_d] == False:
                if self.direction == "left":
                    self.image = self.images_left[0]
                elif self.direction == "right":
                    self.image = self.images_right[0]
            else:
                self.index += 1
                if self.index >= len(self.images_right) or self.index >= len(self.images_left):
                    self.index = 0
                if self.direction == "left":
                    self.image = self.images_left[self.index]
                elif self.direction == "right":
                    self.image = self.images_right[self.index]
            # Gravity
            self.vel_y += 1
            if self.vel_y > 10:
                self.vel_y = 15
            dy += self.vel_y
            # Collisions
            self.in_air = True
            for tile in world.tile_list:
                # Animations
                if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
                    if dx < 0:
                        self.image = self.images_left[0]
                    else:
                        self.image = self.images_right[0]
                # X and Y
                if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
                    dx = 0
                # Vertical
                if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
                    if self.vel_y >= 0:
                        dy = tile[1].top - self.rect.bottom
                        self.in_air = False
                    elif self.vel_y < 0:
                        dy = tile[1].bottom - self.rect.top
                    self.vel_y = 0
            # Collision With Door
            if pygame.sprite.spritecollide(self, door_group, False):
                main_menu = True
        # Game Over Conditions
        if self.rect.bottom > screen_height:
            game_over = True
        elif pygame.sprite.spritecollide(self, water_group, False):
            game_over = True
        elif pygame.sprite.spritecollide(self, enemy_group, False):
            game_over = True
        # Game Over
        if game_over == True:
            self.image = self.dead_image
            self.rect.y -= 5
        # Update Coordinates
        self.rect.x += dx
        self.rect.y += dy
        # Draw Player
        screen.blit(self.image, (self.rect.x - scroll[0] + screen_width // 2, self.rect.y - scroll[1] + screen_height // 2))
        return game_over, main_menu
    # Reset Player
    def reset(self, x, y):
        # Set Up
        self.images_right = []
        self.images_left = []
        self.index = 0
        # Load Images
        for num in range(1, 5):
            img_right = pygame.image.load(f"images/guy{num}.png").convert_alpha()
            img_right = pygame.transform.scale(img_right, (40, 80))
            img_left = pygame.transform.flip(img_right, True, False)
            self.images_right.append(img_right)
            self.images_left.append(img_left)
        self.dead_image = pygame.image.load("images/ghost.png").convert_alpha()
        self.image = self.images_right[0]
        # Rect
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.width = self.image.get_width()
        self.height = self.image.get_height()
        # Other Variables
        self.direction = 0
        self.vel_y = 0
        self.jumped = False
        self.in_air = True
# Level Editor
def level_editor():
    print(os.path.join(os.getcwd(), "venv", "bin", "python3.9"))
    os.system(os.path.join(os.getcwd(), "venv", "bin", "python3.9") + " " + os.path.join(os.getcwd(), "level_editor.py"))
# Draw Text
def draw_text(text, font, text_col, x, y):
	img = font.render(text, True, text_col)
	screen.blit(img, (x, y))
# Scale and Initialise Classes
background1 = pygame.transform.scale(background1, (2000, 1000))
background2 = pygame.transform.scale(background2, (2000, 1000))
background3 = pygame.transform.scale(background3, (2000, 1000))
background4 = pygame.transform.scale(background4, (2000, 1000))
start_button = pygame.transform.scale(button_start, (200, 100))
exit_button = pygame.transform.scale(button_exit, (80, 35))
player = Player(100, screen_height - 180)
start_button = Button(screen_width // 2 -100 , screen_height // 2 - 100, start_button)
exit_button = Button(screen_width // 2 - 40, screen_height // 2 + 50, exit_button)
edit_button = Button(screen_width // 2 -250, screen_height // 2 + 30, button_edit)
custom_button = Button(screen_width // 2 + 150 , screen_height // 2 + 30 , button_custom)
music_button = Button(screen_width // 2 + 700 , screen_height // 2 - 400 , button_music)
menu_button = Button(screen_width //2 + 700 , screen_height //2 - 300, button_exit)
restart_button = Button(screen_width // 2 - 50, screen_height // 2 , button_restart)
# Background Music
pygame.mixer.music.load("sounds/music.mp3")
pygame.mixer.music.play(-1)
paused = False
# Game Loop
run = True
while run:
    # FPS
    clock.tick(fps)
    screen.fill((0,0,0))
    # QUIT
    events = pygame.event.get()
    for event in events:
        if event.type == pygame.QUIT:
            run = False
    # Draw Background
    screen.blit(background4, (0 - scroll[0]* 0.2, 0))
    screen.blit(background3, (0 - scroll[0]* 0.4, 0))
    screen.blit(background2, (0 - scroll[0]* 0.5, 0))
    screen.blit(background1, (0 - scroll[0]* 0.6, 0))


    screen.blit(background4, (2000 - scroll[0]* 0.2, 0))
    screen.blit(background3, (2000 - scroll[0]* 0.4, 0))
    screen.blit(background2, (2000 - scroll[0]* 0.5, 0))
    screen.blit(background1, (2000 - scroll[0]* 0.6, 0))


    screen.blit(background4, (-2000 - scroll[0]* 0.2, 0))
    screen.blit(background3, (-2000 - scroll[0]* 0.4, 0))
    screen.blit(background2, (-2000 - scroll[0]* 0.5, 0))
    screen.blit(background1, (-2000 - scroll[0]* 0.6, 0))



    
    # Menu
    if main_menu:
        game_over = False
        # Start
        if start_button.draw():
            enemy_group.empty()
            water_group.empty()
            door_group.empty()
            if path.exists(f"levels/storymode/level_{level}"):
                pickle_in = open(f"levels/storymode/level_{level}", "rb")
                level_data = pickle.load(pickle_in)
            world = World(level_data)
            main_menu = False
            player.reset(100, screen_height - 130)
            doesnt_exist = False
        # Level Editor
        elif edit_button.draw() == True:
            thread = Thread(target = level_editor)
            thread.start()
            doesnt_exist = False
        # Custom Maps
        if custom_button.draw():
            enemy_group.empty()
            water_group.empty()
            door_group.empty()
            selected_level = pygame_textbox.textinput()
            if path.exists(f"levels/custom/level_{selected_level}"):
                pickle_in = open(f"levels/custom/level_{selected_level}", "rb")
                level_data = pickle.load(pickle_in)
                world = World(level_data)
                main_menu = False
                screen = pygame.display.set_mode((screen_width, screen_height))
                pygame.display.set_caption("Unexpected Adventure")
                player.reset(100, screen_height - 130)
                doesnt_exist = False
            else:
                doesnt_exist = True
                screen = pygame.display.set_mode((screen_width, screen_height))
                pygame.display.set_caption("Unexpected Adventure")
                player.reset(100, screen_height - 130)
        if doesnt_exist:      
            draw_text("Level Doesn't Exist", font, white, screen_width // 2 - 75, screen_height // 2 + 17)
        # Exit
        if exit_button.draw():
            run = False
        # Music
        if music_button.draw():
            if paused == False:
                pygame.mixer.music.pause()
                paused = True
            else:
                pygame.mixer.music.unpause()
                paused = False
    # Game
    if main_menu == False:




        # Draw World
        world.draw()
        water_group.update()
        for water in water_group:
            water.render(screen)
        enemy_group.update()
        for enemy in enemy_group:
            enemy.render(screen)
        door_group.update()
        for door in door_group:
            door.render(screen)
        # Update Player
        game_over, main_menu = player.update(game_over , main_menu)
        scroll[0] += (player.rect.x - scroll[0])*0.02
        scroll[1] = 450


        # Music Button
        if music_button.draw():
            if paused == False:
                pygame.mixer.music.pause()
                paused = True
            else:
                pygame.mixer.music.unpause()
                paused = False
        # Menu Button
        if menu_button.draw():
            player.reset(100, screen_height - 130)
            enemy_group.empty()
            water_group.empty()
            door_group.empty()
            main_menu = True
            game_over = False
        # Restart Button
        if game_over == True:
            if restart_button.draw():
                player.reset(100, screen_height - 130)
                game_over = False
    # Update Display
    pygame.display.update()
# Quit Pygame
pygame.quit()
