import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image
import numpy as np
import hashlib

# Convertir une chaine de caractères en entier
def key_from_string(key_string):
    # Convertir la chaîne en une valeur de hachage (MD5)
    hashed_key = hashlib.md5(key_string.encode()).hexdigest()
    # Convertir la valeur hexadécimale en entier
    key_int = int(hashed_key, 16)
    # Réduire la valeur pour qu'elle soit compatible avec np.random.seed
    return key_int % (2**32)  # Limiter à 32 bits


# Fonction pour générer une permutation déterministe à partir d'une clé
def generate_permutation(size, key):
    np.random.seed(key)
    return np.random.permutation(size)

# Fonction pour appliquer la permutation
def apply_permutation(image_matrix, permutation):
    # Si l'image est en 3D (RGB), appliquer la permutation sur chaque canal
    if len(image_matrix.shape) == 3:
        permuted_matrix = np.zeros_like(image_matrix)
        for channel in range(image_matrix.shape[2]):  # Parcourir R, G, B
            flat_channel = image_matrix[:, :, channel].flatten()
            permuted_channel = flat_channel[permutation]
            permuted_matrix[:, :, channel] = permuted_channel.reshape(image_matrix.shape[:2])
        return permuted_matrix
    else:  # Si l'image est en niveaux de gris (2D)
        flat_matrix = image_matrix.flatten()
        permuted_matrix = flat_matrix[permutation]
        return permuted_matrix.reshape(image_matrix.shape)


# Fonction pour inverser la permutation
def inverse_permutation(permutation):
    inverse = np.zeros_like(permutation)
    inverse[permutation] = np.arange(len(permutation))
    return inverse

# Fonction pour chiffrer l'image
def encrypt_image(image_path, key, save_path):
    image = Image.open(image_path)
    image_matrix = np.array(image)

    # Si la clé est une chaîne, la convertir en entier
    if isinstance(key, str):
        key = key_from_string(key)

    # Générer une permutation
    permutation = generate_permutation(image_matrix.shape[0] * image_matrix.shape[1], key)

    # Appliquer la permutation
    encrypted_matrix = apply_permutation(image_matrix, permutation)

    # Sauvegarder l'image chiffrée
    encrypted_image = Image.fromarray(encrypted_matrix.astype('uint8'))
    encrypted_image.save(save_path)
    messagebox.showinfo("Succès", "L'image a été chiffrée et sauvegardée avec succès.")

# Fonction pour déchiffrer l'image
def decrypt_image(image_path, key, save_path):
    image = Image.open(image_path)
    image_matrix = np.array(image)

    # Si la clé est une chaîne, la convertir en entier
    if isinstance(key, str):
        key = key_from_string(key)

    # Générer la permutation et son inverse
    permutation = generate_permutation(image_matrix.shape[0] * image_matrix.shape[1], key)
    inverse_perm = inverse_permutation(permutation)

    # Appliquer l'inverse de la permutation
    decrypted_matrix = apply_permutation(image_matrix, inverse_perm)

    # Sauvegarder l'image déchiffrée
    decrypted_image = Image.fromarray(decrypted_matrix.astype('uint8'))
    decrypted_image.save(save_path)
    messagebox.showinfo("Succès", "L'image a été déchiffrée et sauvegardée avec succès.")


# Fonction pour sélectionner une image
def select_image():
    file_path = filedialog.askopenfilename(title="Sélectionner une image", filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.gif")])
    if file_path:
        image_path_entry.delete(0, tk.END)
        image_path_entry.insert(0, file_path)

# Fonction pour sélectionner un répertoire de sauvegarde
def select_save_directory():
    dir_path = filedialog.askdirectory(title="Sélectionner un répertoire de sauvegarde")
    if dir_path:
        save_dir_entry.delete(0, tk.END)
        save_dir_entry.insert(0, dir_path)

# Fonction pour chiffrer l'image
def encrypt():
    image_path = image_path_entry.get()
    key = key_entry.get() 
    save_dir = save_dir_entry.get()
    save_path = f"{save_dir}/encrypted_image.jpg"
    encrypt_image(image_path, key, save_path)

def decrypt():
    image_path = image_path_entry.get()
    key = key_entry.get() 
    save_dir = save_dir_entry.get()
    save_path = f"{save_dir}/decrypted_image.jpg"
    decrypt_image(image_path, key, save_path)


# Créer la fenêtre principale
root = tk.Tk()
root.title("Chiffrement et Déchiffrement d'Image")

# Créer les widgets
image_path_label = tk.Label(root, text="Chemin de l'image:")
image_path_label.grid(row=0, column=0, padx=10, pady=10)

image_path_entry = tk.Entry(root, width=50)
image_path_entry.grid(row=0, column=1, padx=10, pady=10)

image_path_button = tk.Button(root, text="Sélectionner une image", command=select_image)
image_path_button.grid(row=0, column=2, padx=10, pady=10)

key_label = tk.Label(root, text="Clé de chiffrement:")
key_label.grid(row=1, column=0, padx=10, pady=10)

key_entry = tk.Entry(root, width=50)
key_entry.grid(row=1, column=1, padx=10, pady=10)

save_dir_label = tk.Label(root, text="Répertoire de sauvegarde:")
save_dir_label.grid(row=2, column=0, padx=10, pady=10)

save_dir_entry = tk.Entry(root, width=50)
save_dir_entry.grid(row=2, column=1, padx=10, pady=10)

save_dir_button = tk.Button(root, text="Sélectionner un répertoire", command=select_save_directory)
save_dir_button.grid(row=2, column=2, padx=10, pady=10)

encrypt_button = tk.Button(root, text="Chiffrer l'image", command=encrypt)
encrypt_button.grid(row=3, column=1, padx=10, pady=10)

decrypt_button = tk.Button(root, text="Déchiffrer l'image", command=decrypt)
decrypt_button.grid(row=3, column=2, padx=10, pady=10)

# Lancer la boucle principale
root.mainloop()
