import pygame
import random
import os

pygame.init()
SCREEN = WIDTH, HEIGHT = 288, 512

info = pygame.display.Info()
width = info.current_w
height = info.current_h

if width >= height:
    win = pygame.display.set_mode(SCREEN, pygame.NOFRAME)
else:
    win = pygame.display.set_mode(SCREEN, pygame.NOFRAME | pygame.SCALED | pygame.FULLSCREEN)

clock = pygame.time.Clock()
FPS = 60

# COLORS
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (30, 144, 255)
RED = (255, 0, 0)
GREEN = (0, 200, 0)
GRAY = (168, 169, 169)
LIGHT_GRAY = (220, 220, 220)
PANEL_COLOR = (240, 240, 240)
BTN_DEFAULT_COLOR = (30, 60, 180)
BTN_CLICKED_COLOR = GREEN
BTN_OUTLINE_COLOR = (70, 130, 180)
TOP_MSG_COLOR = (30, 60, 180)
RESTART_QUIT_COLOR = (30, 60, 180)
LINE_COLOR = (80, 80, 160)        # Soft lines/dashes
REVEALED_LETTER_COLOR = (50, 50, 200)  # Soft blue for revealed letters

# FONTS
alpha = pygame.font.SysFont("cursive", 21)

def load_font(size=12, filename="Fresco Stamp.ttf"):
    try:
        return pygame.font.Font(os.path.join("Fonts", filename), size)
    except Exception:
        try:
            return pygame.font.SysFont("arial", size)
        except Exception:
            return pygame.font.SysFont(None, size)

font = load_font(18)
msg = alpha.render("Guess the word using the hint below", True, TOP_MSG_COLOR)

# Text wrap utility
def render_text_wrapped(text, font, color, max_width):
    words = text.split(" ")
    lines = []
    current_line = []
    for word in words:
        test_line = " ".join(current_line + [word]) if current_line else word
        if font.size(test_line)[0] <= max_width:
            current_line.append(word)
        else:
            if current_line:
                lines.append(" ".join(current_line))
            current_line = [word]
    if current_line:
        lines.append(" ".join(current_line))

    surfaces = [font.render(line, True, color) for line in lines] if lines else [font.render("", True, color)]
    surf_width = max(s.get_width() for s in surfaces)
    surf_height = sum(s.get_height() for s in surfaces)

    surface = pygame.Surface((surf_width, surf_height), pygame.SRCALPHA)
    y = 0
    for s in surfaces:
        surface.blit(s, (0, y))
        y += s.get_height()
    return surface

# IMAGES
img_list = []
TINT_COLOR = (100, 150, 255, 50)  # Light blue tint
for i in range(7):
    path = os.path.join("Assets", f"hangman{i}.png")
    try:
        img = pygame.image.load(path).convert_alpha()
    except Exception:
        img = pygame.Surface((100, 103), pygame.SRCALPHA)
    img = pygame.transform.scale(img, (100, 103))
    tint_surf = pygame.Surface(img.get_size(), pygame.SRCALPHA)
    tint_surf.fill(TINT_COLOR)
    img.blit(tint_surf, (0, 0), special_flags=pygame.BLEND_RGBA_ADD)
    img_list.append(img)

try:
    hangman_logo = pygame.image.load(os.path.join("Assets", "hangman_top.png")).convert_alpha()
except Exception:
    hangman_logo = pygame.Surface((WIDTH - 69, 128), pygame.SRCALPHA)
hangman_logo = pygame.transform.scale(hangman_logo, (WIDTH - 69, 128))

# SOUNDS
def load_sound(path):
    try:
        return pygame.mixer.Sound(path)
    except Exception:
        return None

win_fx = load_sound(os.path.join("Sounds", "win.wav"))
lose_fx = load_sound(os.path.join("Sounds", "lose.wav"))

# WORD DATA
word_dict = {}
word_list = []
words_path = "words.txt"
if not os.path.exists(words_path):
    with open(words_path, "w", encoding="utf-8") as f:
        f.write("PYTHON: A programming language\nAPPLE: A fruit\nHOUSE: A building\n")

with open(words_path, encoding="utf-8") as file:
    for line in file:
        if ":" in line:
            w, m = line.strip().split(":", 1)
            word_dict[w.strip()] = m.strip()

def getWord():
    all_words = list(word_dict.keys())
    unused = [w for w in all_words if w not in word_list]
    if not unused:
        word_list.clear()
        unused = all_words[:]
    word = random.choice(unused)
    meaning = word_dict[word]
    return word.upper(), meaning

word, meaning = getWord()
word_list.append(word)
guessed = ['' for _ in range(len(word))]

# CLASSES
class FadeScreen:
    def __init__(self, w, h, color):
        self.surface = pygame.Surface((w, h))
        self.surface.fill(color)
        self.alpha = 255
    def update(self):
        self.alpha = max(0, self.alpha - 0.8)
    def draw(self, x, y):
        a = int(self.alpha)
        self.surface.set_alpha(a)
        win.blit(self.surface, (x, y))

