"""
Jeu de la vie avec le module pyxel

https://github.com/kitao/pyxel/blob/main/doc/README.fr.md
"""

import pyxel

# =========================================================
# == CONSTANTES
# =========================================================
LARGEUR = 80
HAUTEUR = 80
VIE = 1
MORT = 0
TEXTE = 2
FONDS = 3
COULEUR = {VIE: 8, MORT: 0, TEXTE: 5, FONDS: 10}

# =========================================================
# == INITIALISATION FENETRE
# =========================================================
pyxel.init(LARGEUR, HAUTEUR, title="jeu de la vie")

# =========================================================
# == CHARGEMENT DES IMAGES
# =========================================================
pyxel.load("game_of_life.pyxres")


# =========================================================
# == FONCTIONS DE GRILLE
# =========================================================
def copie_grille(grille):
    """Renvoie une copie de la grille"""
    return [[grille[lig][col] for col in range(LARGEUR)] for lig in range(HAUTEUR)]


def initialiser_grille(grille, motif):
    for (lig, col) in motif:
        grille[lig][col] = VIE


def nombre_voisins_vivants(lig, col):
    """Renvoie le nombre de voisins  vivants de la cellule en (lig, col)"""
    v = 0
    for dl in range(-1, 2):
        for dc in range(-1, 2):
            if (
                (0 <= lig + dl < HAUTEUR)
                and (0 <= col + dc < LARGEUR)
                and (dl, dc) != (0, 0)
                and jeu["grille"][lig + dl][col + dc] == VIE
            ):
                v = v + 1
    return v


def evolution_cellule(grille, lig, col):
    """Renvoie le nouvel état de la cellule en (lig, col) dans grille"""
    etat = grille[lig][col]
    voisins_vivants = nombre_voisins_vivants(lig, col)
    if etat == MORT:
        return voisins_vivants == 3
    else:
        return 2 <= voisins_vivants <= 3


def evolution_grille(grille):
    grille_nouvelle = copie_grille(grille)
    for lig in range(HAUTEUR):
        for col in range(LARGEUR):
            grille_nouvelle[lig][col] = evolution_cellule(grille, lig, col)
    return grille_nouvelle


# =========================================================
# == INITIALISER LE MOTIF
# =========================================================
def charger_fichier(chemin):
    """
    Charger un motif stocké dans un fichier au format Plain
    https://conwaylife.com/wiki/Plaintext
    """
    f = open(chemin)
    ligne1 = f.readline().rstrip()
    titre = " ".join(ligne1.split()[1:])
    motif = []
    for ligne in f:
        if ligne[0] != "!":
            motif.append([c for c in ligne.rstrip()])
    return titre, motif


def motif_dans_grille(lig0, col0, motif, grille):
    decodage = {".": MORT, "O": VIE}
    largeur_motif = len(motif[0])
    hauteur_motif = len(motif)
    assert lig0 + hauteur_motif <= len(grille) and col0 + largeur_motif <= len(
        grille[0]
    ), "Motif trop grand"
    for lig in range(lig0, lig0 + hauteur_motif):
        for col in range(col0, col0 + largeur_motif):
            grille[lig][col] = decodage[motif[lig - lig0][col - col0]]


# =========================================================
# == AFFICHAGES MENU / FIN
# =========================================================


def afficher_menu():
    """Affichage du menu d'accueil"""
    pyxel.text(
        int(LARGEUR * 0.25), int(HAUTEUR * 0.25), f"{jeu['nom']}", COULEUR[TEXTE]
    )
    pyxel.text(
        int(LARGEUR * 0.1), int(HAUTEUR * 0.5), "Press s to start", COULEUR[TEXTE]
    )
    pyxel.text(
        int(LARGEUR * 0.1), int(HAUTEUR * 0.6), "Press q to quit", COULEUR[TEXTE]
    )
    pyxel.blt(0, 0, 0, 0, 0, 3, 3)
    pyxel.blt(HAUTEUR - 3, 0, 0, 8, 0, 3, 3)
    pyxel.blt(0, LARGEUR - 3, 0, 0, 5, 3, 3)
    pyxel.blt(LARGEUR - 3, HAUTEUR - 3, 0, 8, 5, 3, 3)


def afficher_fin():
    """Affichage de fin"""
    pyxel.text(int(LARGEUR * 0.25), int(HAUTEUR * 0.5), "Fin de la vie", COULEUR[TEXTE])
    pyxel.blt(0, 0, 0, 0, 8, 8, 8)
    pyxel.blt(HAUTEUR - 8, 0, 0, 0, 8, 8, 8)
    pyxel.blt(0, LARGEUR - 8, 0, 0, 8, 8, 8)
    pyxel.blt(LARGEUR - 8, HAUTEUR - 8, 0, 0, 8, 8, 8)


# =========================================================
# == UPDATE
# =========================================================
def update():
    """mise à jour des variables (30 fois par seconde)"""
    # une génération par seconde
    if pyxel.btn(pyxel.KEY_S):
        jeu["menu"] = False
    if pyxel.btn(pyxel.KEY_Q):
        jeu["fin"] = True
    if pyxel.frame_count % 10 == 0:
        # mise à jour de la grille
        jeu["grille"] = evolution_grille(jeu["grille"])


# =========================================================
# == DRAW
# =========================================================
def draw():
    """création des objets (30 fois par seconde)"""
    # vide la fenetre
    pyxel.cls(COULEUR[FONDS])
    if jeu["fin"]:
        afficher_fin()
    elif jeu["menu"]:
        afficher_menu()
    else:
        # dessin de la grille
        for lig in range(HAUTEUR):
            for col in range(LARGEUR):
                pyxel.rect(col, lig, 1, 1, COULEUR[jeu["grille"][lig][col]])


# =========================================================
# == DICTIONNAIRE GLOBAL DU JEU
# =========================================================
jeu = {
    "grille": [[MORT for __ in range(LARGEUR)] for _ in range(HAUTEUR)],
    "menu": True,
    "fin": False,
}
# =========================================================
# == PROGRAMME PRINCIPAL
# =========================================================
nom, motif = charger_fichier("period60glidergun.txt")
jeu["nom"] = nom
motif_dans_grille(0, 0, motif, jeu["grille"])
# lancement de l'application
pyxel.run(update, draw)