import pygame
from pygame import mixer
import random
import math
from bisect import insort
import button
import os.path

pygame.init()
screen = pygame.display.set_mode((1000, 700))
pygame.display.set_caption("Space Invaders")
icon = pygame.image.load("images/ICON.png")
pygame.display.set_icon(icon)


space = pygame.image.load('images/SPACE.jpg')
hangar = pygame.image.load('images/HANGAR.jpg')


mixer.music.load("music/background.mp3")
mixer.music.play(-1)


font = pygame.font.Font("font/KodeMono-Medium.ttf", 32)
smaller_font = pygame.font.Font("font/KodeMono-Medium.ttf", 24)
over_font = pygame.font.Font("font/KodeMono-Medium.ttf", 96)

TEXT_COL = (255, 255, 255)

SCORE_FILE_NAME = "score.scr"


new_game_img = pygame.image.load("images2/new_game.png")
difficulty_img = pygame.image.load("images2/difficulty.png")
best_score_img = pygame.image.load("images2/best_score.png")
back_img = pygame.image.load("images2/back.png")
difficulty_easy_img = pygame.image.load("images2/difficulty_easy.png")
difficulty_medium_img = pygame.image.load("images2/difficulty_medium.png")
difficulty_hard_img = pygame.image.load("images2/difficulty_hard.png")
difficulty_insane_img = pygame.image.load("images2/difficulty_insane.png")


new_game_button = button.Button(400, 320, new_game_img, 1)
difficulty_button = button.Button(400, 410, difficulty_img, 1)
best_score_button = button.Button(400, 500, best_score_img, 1)
back_button = button.Button(400, 580, back_img, 1)
difficulty_buttons = [
    button.Button(290, 150, difficulty_easy_img, 1),
	button.Button(510, 150, difficulty_medium_img, 1),
	button.Button(290, 250, difficulty_hard_img, 1),
	button.Button(510, 250, difficulty_insane_img, 1)
]


scene = "menu"
game_paused = False
game_lost = False

# difficulty
difficulty = 0

#starts the game all over again
def reset_enemies(difficulty):
    # create enemies and thier parameters
    enemies = []
    num_of_enemies = 8 + difficulty*2

    for i in range(num_of_enemies):   
        #tempX is made to randomly choose starting direction
        tempX = 0.4
        if random.randint(0, 1):
            tempX *= -1
        # if diificulty if higher than 1, make enemies also move by Y axis 
        if difficulty > 1:
            tempY = random.randint(0, 40*difficulty)/1000
        else:
            tempY = 0
            
        enemies.append({
            "X": random.randint(0, 930),
            "Y": random.randint(0, 30),
            "X_change": tempX,
            "Y_change": tempY
        })
    return enemies  


# draw some text with given parameters
def draw_text(text, font, text_col, x, y):
    text = font.render(text, True, text_col)
    screen.blit(text, (x, y))

# print best score
def print_best_score(best_score):
    draw_text("Best score", font, TEXT_COL, 380, 150)
    for i, item in enumerate(best_score[::-1]):
        text = str(i+1)+".             " + str(item)
        draw_text(text, smaller_font, TEXT_COL, 360, 220+i*40)
    
#check if enemy collides with the bullet
def is_collision(enemyX, enemyY, bulletX, bulletY):
    distance = math.sqrt(math.pow(enemyX-bulletX, 2) + math.pow(enemyY-bulletY, 2))
    if distance < 30:
        return True
    return False
    
# game over
def game_over_text():
    over_text = over_font.render("GAME OVER", True, TEXT_COL)
    screen.blit(over_text, (180, 300))
    

def show_score(x, y):
    score = font.render("Score : "+ str(score_val), True, TEXT_COL)
    screen.blit(score, (x, y))


def shoot_bullet(x, y):
    global bullet_state
    bullet_state = "fire"
    screen.blit(bullet_image, (x + 17, y + 10))


def player(x, y):
    screen.blit(player_image, (x, y))
    

def enemy(x, y):
    screen.blit(enemy_image, (x, y))


    
def update_score_table(scores):
    if os.path.isfile(SCORE_FILE_NAME):
        with open(SCORE_FILE_NAME, "w") as f:
            f.write(" ".join(list(map(str, scores))))
    else:
        with open(SCORE_FILE_NAME, "x") as f:
            f.write(" ".join(list(map(str, scores))))

def read_score_table():
    if not os.path.isfile(SCORE_FILE_NAME):
        update_score_table([0, 0, 0, 0])
    with open(SCORE_FILE_NAME) as f:
        return [int(score) for score in f.read().split()]
    

def add_score(score):
    current = read_score_table()
    current.append(score)
    current.sort(reverse=True)  # Sort in descending order
    updated = current[:4]  # Keep only the top 4 scores
    update_score_table(updated)

    
#scoreboard for each mode individually
score_val = 0
textX = 10
textY = 10

#player data
playerX = 450
playerY = 580
player_image = pygame.image.load("images/SPACESHIP.png")
playerX_change = 0

