from random import *
from math import *
from time import *
from PIL import Image
import numpy as np
from scipy.ndimage import gaussian_filter
import threading
import sys
import cv2

'''
PROCEDE :
1- Générer une image aléatoirement
2- Appliquer successivement :
    - un flou de l'image
    - une multiplication des couleurs
De manière à faire grossir les symboles

'''


class Process:


    def __init__(self, message, debut, fin):
        '''initialise un objet de classe Process'''
        self.debut = debut
        self.fin = fin
        self.previous = 0
        self.percent = 0
        self.start_time = monotonic()
        print(message + '\n')
    
    def aff(self, value):
        '''Affiche le pourcentage de chargement correspondant à value'''
        fact = (value - self.debut)/(self.fin-self.debut)
        fact += (fact==0.0)*0.01

        spent_time = monotonic() - self.start_time

        remaining = int(spent_time/fact*(1-fact))

        hours = remaining//3600
        minutes = (remaining - hours*3600)//60
        seconds = remaining - hours*3600 - minutes*60
        
        percent = int(fact * 100)

        if percent > self.percent:
            self.percent = percent
            print("\033[2K\033[A", end='')
            write_str = str(self.percent) + " % effectué -- fin estimée dans "
            if hours != 0:
                write_str += str(hours) + "h "
            if minutes != 0:
                write_str += str(minutes) + "min "
            
            write_str += str(seconds) + "s"

            print(write_str, "          ")

            self.previous = len(write_str)
    
    def end(self):
        self.aff(self.fin)
        print("\033[2K\033[A")







# VARIABLE GLOBALE CONTENANT LE PRECEDENT NOMBRE ALEATOIRE


def tuple_int(t):
    return (int(t[0]), int(t[1]), int(t[2]))



def save_image(rgb_array, filename):
    """
    Sauvegarde un tableau 2D de couleurs RGB dans un fichier PNG.
    
    :param rgb_array: Liste de listes contenant des tuples RGB.
    :param filename: Nom du fichier PNG de sortie.
    """
    # Obtenir les dimensions de l'image
    height = len(rgb_array)
    width = len(rgb_array[0])

    # Créer une nouvelle image avec les dimensions spécifiées
    image = Image.new("RGB", (width, height))

    # Charger les pixels de l'image avec les valeurs RGB du tableau
    for y in range(height):
        for x in range(width):
            image.putpixel((x, y), tuple_int(rgb_array[y][x]))

    # Sauvegarder l'image dans un fichier PNG
    image.save(filename)







def ran_couleur():
    return (randint(0, 256), randint(0, 256), randint(0, 256))



kernel_size = lambda sigma : 2*int(4*sigma)+1
remove_zero = lambda c : (c == 0)*1 + c

vect_remove_zero = np.vectorize(remove_zero)


def image_ranGen(hauteur, largeur):
    image = np.random.randint(0, 255, (hauteur, largeur, 3))
    return image




def dim(image): # hauteur, largeur
    return (len(image), len(image[0]))





def make_iterations(image, taille, K, deb, n):
    image = image.astype(np.uint8)
    #on effectue n itérations
    p = Process("Affinage de l'image...", deb, n)
    for i in range(deb, n):
        size = kernel_size(i*taille/K)
        image = cv2.GaussianBlur(image, (size, size), i*taille/K)
        
        image = image * (2 + 5*(n-i)/n)
        image = image%256
        image = vect_remove_zero(image)
        p.aff(i)
    p.end()
    return image




def calc_dim(width, height, n):
    taille = sqrt(height*width)
    if taille <= 100 or n < 3: # cas de base
        return width, height
    else:
        return calc_dim(width//2, height//2, n-3)





def generer(depart, width, height, K, n, n_base): # on part de l'image de départ
    '''
    Cette fonction permet d'exécuter les itérations finales sur une image intermédiaire
    En particulier, depart <- $ et n_base = 3 correspondent au cas où l'on génère tout d'un coup
    [depart] : image utilisée comme cas de base
    [width] : largeur de l'image finale voulue
    [height] : hauteur de l'image finale voulue
    [K] : Paramètre de taille de forme lors de la génération
    [n] : nombre d'itérations. Ce nombre est normalement calculé en fonction de K
    [n_base] : nombre d'itérations restantes au bout duquel on applique le cas de base
    
    '''
    taille = sqrt(height*width)

    if taille <= 100 or n < n_base: # cas de base
        #on effectue n itérations
        print(f"Génération d'une image de taille {width}x{height}...")
        image = make_iterations(depart, taille, K, 1, n)
        
    else:
        image = generer(depart, width//2, height//2, K, n-3, n_base)
        # on agrandit l'image
        print(f"Agrandissement de l'image en {width}x{height}...")
        image = image_resized = np.repeat(np.repeat(image, 2, axis=0), 2, axis=1)
        # on effectue trois itérations
        image = make_iterations(image, taille, K, n-3, n)

    return image



def make_name():
    T = localtime()
    name = str(T[2]) + '-' + str(T[1]) + '-' + str(T[0]) + '_' + str(T[3]) + ':' + str(T[4]) + '.png'
    return name





def main(args):
    
    width = 1920
    height = 1080
    # plus K est grand, plus les symboles que l'on va observer seront petits
    # apparition des symboles : 100
    # mélange de flou et de taches : 400 et +

    # traitement des arguments

    if len(args) > 2 and args[1][0:2] == 'K=':
        K = int(args[1][2:])
        name = args[2]
    elif len(args) > 2 and args[2][0:2] == 'K=':
        K = int(args[2][2:])
        name = args[1]
    elif len(args) > 1 and args[1][0:2] == 'K=':
        K = int(args[1][2:])
        name = make_name()+".png"
    else:
        K = 110
        name = make_name()+".png"
    
    n = max(5,int(3000/K)) # calcul d'un nombre d'itérations en fonction de la taille des objets
    
    width_b, height_b = calc_dim(width, height, n) # calcul des dimensions des images de base
    
    seed = image_ranGen(height_b, width_b) # image de base pour toute la vidéo
    image = generer(seed, width, height, K, n, 3)

    save_image(image, name)
    
    print("Terminé !")





if __name__ == '__main__':
    '''for i in range(1, 51):
        K = 'K=' + str(randint(100, 120))
        print('\nParamétrage de la nouvelle image', K)
        main(['ranimg.py', K, make_name()])
        print(f'Image n°{i} terminée !')'''
    
    main(sys.argv)
