####################################################
# Auteurs:
# Pierre Coucheney
# Toto
# Groupe:
# MPCI 2
# dépot Github:
# https://github.com/coucheney/jeudi_jeu_de_la_vie
###################################################

#########################
# import des modules

import tkinter as tk
import copy

##########################
# constantes

COUL_FOND = "grey20"
COULEUR_QUADR = "grey60"
COULEUR_VIVANT = "yellow"
LARGEUR = 600
HAUTEUR = 400
COTE = 20
NB_COL = LARGEUR // COTE
NB_LIG = HAUTEUR // COTE

#########################
# variables globales

# tableau à 2 dimensions tel que tableau[i][j] vaut 0 si la case est morte
# et l'identifiant du carré dessiné si la case est vivante
tableau = None
val = 0
delai = 500


########################
# fonctions

def quadrillage():
    """Affiche un quadrillage constitué de carrés de côté COTE"""
    y = 0
    while y <= HAUTEUR:
        canvas.create_line((0, y), (LARGEUR, y), fill=COULEUR_QUADR)
        y += COTE
    x = 0
    while x <= LARGEUR:
        canvas.create_line((x, 0), (x, HAUTEUR), fill=COULEUR_QUADR)
        x += COTE


def xy_to_ij(x, y):
    """Retourne la colonne et la ligne du tableau correspondant
       aux coordonnées (x,y) du canevas"""
    # A FAIRE
    return x // COTE, y // COTE


def chg_case(event):
    """Modifier l'état de la case aux coordonnées (event.x, event.y)"""
    i, j = xy_to_ij(event.x, event.y)
    if tableau[i][j] > 0:
        # si case vivante
        canvas.delete(tableau[i][j])
        tableau[i][j] = 0
    else:
        # si case morte
        x, y = COTE * i, COTE * j
        carre = canvas.create_rectangle((x, y),
                                        (x + COTE, y + COTE),
                                        fill=COULEUR_VIVANT,
                                        outline=COULEUR_QUADR
                                        )
        tableau[i][j] = carre


def nb_vivant(i, j):
    """Retourne le nombre de cases vivantes autour
       de la case de coordonnées (i, j)
    """
    cpt = 0
    for k in range(max(0, i-1), min(NB_COL, i+2)):
        for el in range(max(0, j-1), min(NB_LIG, j+2)):
            if tableau[k][el] != 0 and [k, el] != [i, j]:
                cpt += 1
    return cpt


def traite_case(i, j):
    """Fait une étape de l'automate pour la case de coordonnées (i, j)
    en retournant la nouvelle valeur de la case du tableau"""
    n = nb_vivant(i, j)
    if tableau[i][j] == 0:
        # si case morte
        if n == 3:
            x, y = COTE * i, COTE * j
            carre = canvas.create_rectangle((x, y),
                                            (x + COTE, y + COTE),
                                            fill=COULEUR_VIVANT,
                                            outline=COULEUR_QUADR
                                            )
            return carre
        else:
            return 0
    else:
        # si case vivante
        if n in [2, 3]:
            return tableau[i][j]
        else:
            canvas.delete(tableau[i][j])
            return 0


def etape():
    """Fait une étape du jeu de la vie en modifiant
       la variable globale tableau
    """
    global tableau
    tableau_res = copy.deepcopy(tableau)
    for i in range(NB_COL):
        for j in range(NB_LIG):
            tableau_res[i][j] = traite_case(i, j)
    tableau = tableau_res


def etape_n(event):
    """Appelle la fonction etape sans argument"""
    etape()


def sauvegarder():
    """Sauvegarde les valeurs du tableau dans le fichier sauvegarde.txt"""
    fic = open("sauvegarde.txt", "w")
    for j in range(NB_LIG):
        for i in range(NB_COL):
            fic.write(str(tableau[i][j]) + "\n")
    fic.close()


def charger():
    """Charger le fichier sauvegarde.txt pour
       modifier le tableau et la grille
    """
    fic = open("sauvegarde.txt", "r")
    canvas.delete("all")
    quadrillage()
    cpt = 0
    for ligne in fic:
        i, j = cpt % NB_COL, cpt // NB_COL
        n = int(ligne)
        if n == 0:
            res = 0
        else:
            x, y = COTE * i, COTE * j
            res = canvas.create_rectangle((x, y),
                                          (x + COTE, y + COTE),
                                          fill=COULEUR_VIVANT,
                                          outline=COULEUR_QUADR
                                          )
        tableau[i][j] = res
        cpt += 1
    fic.close()


def start():
    """Démarre le jeu de la vie"""
    global id_after
    etape()
    id_after = racine.after(delai, start)


def start_stop():
    """Démarre ou arrête l'automate et change le nom du bouton"""
    global val
    if val == 0:
        bout_start_stop.config(text="arrêter")
        start()
    else:
        bout_start_stop.config(text="démarrer")
        racine.after_cancel(id_after)
    val = 1 - val


def augmente_delai(event):
    """Augmente le délai entre 2 étapes de l'automate"""
    global delai
    if delai < 1000:
        delai += 10
        lbl_delai.config(text="Délai entre 2 étapes: " + str(delai) + "ms")


def diminue_delai(event):
    """Diminue le délai entre 2 étapes de l'automate"""
    global delai
    if delai > 10:
        delai -= 10
        lbl_delai.config(text="Délai entre 2 étapes: " + str(delai) + "ms")


########################
# programme principal

tableau = []
for i in range(NB_COL):
    tableau.append([0] * NB_LIG)


racine = tk.Tk()
racine.title("Jeu de la vie")
# création des widgets
canvas = tk.Canvas(racine, bg=COUL_FOND, width=LARGEUR, height=HAUTEUR)
bout_sauv = tk.Button(racine, text="sauvegarder", command=sauvegarder)
bout_charger = tk.Button(racine, text="charger", command=charger)
bout_start_stop = tk.Button(racine, text="démarrer", command=start_stop)
lbl_delai = tk.Label(racine, text="Délai entre 2 étapes: " + str(delai) + "ms")

# positionnement
canvas.grid(rowspan=3)
bout_charger.grid(column=1, row=0)
bout_sauv.grid(column=1, row=1)
bout_start_stop.grid(column=1, row=2)
lbl_delai.grid(column=0, row=3)

# gestion des événements
canvas.bind("<Button-1>", chg_case)
racine.bind("n", etape_n)
racine.bind("m", diminue_delai)
racine.bind("p", augmente_delai)

# autres fonctions
quadrillage()

# boucle principal
racine.mainloop()
