# Arup Guha
# Started 6/16/2023, last updated 6/21/2023
# Changed to use objects and inherit from sprite on 6/22/2025
# Sushi game functions

import random
import math
import time
import pygame, sys
from pygame.locals import *

# Useful Constants
SCREEN_W = 1000
SCREEN_H = 600
WHITE = pygame.Color(255,255,255)
BLUE = pygame.Color(0,0,255)
RED = pygame.Color(255,0,0)


# Inherit Sprite, use this class for sushi and detos.
class Obj(pygame.sprite.Sprite):

    # constructor (and we can use super instead of sprite.Sprite
    def __init__(self, img, pos, speedX, speedY):
        pygame.sprite.Sprite.__init__(self)
        self.image = img
        self.rect = self.image.get_rect()
        self.rect.x = pos[0]
        self.rect.y = pos[1]
        self.speedX = speedX
        self.speedY = speedY

    def draw(self, surface):
        surface.blit(self.image, (self.rect.x, self.rect.y))

    # update would do nothing if blank, but let's make it move
    def update(self):
        self.rect.x += self.speedX
        self.rect.y += self.speedY
            
# Inherit Sprite, use this class for the player.
class Player(pygame.sprite.Sprite):

    # constructor (and we can use super instead of sprite.Sprite
    def __init__(self, img, pos):
        pygame.sprite.Sprite.__init__(self)
        self.image = img
        self.rect = self.image.get_rect()
        self.rect.x = pos[0]
        self.rect.y = pos[1]
        self.speedX = 0

    def draw(self, surface):
        surface.blit(self.image, (self.rect.x, self.rect.y))

    # update would do nothing if blank, but let's make it move
    def update(self, SCREEN_W):
        self.rect.x = (self.rect.x + self.speedX + SCREEN_W)%SCREEN_W

    # updates the current speed by dx
    def changeSpeed(self, dx):

        # Speed update.
        self.speedX += dx

        # Lets me change directions faster, to go left.
        if dx < 0 and self.speedX > 0:
            self.speedX = 0

        # Same for right.
        if dx > 0 and self.speedX < 0:
            self.speedX = 0

        # This is my max speed right.
        if self.speedX > 9:
            self.speedX = 9

        # And max speed left.
        if self.speedX < -9:
            self.speedX = -9
    
    # Returns how many items in sushiList intersect with me and removes them from sushiList.
    def numIntersect(self, sushiGroup):

        res = 0

        # Go through list backwards.
        for item in sushiGroup:

            # Here is a collision, add 1 to answer and remove from list.
            if self.rect.colliderect(item.rect):
                res += 1
                sushiGroup.remove(item)

        # Here is our answer.
        return res

# This function removes all items that will never be visible again.
def removeUseless(items):
    for item in items:
        if item.rect.y > SCREEN_H:
            items.remove(item)

def draw(text, font, color, surface, x, y):
    # Draw text on a new Surface. Title, antialias and color are used.
    text = font.render(text, 1, color)

    # Returns a new rectangle covering the entire surface.
    # This rectangle will always start at (0, 0) with a width and height the same size as the image.
    textrect = text.get_rect()

    # Sets location of text.
    textrect.topleft = (x, y)

    # Draw one image onto another. Source and destination are passed. Source is textobj and it will be written onto textrect,
    surface.blit(text, textrect)

# Just gets the user's name.
def getname():

    # Some default things
    pygame.init()
    clock = pygame.time.Clock()
    bigfont = pygame.font.SysFont(None, 60)

    # Set up display.
    DISPLAYSURF = pygame.display.set_mode((SCREEN_W, SCREEN_H))
    pygame.display.set_caption("Get Name Screen")
    buffer = ""
    textbox = pygame.Rect(550, 100, 300, 50)
    
    while True:

        DISPLAYSURF.fill((0, 0, 0))
        draw('Enter Your Name', bigfont, (255, 255, 255), DISPLAYSURF, 200, 100)
        pygame.draw.rect(DISPLAYSURF, (192,192,192), textbox)
        draw(buffer, bigfont, (255, 255, 255), DISPLAYSURF, 550, 100)

        # Get events from the queue
        for event in pygame.event.get():

            # If you close the game.
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            # If you press down a key.
            if event.type == KEYDOWN:
                
                # If that key was the escape button do this.
                if event.key == K_ESCAPE:
                    pygame.quit()
                    sys.exit()

                # Appends a lowercase letter.
                if event.unicode >= 'a' and event.unicode <= 'z' and len(buffer) < 15:
                    buffer = buffer + event.unicode

                # Appends an uppercase letter.
                if event.unicode >= 'A' and event.unicode <= 'Z' and len(buffer) < 15:
                    buffer = buffer + event.unicode

                # Removes the last letter.
                if event.key == K_BACKSPACE and len(buffer) > 0:
                    buffer = buffer[:len(buffer)-1]

                # Done with the name return it.
                if event.key == K_RETURN:
                    return buffer

        # Update portions of the screen for software displays
        pygame.display.update()

        # Update the clock in milliseconds. Should be called once per frame.
        clock.tick(60)
        
