# SKUNKY THE TREASURE HUNTER

"""
World Elements:
p: player
t: tree
f: fire
g: treasure
c: coin
"""

import pygame
from enum import Enum
import os
import sys

class Orientation(Enum):
    UP = 0
    RIGHT = 1
    DOWN = 2
    LEFT = 3

# Define grid elements
EMPTY, TREE, FIRE, PLAYER, TREASURE, COIN = 0, 1, 2, 3, 4, 5

# Define colors
WHITE = (255, 255, 255)
GRAY = (200, 200, 200)

class Skunky:
    # MAPS
    labyrinths = [
        [
            "tttttttttt",
            "tp  ct c t",
            "tttf tgt t",
            "t    ttt t",
            "t tt t   t",
            "t ct  ctct",
            "ttttfffttt",
        ],[
            "tttttttttttt",
            "t ttttct**gt",
            "tc   t t*ttt",
            "tttt t tc**t",
            "tt*****ttt*t",
            "tt*fff*****t",
            "tt***ttttttt",
            "tttt**c****t",
            "tctttttttt*t",
            "t  t****f**t",
            "tt t*tt***tt",
            "tp***  tt ct",
            "tttttttttttt",
        ],[ # by lia m.
            "fffffffffffffffffffffffffffff",
            "f     t         t       t   f",
            "f ttt t ttttttt t ttttt t t f",
            "f  ct t  ct   t t tc  t tct f",
            "ftttt ttttt t t t ttt t ttt f",
            "g     t t ttt t ttttttt t t f",
            "ftttt t t ttt t ttttttt t t f",
            "f t   tct       t         t f",
            "f ttt ttt ttttt t ttt ttttt f",
            "f t t     t  ct t t t t  ct f",
            "f t ttttttt ttt t t t t ttt f",
            "f   tc    t     t   t   t   f",
            "f t t t ttt ttttt t t t tcttf",
            "f t t t   t tc    tct t     f",
            "f t t ttt t ttttttttt ttttttf",
            "f t t tct t         t     tcf",
            "f t t t t  cttttttt ttttt t f",
            "f t ttt ttttt     t tc  t   f",
            "f t         t ttt t ttt ttt f",
            "f ttt t t   t  ct t       t f",
            "f t   t ttttttttt t ttttt t f",
            "f t ttt     t   t t t  ct t f",
            "f t tcttttt t t t   t ttt t f",
            "f   t tc  t   t t t t       f",
            "ftttt t t t t t t t ttttttttf",
            "f       t tct t   t        cf",
            "f ttttttt ttt tt tttt tttt tf",
            "f      pt     t  ct     t   f",
            "fffffffffffffffffffffffffffff",
        ],[
            "tttttttttt",
            "g    ttttt",
            "tttf ttttt",
            "t    t p t",
            "t    t   t",
            "t  ftt   t",
            "f    f   t",
            "f        t",
            "tttttttttt",

        ]
    ]

    DELAY_MIN = 50

    def __init__(self,world_map=None,delay_time=1000,cell_size=50,close_when_over=False) -> None:
        if world_map:
            if isinstance(world_map,int):
                self.map = Skunky.labyrinths[world_map]
            else:
                self.map = world_map
        else:
            self.map = Skunky.labyrinths[0]
        
        self.CELL_SIZE = cell_size
        self.delay_time = delay_time
        self.NX = len(self.map[0])
        self.NY = len(self.map)
        self.width  = self.NX * self.CELL_SIZE
        self.height = self.NY * self.CELL_SIZE
        self.world = [[EMPTY] * self.NX for i in range(self.NY)]
        self.x,self.y = None,None # player coords
        self.orientation = Orientation.UP
        self.is_on = True # true if game is ongoing
        self.won = False
        self.something_in_front = None
        self.something_in_back = None
        self.something_left = None
        self.something_right = None
        self.delay_min = Skunky.DELAY_MIN
        self.close_when_over = close_when_over
        self.score = 0

        # build world from map
        for y in range(len(self.map)):
            for x in range(len(self.map[y])):
                if self.map[y][x] == 't':
                    self.world[y][x] = TREE
                elif self.map[y][x] == 'f':
                    self.world[y][x] = FIRE
                elif self.map[y][x] == 'g':
                    self.world[y][x] = TREASURE
                elif self.map[y][x] == 'c':
                    self.world[y][x] = COIN
                if self.map[y][x] == 'p':
                    # world[y][x] = PLAYER
                    if not self.x and not self.y:
                        self.x,self.y = x,y
                    else:
                        raise Exception

        if self.x==None or self.y==None:
            raise Exception("Player missing in map!")

        # Initialize Pygame
        pygame.init()

        bg_img =         pygame.image.load(os.path.join('images','bg_earth.png'))
        tree_img =       pygame.image.load(os.path.join('images','tree.png'))
        fire_img =       pygame.image.load(os.path.join('images','fire.png'))
        treasure_img =   pygame.image.load(os.path.join('images','treasure.png'))
        player_img =     pygame.image.load(os.path.join('images','skunk.png'))
        gameover_img =   pygame.image.load(os.path.join('images','game_over.png'))
        win_img =        pygame.image.load(os.path.join('images','win.png'))
        coin_img =       pygame.image.load(os.path.join('images','coin.png'))

        # Resize images to fit the grid cell size
        self.bg_img   = pygame.transform.scale(bg_img, (self.CELL_SIZE, self.CELL_SIZE))
        self.tree_img = pygame.transform.scale(tree_img, (self.CELL_SIZE, self.CELL_SIZE))
        self.fire_img = pygame.transform.scale(fire_img, (self.CELL_SIZE, self.CELL_SIZE))
        self.treasure_img = pygame.transform.scale(treasure_img, (self.CELL_SIZE, self.CELL_SIZE))
        self.player_img = pygame.transform.scale(player_img, (self.CELL_SIZE, self.CELL_SIZE))
        self.coin_img  = pygame.transform.scale(coin_img, (self.CELL_SIZE, self.CELL_SIZE))
        resize_factor = 0.9
        size = int(self.height*resize_factor) # size for game over / you win image
        if self.height > self.width:
            size = int(self.width*resize_factor)
        self.gameover_img = pygame.transform.scale(gameover_img, (size,size))
        self.win_img = pygame.transform.scale(win_img, (size,size))

        # Initialize screen
        self.screen = pygame.display.set_mode((self.NX*self.CELL_SIZE, self.NY*self.CELL_SIZE))
        pygame.display.set_caption("Skunky the Treasure Hunter")

        self.update()
        self.draw()

    def get_coords_neighboring_cells(self):
        # front,back,left,right
        x,y = self.x, self.y
        if self.orientation == Orientation.UP: #y_front -= 1
            return [x,y-1],[x,y+1],[x-1,y],[x+1,y]
        elif self.orientation == Orientation.DOWN: #y_front += 1
            return [x,y+1],[x,y-1],[x+1,y],[x-1,y]
        elif self.orientation == Orientation.RIGHT: #x_front += 1
            return [x+1,y],[x-1,y],[x,y-1],[x,y+1]
        elif self.orientation == Orientation.LEFT: #x_front -= 1
            return [x-1,y],[x+1,y],[x,y+1],[x,y-1]

    def is_in_front_of(self,type):
        # checks if skunky stands in front of (and thus sees) COIN, TREASURE ...
        xy_front,xy_back,xy_left,xy_right = self.get_coords_neighboring_cells()
        if self.world[xy_front[1]][xy_front[0]] == type:
            return True
        return False

    def is_right_of(self,type):
        xy_front,xy_back,xy_left,xy_right = self.get_coords_neighboring_cells()
        if self.world[xy_left[1]][xy_left[0]] == type:
            return True
        return False

    def is_left_of(self,type):
        xy_front,xy_back,xy_left,xy_right = self.get_coords_neighboring_cells()
        if self.world[xy_right[1]][xy_right[0]] == type:
            return True
        return False

    # def is_in_front_of_something(self):
    #     xy_front,xy_back,xy_left,xy_right = self.get_coords_neighboring_cells()
    #     if self.world[xy_front[1]][xy_front[0]] != 0: # EMPTY
    #         return True
    #     return False

    # def is_in_front_of_coin(self):
    #     return self.is_in_front_of(COIN)

    # def is_in_front_of_tree(self):
    #     return self.is_in_front_of(TREE)

    # def is_in_front_of_fire(self):
    #     return self.is_in_front_of(FIRE)

    def is_in_front_of_treasure(self):
        return self.is_in_front_of(TREASURE)

    def is_left_of_treasure(self):
        return self.is_left_of(TREASURE)

    def is_right_of_treasure(self):
        return self.is_right_of(TREASURE)

    def is_in_front_of_obstacle(self):
        if self.is_in_front_of(TREE): return True
        if self.is_in_front_of(FIRE): return True
        return False

    def is_left_of_obstacle(self):
        if self.is_left_of(TREE): return True
        if self.is_left_of(FIRE): return True
        return False

    def is_right_of_obstacle(self):
        if self.is_right_of(TREE): return True
        if self.is_right_of(FIRE): return True
        return False

    def update(self):
        if self.is_on:
            xy_front,xy_back,xy_left,xy_right = self.get_coords_neighboring_cells()
            self.something_in_front = False
            self.something_in_back = False
            self.something_left = False
            self.something_right = False
            if self.world[xy_front[1]][xy_front[0]] == TREE or self.world[xy_front[1]][xy_front[0]] == FIRE: self.something_in_front = True
            if self.world[xy_back[1]][xy_back[0]] == TREE or self.world[xy_back[1]][xy_back[0]] == FIRE: self.something_in_back = True
            if self.world[xy_left[1]][xy_left[0]] == TREE or self.world[xy_left[1]][xy_left[0]] == FIRE: self.something_left = True
            if self.world[xy_right[1]][xy_right[0]] == TREE or self.world[xy_right[1]][xy_right[0]] == FIRE: self.something_right = True

    def show_indefinitely(self):
        # call this method at end of code to show last state
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
            pygame.time.delay(self.delay_min)

    def move(self):
        # moves skunky forward by one step. Checks if wins or loses.
        if self.is_on:
            xy_front,xy_back,xy_left,xy_right = self.get_coords_neighboring_cells()
            x_new,y_new = xy_front
            # check for collisions
            if self.world[y_new][x_new] == TREE:
                pass
            elif self.world[y_new][x_new] == FIRE:
                self.is_on = False
                print("GAME OVER!")
            elif self.world[y_new][x_new] == TREASURE:
                self.x,self.y = x_new,y_new
                self.is_on = False
                self.won = True
                print("YOU WON!")
            elif self.world[y_new][x_new] == COIN:
                self.world[y_new][x_new] = ' '
                self.score += 1
                self.x,self.y = x_new,y_new
            else: # just an empty field
                self.x,self.y = x_new,y_new
            self.draw()

    def right(self):
        self.orientation = Orientation((self.orientation.value + 1)%len(Orientation))
        self.player_img = pygame.transform.rotate(self.player_img,-90)
        self.draw()

    def left(self):
        self.orientation = Orientation((self.orientation.value - 1)%len(Orientation))
        self.player_img = pygame.transform.rotate(self.player_img,90)
        self.draw()

    def draw(self):
        self.update()

        # self.screen.fill(WHITE) # bg color

        for row in range(self.NY):
            for col in range(self.NX):
                element = self.world[row][col]
                x = col * self.CELL_SIZE
                y = row * self.CELL_SIZE
                self.screen.blit(self.bg_img, (x, y))
                if element == TREE:
                    self.screen.blit(self.tree_img, (x, y))
                elif element == FIRE:
                    self.screen.blit(self.fire_img, (x, y))
                elif element == TREASURE:
                    self.screen.blit(self.treasure_img, (x, y))
                elif element == COIN:
                    self.screen.blit(self.coin_img, (x, y))
            
            self.screen.blit(self.player_img, (self.x * self.CELL_SIZE, self.y  * self.CELL_SIZE))

        # draw grid:
        for x in range(0, self.NX):
            pygame.draw.line(self.screen, GRAY, (x*self.CELL_SIZE, 0), (x*self.CELL_SIZE, self.NY*self.CELL_SIZE))
        for y in range(0, self.NY):
            pygame.draw.line(self.screen, GRAY, (0, y*self.CELL_SIZE), (self.NX*self.CELL_SIZE, y*self.CELL_SIZE))

        if not self.is_on:
            if self.won:
                self.screen.blit(self.win_img, (0,0))
            else:
                self.screen.blit(self.gameover_img, (0,0))
            pygame.display.flip()
            # Don't close window after game won/over
            if not self.close_when_over:
                self.show_indefinitely()
    
        pygame.display.flip()

        t0 = pygame.time.get_ticks()
        while pygame.time.get_ticks() - t0 < self.delay_time:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
            pygame.time.delay(self.delay_min)

    def free_walk_mode(self):
        self.delay_time = 0
        self.delay_min = 100
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
            # Naviation of player
            keys = pygame.key.get_pressed()
            if keys[pygame.K_RIGHT]: self.right()
            if keys[pygame.K_LEFT]: self.left()
            if keys[pygame.K_UP]: self.move()
            pygame.time.delay(self.delay_min)


