# -*- coding: utf-8 -*-
"""
Created on Sat May 25 15:22:41 2024

@author: Stéphane Pasquet
@url : https://www.mathweb.fr/euclide/generateur-de-grilles-de-sudoku/
"""

# pyinstaller sudoku.py pour générer le fichier exe
"""
Modules
"""

from random import randint
from tkinter import Tk, Entry, Canvas, Button, Radiobutton, Label, IntVar, mainloop
from tkinter import filedialog
from PIL import ImageGrab,Image
from datetime import datetime
from os import system, makedirs, remove
from copy import deepcopy
from shutil import copy


""" -------------------------------------------
Fonctions propres au Sudoku
"""

def ligne(S,i):
    return [S[i][j] for j in range(0,9) if S[i][j] != 0 ]

def colonne(S,j):
    return [S[i][j] for i in range(0,9) if S[i][j] != 0 ]

def bloc(S,i,j):
    r = []
    ligne = (i // 3) * 3
    colonne = (j // 3) * 3
    for l in  [ligne,ligne+1,ligne+2]:
        for c in [colonne,colonne+1,colonne+2]:
            if S[l][c] != 0:
                r.append(S[l][c])
    return r

def possibles(S,i,j):
    return [k for k in range(1,10) if k not in bloc(S,i,j) and k not in ligne(S,i) and k not in colonne(S,j)]

def suivante(i,j):
    if j == 8:
        i += 1
        j = 0
    else:
        j += 1

    return i,j

def solve(S,i,j):
    if i == 9:
        return True
    elif S[i][j] > 0:
        i,j = suivante(i,j)
        return solve(S,i,j)
    for k in possibles(S,i,j):
        S[i][j] = k
        a,b = suivante(i,j)
        if solve(S,a,b):
            return True
    S[i][j] = 0
    return False

"""
Génération d'une grille
"""

def generSudoku(dim = 9):
    S = [ [0] * dim for i in range(dim) ]
    L = [(0,0) , (3,1) , (6,2) , (1,3) , (4,4) , (7,5) , (2,6) , (5,7) , (8,8)]
    for i in L:
        S[i[0]][i[1]] = randint(1,9)

    solve(S,0,0)

    return S


"""
Génération de grille à trous
"""

def trous(S,dim = 9,niveau=1):
    T = deepcopy(S)
    for bloc in range(dim):
        if niveau == 1:
            nb = randint(4,6)
        elif niveau == 2:
            nb = randint(5,7)
        else:
            nb = randint(7,9)
        for k in range(nb):
            i = randint(0,dim//3-1)
            j = randint(0,dim//3-1)
            T[3 * (bloc//3) + i][3 * (bloc%3) + j] = 0

    return T

"""
Conversion en LaTeX + PDF
"""

def preambule(*packages):
    p = ""
    for i in packages:
        p = p+"\\usepackage{"+i+"}\n"
    return p

def convert_to_latex(grille,*args):
    dirname = filedialog.askdirectory(initialdir = "/",title = "Sélectionner le dossier de destination")
    dirname += '/Sudoku/'
    document = "\\documentclass[12pt]{article}\n"
    document += "\\usepackage[utf8]{inputenc}\n" + preambule('tikz','lmodern') + "\n\\begin{document}\n\\begin{tikzpicture}\n"
    document += "\\draw (0,0) grid[step=1cm] (9,-9);\n\\draw[line width=2pt] (0,0) grid[step=3cm] (9,-9.01);\n"
    for i in range(9):
        for j in range(9):
            if grille[i][j] != 0:
                if len(args) != 0:
                    if args[0][i][j] == 0:
                        couleur = 'red'
                    else:
                        couleur = 'black'
                else:
                    couleur ='black'
                document = document + "\\node["+couleur+"] at (" + str(i+0.5) + ",-" + str(j+0.5) + ") {"+ str(grille[i][j])+"};\n"
    document = document + "\\end{tikzpicture}\n\\end{document}"

    if len(args) != 0:
        filenameTEX = "grille"+suffixePicture()+"_solution.tex"
    else:
        filenameTEX = "grille"+suffixePicture()+".tex"

    # sauvegarde du fichier TEX dans le répertoire courant
    fichier = open(filenameTEX,"x")
    fichier.write(document)
    fichier.close()

    # compilation PdfLaTeX dans le répertoire courant
    cmd = 'echo Compilation du fichier '+filenameTEX
    cmd = "pdflatex  --shell-escape -synctex=1 -interaction=nonstopmode "+filenameTEX
    system(cmd)

    # création des répertoires
    makedirs(dirname+'latex/', exist_ok = True)
    makedirs(dirname+'pdf/', exist_ok = True)

    # copie des fichiers TEX et PDF
    filenamePDF = filenameTEX[:-3]+'pdf'
    cmd = 'echo Copie de '+filenamePDF+' vers '+dirname+'pdf/'+filenamePDF
    system(cmd)
    copy(filenamePDF, dirname+'pdf/'+filenamePDF)
    cmd = 'echo Copie de '+filenameTEX+' vers '+dirname+'latex/'+filenameTEX
    system(cmd)
    copy(filenameTEX, dirname+'latex/'+filenameTEX)
    cmd = 'echo Suppression de '+filenamePDF[:-3] + '*'
    system(cmd)
    cmd = 'del '+filenamePDF[:-3] + '*'
    system(cmd)
    

"""
Dessin d'une grille à compléter
"""

def suffixePicture():
    date = datetime.now()
    year = date.year
    month = date.month
    day = date.day
    hour = date.hour
    minute = date.minute
    seconde = date.second
    return str(year)+str(month)+str(day)+str(hour)+str(minute)+str(seconde)

def drawGrille(S,dim = 9):
    def save_canvas():
        x, y = canvas.winfo_rootx(), canvas.winfo_rooty()
        w, h = canvas.winfo_width(), canvas.winfo_height()
        filenamePNG = 'grille'+suffixePicture()+'.png'
        ImageGrab.grab((x,y,x+w,y+h)).save(filenamePNG)
        dirname = filedialog.askdirectory(initialdir = "/",title = "Sélectionner le dossier de destination")
        dirname += '/Sudoku/images/'
        makedirs(dirname, exist_ok=True)
        copy(filenamePNG, dirname+filenamePNG)
        avertiss = Tk()
        avertissK = Canvas(avertiss,width=500,height=100)
        avertissK.pack()
        avertiss.title('Grille sauvegardée')
        avertiss.iconbitmap("sudoku.ico")
        avertissK.create_text(250,40,text='Grille sauvegardée sous :',font=("Helvetica", 10),fill='red')
        avertissK.create_text(250,60,text=dirname+filenamePNG,font=("Helvetica", 10),fill='red')
        remove(filenamePNG)

    def affiche_solution():
        drawGrilleComplete(grille_complete,S)

    def convert_latex():
        convert_to_latex(S)

    grille = Tk()
    grille.title('Sudoku - Par Stéphane Pasquet')
    grille.iconbitmap("sudoku.ico")
    canvas = Canvas(grille,width = (dim + 1) * 30 + 15 , height = (dim + 1) * 30+15 , bg = "white")
    canvas.pack()

    for i in range(dim+1):
        canvas.create_line(20 , 20 + i*30 , 20 + dim*30 , 20 + i*30)
        canvas.create_line(20 + i*30 , 20 , 20 + i*30 , 20 + dim*30)
    for i in range(dim//3+1):
        canvas.create_line(20 , 20 + 3*i*30 , 20 + dim*30 , 20 + 3*i*30,width=2)
        canvas.create_line(20 + 3*i*30 , 20 , 20 + 3*i*30 , 20 + dim*30,width=2)
    for i in range(dim):
        for j in range(dim):
            if S[i][j] != 0:
                canvas.create_text(35+30*j,35+30*i,text=S[i][j])

    Button(grille,text='Enregistrer',command=save_canvas).pack()
    Button(grille,text='Convertir en LaTeX + PDF',command=convert_latex).pack()
    Button(grille,text='Solution',command=affiche_solution).pack()
    
    mainloop()

"""
Dessin d'une grille complétée
"""

def drawGrilleComplete(S_complete,S,dim = 9):
    def convert_latex():
        convert_to_latex(S_complete,S)
        
    def save_canvas():
        x, y = canvasSol.winfo_rootx(), canvasSol.winfo_rooty()
        w, h = canvasSol.winfo_width(), canvasSol.winfo_height()
        filenamePNG = 'grille'+suffixePicture()+'_solution.png'
        ImageGrab.grab((x,y,x+w,y+h)).save(filenamePNG)
        sol.destroy()
        dirname = filedialog.askdirectory(initialdir = "/",title = "Sélectionner le dossier de destination")
        dirname += '/Sudoku/images/'
        makedirs(dirname, exist_ok=True)
        copy(filenamePNG, dirname+filenamePNG)
        avertiss = Tk()
        avertissK = Canvas(avertiss,width=500,height=100)
        avertissK.pack()
        avertiss.title('Solution sauvegardée')
        avertiss.iconbitmap("sudoku.ico")
        avertissK.create_text(250,40,text='Solution sauvegardée sous :',font=("Helvetica", 10),fill='red')
        avertissK.create_text(250,60,text=dirname+filenamePNG,font=("Helvetica", 10),fill='red')
    sol = Tk()
    sol.title('Sudoku - Solution - Par Stéphane Pasquet')
    sol.iconbitmap("sudoku.ico")
    canvasSol = Canvas(sol,width = (dim + 1) * 30 + 15 , height = (dim + 1) * 30+15 , bg = "white")
    canvasSol.pack()

    for i in range(dim+1):
        canvasSol.create_line(20 , 20 + i*30 , 20 + dim*30 , 20 + i*30)
        canvasSol.create_line(20 + i*30 , 20 , 20 + i*30 , 20 + dim*30)
    for i in range(dim//3+1):
        canvasSol.create_line(20 , 20 + 3*i*30 , 20 + dim*30 , 20 + 3*i*30,width=2)
        canvasSol.create_line(20 + 3*i*30 , 20 , 20 + 3*i*30 , 20 + dim*30,width=2)
    for i in range(dim):
        for j in range(dim):
            if S[i][j] != 0:
                canvasSol.create_text(35+30*j,35+30*i,text=S[i][j])
            else:
                canvasSol.create_text(35+30*j,35+30*i,text=S_complete[i][j],fill='red')

    Button(sol,text='Enregistrer',command=save_canvas).pack()
    Button(sol,text='Convertir en LaTeX + PDF',command=convert_latex).pack()
    
    mainloop()

"""
post action
"""

def post_action():
    global grille_complete
    grille_complete = generSudoku()
    grille_trous = trous(grille_complete , niveau = niv)
    drawGrille(grille_trous)

"""
Menu

def makeentry(parent, caption, width=None, **options):
    Label(parent, text=caption).pack()
    entry = Entry(parent, **options)
    if width:
        entry.config(width=width)
    entry.pack()
    return entry
"""

def menu():
    def recup_niveau():
        global niv
        niv = niveauTk.get()

    menu = Tk()
    menu.title('Sudoku - Par Stéphane Pasquet')
    menu.iconbitmap("sudoku.ico")
    menuCanvas = Canvas(menu,width = 350 , height = 50)
    menuCanvas.pack()

    niveauTk = IntVar()

    Label(menu,text="Générateur de Sudoku",font=("Helvetica", 20)).pack()

    Radiobutton(menu,text='Niveau Facile',variable=niveauTk,value=1,command=recup_niveau).pack()
    Radiobutton(menu,text='Niveau Moyen',variable=niveauTk,value=2,command=recup_niveau).pack()
    Radiobutton(menu,text='Niveau Difficile',variable=niveauTk,value=3,command=recup_niveau).pack()
    Label(menu,text='\n').pack()
    Button(text='Valider',command=post_action).pack()
    Label(menu,text='\n').pack()
    menu.mainloop()


if __name__ == '__main__':
    menu()