#bullet data
bullet_image = pygame.image.load("images/BULLET.png")
bulletX = 0
bulletY = 560
bulletY_change = 20
bullet_state = "ready"

#enemy data
enemy_image = pygame.image.load("images/ENEMY.png")
enemies = reset_enemies(difficulty)
num_of_enemies = len(enemies)

#Game loop
running = True
while running:
    # choose what scene is active
    match scene:
        # menu scenes
        case "menu":
            screen.blit(hangar, (0, 0))
            # if any of buttons is pressed, change the scene
            if new_game_button.draw(screen):
                scene = "game"
            if difficulty_button.draw(screen):
                scene = "difficulty"
            if best_score_button.draw(screen):
                scene = "best_score"
        case "best_score":
            screen.blit(hangar, (0, 0))
            print_best_score(read_score_table())
            # if pressed, go back to menu
            if back_button.draw(screen):
                scene = "menu"
        case "difficulty":
            screen.blit(hangar, (0, 0))
            draw_text("Choose the difficulty", font, TEXT_COL, 350, 50)
            # if any of buttons pressed, difficutly is assigned
            for i in range(0, len(difficulty_buttons)):
                if difficulty_buttons[i].draw(screen):
                    difficulty = i
                    scene = "menu"
                    enemies = reset_enemies(difficulty)
            if back_button.draw(screen):
                scene = "menu"
        # game scene
        case "game":
            #background
            screen.blit(space, (0, 0))
            
            #works only when the game is not paused
            if not game_paused:
                # spaceship (player) movement
                playerX += playerX_change
                if playerX <= 2:
                    playerX = 2
                elif playerX >= 928:
                    playerX = 928
                    
                # enemy 
                for i, enem in enumerate(enemies):
                    #Game over when enemies reach the spaceship
                    if enem["Y"] > 520:
                        for j in range(num_of_enemies):
                            enemies[j]["Y"] = 2000
                        game_over_text()
                        add_score(score_val)
                        score_val = 0
                        #if game is lost, print some text like wanna start again? press R to continue
                        game_lost = True
                        draw_text('Press "TAB" to return to menu', font, TEXT_COL, 270, 600)
                        break
                    
                    # enemy movement
                    enemies[i]["X"] += enemies[i]["X_change"]
                    enemies[i]["Y"] += enemies[i]["Y_change"]
                    if enemies[i]["X"] <= 2:
                        enemies[i]["X_change"] = 0.4
                        enemies[i]["Y"] += 50
                    elif enemies[i]["X"] >= 928:
                        enemies[i]["X_change"] = -0.4
                        enemies[i]["Y"] += 50
                        
                    #Collision check
                    collision = is_collision(enem["X"], enem["Y"], bulletX, bulletY)
                    if collision:
                        explosion_Sound = mixer.Sound("music/explosion.wav")
                        explosion_Sound.play()
                        bulletY = 560
                        bullet_state = "ready" 
                        score_val += 1
                        enemies[i]["X"] = random.randint(0, 930)
                        enemies[i]["Y"] = random.randint(0, 30)
                    enemy(enem["X"], enem["Y"])
                    
                #bullet movement
                if bulletY <= 0:
                    bulletY = 560
                    bullet_state = "ready"
                if bullet_state == "fire":
                    shoot_bullet(bulletX, bulletY)
                    bulletY -= 4.5
                
                # score, enemy and player update
                draw_text("Press R to pause", smaller_font, TEXT_COL, 750, 10)
                show_score(textX, textY)
                for i, enem in enumerate(enemies):
                    enemy(enem["X"], enem["Y"])
                player(playerX, playerY)
            else:
                #display menu
                match difficulty:
                    case 0:
                        temp = "easy"
                    case 1:
                        temp = "mid"
                    case 2:
                        temp = "hard"
                    case 3:
                        temp = "insane"
                print_best_score(read_score_table())
                draw_text("Press R to resume", smaller_font, TEXT_COL, 350, 550)
        case _:
            screen.blit(space, (0, 0))
    #event loop
    for event in pygame.event.get():
        if scene == "game":  
        # player movement with left and right arrows. When key is unpressed, stop the movement 
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r:
                    if game_paused:
                        game_paused = False
                    else:
                        game_paused = True
                # if game is over, then you can restart it by pressing the R key
                if game_lost and event.key == pygame.K_TAB:
                    scene = "menu"
                    game_lost = False
                if event.key == pygame.K_LEFT:
                    playerX_change = -1.2
                if event.key == pygame.K_RIGHT:
                    playerX_change = 1.2
                if event.key == pygame.K_SPACE:
                    if bullet_state == "ready":
                        bullet_sound = mixer.Sound("music/Laser.wav")
                        bullet_sound.play()
                        bulletX = playerX
                        shoot_bullet(bulletX, bulletY)
            # stop ship from moving
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                    playerX_change = 0
                
        #stop when event is quit
        if event.type == pygame.QUIT:
            running = False
    
    pygame.display.update()