# Sparsh Pandey
# 23 Jun 2025
# Game loop with actual maze game. 
import pygame, sys
import mazeGen

# I decided to make a game class, I'll talk more about it in class
class Game:

    # constructor 
    def __init__(self):

        # initlaize stuff
        pygame.init()
        pygame.display.set_caption("Maze Platformer")
        self.D_WIDTH = 800
        self.D_HEIGHT = 600
        self.DISPLAYSURF = pygame.display.set_mode((self.D_WIDTH, self.D_HEIGHT))

        # for fps
        self.clock = pygame.time.Clock()

        # for text
        self.font = pygame.font.SysFont(None, 36)

        # Player stats (except tile size)
        self.TILE_SIZE = 64
        self.PLAYER_SIZE = 50
        self.xSpeed, self.ySpeed = 5, 5

        # initilaize player on first tile (top left)
        self.xLoc, self.yLoc = 1 * self.TILE_SIZE, 1 * self.TILE_SIZE

        # For constant movement
        self.moveLeft = self.moveRight = self.moveUp = self.moveDown = False

        # Generate maze with 51 by 31 size
        self.maze = mazeGen.generate_maze(51, 31)

        # Camera offset (I don't want the player to see the whole screen at once, it defeats the purpose)
        self.camera_offset_x = 0
        self.camera_offset_y = 0

    def is_wall(self, x, y):
        # Get our current tile
        col = x // self.TILE_SIZE
        row = y // self.TILE_SIZE

        if 0 <= row < len(self.maze) and 0 <= col < len(self.maze[0]):
            return self.maze[row][col] == 1
        return True  # Out of bounds is treated as wall

    # update camera location
    def update_camera(self):

        # Let's find the offset from where the player actually is vs where we want them to be 
        # (in the middle of the screen). Ignroing the third term, the first - second would tell 
        # us the difference in the location of the character vs the middle of the screen. We 
        # can use this offset to adjust everything else we draw instead of the character itself.
        # Any guesses why we have the playersize // 2 in the end?
        self.camera_offset_x = self.xLoc - self.D_WIDTH // 2 + self.PLAYER_SIZE // 2
        self.camera_offset_y = self.yLoc - self.D_HEIGHT // 2 + self.PLAYER_SIZE // 2

    # use our generated maze to draw an actual maze (in real time,
    # this is getting updated in the while loop)
    def draw_maze(self):

        # loop through our maze
        for row_idx, row in enumerate(self.maze):
            for col_idx, tile in enumerate(row):

                # notice how we have to subtract offset to know where 
                # to actually draw it. First term gives us the actual
                # location if we were completely zoomed out if that makes sense
                screen_x = col_idx * self.TILE_SIZE - self.camera_offset_x
                screen_y = row_idx * self.TILE_SIZE - self.camera_offset_y

                # assign color based on value of cell
                color = "black" if tile == 1 else "green" if tile == 2 else "lightgray"

                # draw our current cell
                pygame.draw.rect(self.DISPLAYSURF, color, (screen_x, screen_y, self.TILE_SIZE, self.TILE_SIZE))

    # our main run function
    def run(self):

        startTime = pygame.time.get_ticks()
        # game loop
        while True:

            # basic events for quit and keyboard movements
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()

                # did this for hold this time, when keydown, then make bool true
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_RIGHT:
                        self.moveRight = True
                    if event.key == pygame.K_LEFT:
                        self.moveLeft = True
                    if event.key == pygame.K_UP:
                        self.moveUp = True
                    if event.key == pygame.K_DOWN:
                        self.moveDown = True

                # key is back up, so let's make bool false so we stop moving
                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_RIGHT:
                        self.moveRight = False
                    if event.key == pygame.K_LEFT:
                        self.moveLeft = False
                    if event.key == pygame.K_UP:
                        self.moveUp = False
                    if event.key == pygame.K_DOWN:
                        self.moveDown = False

            # if moving on a specific side, check if you're colliding with a wall on that side.

            # There are some issues with this, let's try and fix it!
            if self.moveRight:
                if not self.is_wall(self.xLoc + self.xSpeed + self.PLAYER_SIZE - 1, self.yLoc):
                    self.xLoc += self.xSpeed
            if self.moveLeft:
                if not self.is_wall(self.xLoc - self.xSpeed, self.yLoc):
                    self.xLoc -= self.xSpeed
            if self.moveDown:
                if not self.is_wall(self.xLoc, self.yLoc + self.ySpeed + self.PLAYER_SIZE - 1):
                    self.yLoc += self.ySpeed
            if self.moveUp:
                if not self.is_wall(self.xLoc, self.yLoc - self.ySpeed):
                    self.yLoc -= self.ySpeed

            # Update camera to center on player
            self.update_camera()

            # background
            self.DISPLAYSURF.fill("blue")

            # draw our current maze (with offset in mind)
            self.draw_maze()

            # draw our rect, I did it like this so it's easier to see what's going on
            pygame.draw.rect(
                self.DISPLAYSURF,
                "white",
                (self.D_WIDTH // 2 - self.PLAYER_SIZE // 2, # always drawn in the middle of the screen
                 self.D_HEIGHT // 2 - self.PLAYER_SIZE // 2,
                 self.PLAYER_SIZE, #square, so w and h are the same
                 self.PLAYER_SIZE)
            )

            # get current tile
            player_col = (self.xLoc + self.PLAYER_SIZE // 2) // self.TILE_SIZE
            player_row = (self.yLoc + self.PLAYER_SIZE // 2) // self.TILE_SIZE

            # check if current tile is the end. Get time so we can display it
            if self.maze[player_row][player_col] == 2:
                endTime = pygame.time.get_ticks()
                break
            
            # update display and run at 60 fps
            pygame.display.update()
            self.clock.tick(60)
        
        # fill black in screen
        self.DISPLAYSURF.fill("blue")

        # Text in end
        text = self.font.render(f"Game Over! Final Time: {(endTime - startTime) / 1000} seconds.", True, (255, 255, 255))
        self.DISPLAYSURF.blit(text, text.get_rect(center=(self.D_WIDTH // 2, self.D_HEIGHT // 2)))

        # update display and wait 3 seconds (we want to display our message)
        pygame.display.update()
        pygame.time.wait(3000)

