#!/usr/bin/python3
import gi, random
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Pango
import cairo

WORDS = ['python','programming','computer','keyboard','monitor','software','hardware',
         'algorithm','database','network','internet','security','function','variable',
         'terminal','compiler','library','interface','framework','developer',
         'hangman','elephant','dinosaur','umbrella','birthday']

HANGMAN = [
    [],
    [(50,10),(50,90)],  # pole
    [(10,10),(90,10),(50,10)],  # top bar + rope
    [(50,30,'head')],
    [(50,45),(50,70)],  # body
    [(50,50),(30,60)],  # left arm
    [(50,50),(70,60)],  # right arm
    [(50,70),(35,90)],  # left leg
    [(50,70),(65,90)],  # right leg
]

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='Hangman')
        self.set_default_size(500, 480)
        self.word = ''; self.guessed = set(); self.wrong = 0
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        vbox.set_margin_top(8); vbox.set_margin_bottom(8)
        vbox.set_margin_start(8); vbox.set_margin_end(8)
        self.add(vbox)
        self.status_lbl = Gtk.Label()
        vbox.pack_start(self.status_lbl, False, False, 0)
        hb = Gtk.Box(spacing=10)
        self.da = Gtk.DrawingArea(); self.da.set_size_request(140, 140)
        self.da.connect('draw', self.draw_hangman)
        hb.pack_start(self.da, False, False, 0)
        right = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        self.word_lbl = Gtk.Label()
        self.word_lbl.override_font(Pango.FontDescription('Monospace Bold 22'))
        right.pack_start(self.word_lbl, True, True, 0)
        self.wrong_lbl = Gtk.Label()
        right.pack_start(self.wrong_lbl, False, False, 0)
        hb.pack_start(right, True, True, 0)
        vbox.pack_start(hb, False, False, 0)
        # letter buttons
        letters_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
        self.letter_btns = {}
        for row_letters in ['QWERTYUIOP','ASDFGHJKL','ZXCVBNM']:
            row = Gtk.Box(spacing=2)
            row.set_halign(Gtk.Align.CENTER)
            for ch in row_letters:
                btn = Gtk.Button(label=ch)
                btn.set_size_request(36, 36)
                btn.connect('clicked', self.guess, ch.lower())
                row.pack_start(btn, False, False, 0)
                self.letter_btns[ch.lower()] = btn
            letters_box.pack_start(row, False, False, 0)
        vbox.pack_start(letters_box, False, False, 0)
        new_btn = Gtk.Button(label='New Game')
        new_btn.connect('clicked', lambda *_: self.new_game())
        vbox.pack_start(new_btn, False, False, 0)
        self.new_game()
    def new_game(self):
        self.word = random.choice(WORDS)
        self.guessed = set(); self.wrong = 0
        for btn in self.letter_btns.values():
            btn.set_sensitive(True)
            btn.get_style_context().remove_class('destructive-action')
        self.update_display()
    def guess(self, btn, ch):
        if ch in self.guessed: return
        self.guessed.add(ch)
        if ch not in self.word:
            self.wrong += 1
            btn.get_style_context().add_class('destructive-action')
        btn.set_sensitive(False)
        self.update_display()
    def update_display(self):
        disp = ' '.join(ch if ch in self.guessed else '_' for ch in self.word)
        self.word_lbl.set_text(disp)
        self.wrong_lbl.set_text(f'Wrong guesses: {self.wrong}/6')
        self.da.queue_draw()
        if '_' not in disp:
            self.status_lbl.set_markup('<span foreground="green" weight="bold">You won!</span>')
        elif self.wrong >= 6:
            self.status_lbl.set_markup(f'<span foreground="red">Game Over! Word: {self.word}</span>')
        else:
            self.status_lbl.set_text(f'Guess the {len(self.word)}-letter word')
    def draw_hangman(self, w, cr):
        alloc = w.get_allocation()
        W, H = alloc.width, alloc.height
        cr.set_source_rgb(1,1,1); cr.paint()
        cr.set_source_rgb(0.2,0.2,0.2)
        cr.set_line_width(3)
        # gallows
        cr.move_to(10,H-10); cr.line_to(W-10,H-10); cr.stroke()
        cr.move_to(W//2, H-10); cr.line_to(W//2, 10); cr.stroke()
        cr.move_to(W//2, 10); cr.line_to(W*3//4, 10); cr.stroke()
        cr.move_to(W*3//4, 10); cr.line_to(W*3//4, 25); cr.stroke()
        if self.wrong >= 1:
            cr.arc(W*3//4, 35, 10, 0, 6.28); cr.stroke()
        if self.wrong >= 2:
            cr.move_to(W*3//4, 45); cr.line_to(W*3//4, 75); cr.stroke()
        if self.wrong >= 3:
            cr.move_to(W*3//4, 55); cr.line_to(W*3//4-15, 65); cr.stroke()
        if self.wrong >= 4:
            cr.move_to(W*3//4, 55); cr.line_to(W*3//4+15, 65); cr.stroke()
        if self.wrong >= 5:
            cr.move_to(W*3//4, 75); cr.line_to(W*3//4-12, 90); cr.stroke()
        if self.wrong >= 6:
            cr.move_to(W*3//4, 75); cr.line_to(W*3//4+12, 90); cr.stroke()

win = App()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