# Plays the game for player.
def playgame(player):

    # Basic set up.
    pygame.init()
    DISPLAYSURF = pygame.display.set_mode((SCREEN_W, SCREEN_H))
    pygame.display.set_caption("Sushi Game!")
    clock = pygame.time.Clock()
    

    # Store sushi here....
    sushi= []
    
    # Store Deetos here.
    detos = []

    # Initialize variables.
    myscore = 0
    lives = 3
    DY = 5
    DX = 0
    DETOSRATE = [100, 50, 30, 15,  5]
    SUSHIRATE = [ 50, 40, 30, 20, 10]
    level = 0

    # This is the one image we will use.
    sushiPic = pygame.image.load("dangoo.jpg")
    sushiPic = pygame.transform.scale(sushiPic,(100,50))

    # This is what we're avoiding.
    detosPic = pygame.image.load("detosarenogood.png")
    detosPic = pygame.transform.scale(detosPic,(50,50))

    myImage = pygame.image.load("spottedoof.jpg")
    myImage = pygame.transform.scale(myImage,(80,80))
    me = Player(myImage, (SCREEN_W/2, SCREEN_H-80))

    # So we know how many frames we've run.
    step = 0

    curT = time.time()

    # Event loop.
    while True:
        
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == KEYDOWN:
                 if event.key == K_LEFT: #If the left arrow was pressed, then we want our ball to move to the left
                     me.changeSpeed(-3)
                 if event.key == K_RIGHT: #If the right arrow was pressed, then we want our ball to move to the right
                     me.changeSpeed(3)

        # These images are big, so I only add a drop once every 10 time steps.
        if step%SUSHIRATE[level] == 0:
            x = random.randint(1, SCREEN_W)
            item = Obj(sushiPic, (x,0), DX, DY)
            sushi.append(item)
            
        # These will be created half as often as the sushi.
        if step%DETOSRATE[level] == 0:
            x = random.randint(1, SCREEN_W)
            item = Obj(detosPic, (x, 0), DX, DY)
            detos.append(item)

        # Start drawing.
        DISPLAYSURF.fill(WHITE)

        # blit allows us to draw a surface onto another surface.
        for x in sushi:
            x.draw(DISPLAYSURF)
        for x in detos:
            x.draw(DISPLAYSURF)
        me.draw(DISPLAYSURF)

        # Update score, lives.
        myscore += me.numIntersect(sushi)
        lives -= me.numIntersect(detos)
        font = pygame.font.SysFont("Arial", 24)

        # Draw score, lives.
        draw("Score: "+str(myscore), font, BLUE, DISPLAYSURF, 20, 20)
        draw("Lives: "+str(lives), font, RED, DISPLAYSURF, 20, 120)

        # Move all objects.
        for x in sushi:
            x.update()
        for x in detos:
            x.update()
        me.update(SCREEN_W)

        # We have died.
        if lives == 0:

            # Display final score, wait a bit and return.
            draw(player+", you Died. Final Score: "+str(myscore), font, BLUE, DISPLAYSURF, 20, 220)
            pygame.display.update()
            pygame.time.wait(2000)

            # We return this to the main screen.
            return myscore

        # Update the display and process lists.
        level = min(4, myscore//10)
        DY = 5 + 2*level
        pygame.display.update()        
        removeUseless(sushi)
        removeUseless(detos)
        clock.tick(30)
        step += 1

# Shows the high scores from the file, fileName.
def showhighscores(fileName):

    # Set up.
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_W, SCREEN_H))
    screen.fill(WHITE)
    pygame.display.set_caption("Leaderboard")
    clock = pygame.time.Clock()

    # Open the file with the scores.
    myFile = open(fileName, "r")

    # Read in file contents, store names, scores, assumed to be in order.
    n = int(myFile.readline())
    names = []
    scores = []
    for i in range(n):
        toks = myFile.readline().split()
        names.append(toks[1])
        scores.append(int(toks[0]))
    myFile.close()

    # Game loop...
    while True:

        # Created buttons. X, Y, Width, Height.
        button_1 = pygame.Rect(350, 500, 300, 100)

        # Draws buttons onto screen
        pygame.draw.rect(screen, (192,192,192), button_1)
        font = pygame.font.SysFont(None, 36)
        draw('Return to Main Menu', font, (255, 255, 255), screen, 370, 535)

        # We'll used thiese two fonts as well.
        font = pygame.font.SysFont("Arial", 36)
        font2 = pygame.font.SysFont("Arial", 48)
        txtsurf = font2.render("LEADERBOARD", True, (0,0,0))
        screen.blit(txtsurf, (350, 0))

        # Draw top 10 scores, at most.
        for i in range(0, min(10,n)):
            txtsurf = font.render(str(i+1)+". "+names[i], True, BLUE)
            screen.blit(txtsurf,( 350 , 70+40*i ))
            txtsurf2 = font.render(str(scores[i]), True, BLUE)
            screen.blit(txtsurf2,( 600 , 70+40*i ))

        # Look for events - basically to go back to other screen.
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == MOUSEBUTTONDOWN:
                # 1 - left click, 2 - middle click, 3 - right click, 4 - scroll up, 5 - scroll down
                if event.button == 1:

                    mx, my = pygame.mouse.get_pos()
                    
                    # We just go back.
                    if button_1.collidepoint((mx, my)):
                        return

        # Update portions of the screen for software displays
        pygame.display.update()

        # Update the clock in milliseconds. Should be called once per frame.
        clock.tick(60)

# This function updates the scores in fileName based on this new entry.
def updateScores(fileName,entry):

    # Open the file with the scores.
    myFile = open(fileName, "r")

    # Read in file contents, store as list of lists...
    n = int(myFile.readline())
    items = []
    scores = []
    for i in range(n):
        toks = myFile.readline().split()
        items.append([int(toks[0]),toks[1]])
    myFile.close()

    # Add this new entry in.
    items.append(entry)

    # Re-sort and reverse.
    items.sort()
    items.reverse()

    # Write everything back out to the file.
    myFile = open(fileName, "w")
    myFile.write(str(n+1)+"\n")
    for i in range(n+1):
        myFile.write(str(items[i][0])+" "+items[i][1]+"\n")
    myFile.close()
    