class Button(pygame.sprite.Sprite):
    def __init__(self, text, x, y):
        super(Button, self).__init__()
        self.text = text
        self.image = alpha.render(self.text, True, BTN_DEFAULT_COLOR)
        self.rect = pygame.Rect(x, y, 34, 30)
        self.clicked = False
    def collision(self, pos):
        if pos and not self.clicked and self.rect.collidepoint(pos):
            self.image = alpha.render(self.text, True, BTN_CLICKED_COLOR)
            self.clicked = True
            return True, self.text
        return False, None
    def update(self):
        pygame.draw.rect(win, BTN_OUTLINE_COLOR, self.rect, 2)
        win.blit(self.image, (self.rect.centerx - self.image.get_width()//2,
                              self.rect.centery - self.image.get_height()//2))
    def reset(self):
        self.clicked = False
        self.image = alpha.render(self.text, True, BTN_DEFAULT_COLOR)

btns = []
for i in range(26):
    text = f"{chr(65 + i)}"
    x = 20 + (i % 7) * 36
    y = 330 + (i // 7) * 36
    btns.append(Button(text, x, y))

restart_img = alpha.render("RESTART", True, RESTART_QUIT_COLOR)
quit_img = alpha.render("QUIT", True, RESTART_QUIT_COLOR)
restart_rect = restart_img.get_rect()
restart_rect.centerx = WIDTH//2 + 30
restart_rect.centery = 180
quit_rect = quit_img.get_rect()
quit_rect.centerx = WIDTH//2 + 30
quit_rect.centery = 210

# GAME VARIABLES
lives = 6
score = 0
gameover = False
homepage = True
fadeScreen = FadeScreen(WIDTH, HEIGHT, LIGHT_GRAY)

# MAIN LOOP
running = True
while running:
    pos = None
    win.fill(LIGHT_GRAY)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE or event.key == pygame.K_q:
                running = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()

    if homepage:
        if fadeScreen.alpha > 0:
            win.fill(LIGHT_GRAY)
            fadeScreen.update()
            fadeScreen.draw(0,0)
            hangman_logo.set_alpha(int(fadeScreen.alpha))
            win.blit(hangman_logo,(WIDTH//2 - hangman_logo.get_width()//2, HEIGHT//2 - hangman_logo.get_height()//2))
        else:
            homepage = False
    else:
        win.fill(LIGHT_GRAY)
        # Panels
        pygame.draw.rect(win, PANEL_COLOR,(0,0,WIDTH,90))
        pygame.draw.rect(win, BLUE,(0,0,WIDTH,90),2)
        pygame.draw.rect(win, PANEL_COLOR,(0,HEIGHT//2 +30, WIDTH, HEIGHT))
        pygame.draw.rect(win, BLUE,(0,HEIGHT//2 +30, WIDTH, HEIGHT),2)
        # Hangman image
        image = img_list[max(0,min(6,6-lives))]
        win.blit(image,(WIDTH//2 - image.get_width()//2 - 60, HEIGHT//2 - image.get_height()//2 - 100))
        # Score/Lives
        win.blit(alpha.render(f"Score : {score}",True,LINE_COLOR),(WIDTH//2+30,110))
        win.blit(alpha.render(f"Lives : {lives}",True,LINE_COLOR),(WIDTH//2+30,130))
        # Buttons
        for btn in btns:
            btn.update()
            clicked, letter = btn.collision(pos)
            if clicked and not gameover:
                if letter in word:
                    for i in range(len(word)):
                        if word[i] == letter:
                            guessed[i] = letter
                    if guessed.count('') == 0:
                        if win_fx: win_fx.play()
                        score +=1
                        word, meaning = getWord()
                        guessed = ['' for _ in range(len(word))]
                        word_list.append(word)
                        for b in btns: b.reset()
                else:
                    lives -= 1
                    if lose_fx and lives ==0: lose_fx.play()
                    if lives<0: lives=0
                if lives==0: gameover=True
        # Word dashes
        base_x = WIDTH//2 - ((18*len(word))//2)
        for i in range(len(word)):
            x1,y1=(base_x+20*i,HEIGHT//2)
            x2,y2=(base_x+20*i+15,HEIGHT//2)
            pygame.draw.line(win,LINE_COLOR,(x1,y1),(x2,y2),2)
            if not gameover:
                char = alpha.render(guessed[i],True,LINE_COLOR)
            else:
                char = alpha.render(word[i],True,REVEALED_LETTER_COLOR)
            win.blit(char,(x1+3,y1-15))
        # Hint
        wrapped = render_text_wrapped(meaning,font,TOP_MSG_COLOR,WIDTH-40)
        hint_x = WIDTH//2 - wrapped.get_width()//2
        win.blit(wrapped,(hint_x,50))
        win.blit(msg,(WIDTH//2 - msg.get_width()//2,25))
        # Gameover
        if gameover:
            win.blit(restart_img,restart_rect)
            win.blit(quit_img,quit_rect)
            pygame.draw.rect(win,RED,(restart_rect.x-5,restart_rect.y-5,restart_rect.width+10,restart_rect.height+8),2)
            pygame.draw.rect(win,RED,(quit_rect.x-5,quit_rect.y-5,quit_rect.width+10,quit_rect.height+8),2)
            if pos and restart_rect.collidepoint(*pos):
                score=0
                lives=6
                gameover=False
                word, meaning = getWord()
                guessed = ['' for _ in range(len(word))]
                word_list.clear()
                word_list.append(word)
                for b in btns: b.reset()
            if pos and quit_rect.collidepoint(*pos):
                running=False

    pygame.draw.rect(win,BLUE,(0,0,WIDTH,HEIGHT),3)
    clock.tick(FPS)
    pygame.display.update()

pygame.quit()
