#########################################
### Définition du programme principal ###
#########################################

##################################################################

### Importation des modules utiles

import tkinter
import sys
import menu
import classes
import apropos
import limites

##################################################################

### Définition des fonctions d'affectation des molécules à partir des menus déroulants

def norma_name(nom_mol):
    '''Renvoie le nom de la molécule sans accents et autres trucs gênants.'''
    return(nom_mol.replace("é", "e").replace("è", "e"))

### Définition de la fonction d'ouverture des fichiers correspondants aux
### molécules sélectionnées

def ouverture(mol1, mol2):
    sys.path.append("fichiers_molecules")
    return(__import__(norma_name(mol1)), __import__(norma_name(mol2)))

### Définition de la fonction servant à sélectionner les orbitales
### qui interagiront (les plus proches en énergie)

def comparaison_et_selection_des_OF(fichier1, fichier2):
    '''Renvoie la liste des deux OF qui vont interagir à partir des fichiers des molécules'''
    energieHO1 = fichier1.molecule.HO.energie
    energieBV1 = fichier1.molecule.BV.energie
    energieHO2 = fichier2.molecule.HO.energie
    energieBV2 = fichier2.molecule.BV.energie
    if abs(energieHO1 - energieBV2) < abs(energieBV1 - energieHO2):
        return([fichier1.molecule.HO, fichier2.molecule.BV])
    else:
        return([fichier1.molecule.BV, fichier2.molecule.HO])

### Définition de la fonction servant à déterminer quelle sera l'interaction principale

def interaction_principale(liste_des_deux_OF):
    '''Renvoie une liste de structure [[OF1, atome_de_plus gros coefficient de l'OF1], [...]]'''
    orb_mol1 = liste_des_deux_OF[0]
    orb_mol2 = liste_des_deux_OF[1]
    couples1 = orb_mol1.tableau_de_coeffs.couple_atome_coeff
    couples2 = orb_mol2.tableau_de_coeffs.couple_atome_coeff
    
    coeff_max_1 = 0
    for i in range(1, len(couples1)):
        if abs(couples1[i][1]) >= coeff_max_1:
            coeff_max_1 = abs(couples1[i][1])
            atome1 = couples1[i][0]
            
    coeff_max_2 = 0
    for i in range(1, len(couples2)):
        if abs(couples2[i][1]) >= coeff_max_2:
            coeff_max_2 = abs(couples2[i][1])
            atome2 = couples2[i][0]

    return([[orb_mol1, atome1], [orb_mol2, atome2]])


### Définition de la fonction servant à déterminer quelle sera l'interaction secondaire et
### si elle sera favorable

def interaction_secondaire(liste_des_deux_OF):
    '''Renvoie une liste de structure \n
       [favorable ou non (format str),\n
       [OF1, atome de l'interaction secondaire de l'OF1],\n
       [OF2, atome de l'interaction secondaire de l'OF2]]'''
    orb_mol1 = liste_des_deux_OF[0]
    orb_mol2 = liste_des_deux_OF[1]
    couples1 = orb_mol1.tableau_de_coeffs.couple_atome_coeff
    couples2 = orb_mol2.tableau_de_coeffs.couple_atome_coeff

    coeff_max_1 = 0
    for i in range(1, len(couples1)):
        if abs(couples1[i][1]) >= abs(coeff_max_1):
            coeff_max_1 = couples1[i][1]
            atome_max1 = couples1[i][0]
            
    coeff_max_2 = 0
    for i in range(1, len(couples2)):
        if abs(couples2[i][1]) >= abs(coeff_max_2):
            coeff_max_2 = couples2[i][1]
            atome_max2 = couples2[i][0]

    couples1.remove([atome_max1, coeff_max_1])
    couples2.remove([atome_max2, coeff_max_2])

    coeff_sec_1 = 0
    for i in range(1, len(couples1)):
        if abs(couples1[i][1]) >= abs(coeff_sec_1):
            coeff_sec_1 = couples1[i][1]
            atome_sec1 = couples1[i][0]
            
    coeff_sec_2 = 0
    for i in range(1, len(couples2)):
        if abs(couples2[i][1]) >= abs(coeff_sec_2):
            coeff_sec_2 = couples2[i][1]
            atome_sec2 = couples2[i][0]

    if (coeff_max_1 * coeff_max_2 > 0 and coeff_sec_1 * coeff_sec_2 > 0) or \
       (coeff_max_1 * coeff_max_2 < 0 and coeff_sec_1 * coeff_sec_2 < 0):
        fav_ou_pas = 'favorable'
    else:
        fav_ou_pas = 'défavorable'

    return([fav_ou_pas, [orb_mol1, atome_sec1], [orb_mol2, atome_sec2]])

    

### Définition de la super fonction de la mort qui va être exécutée l'air de rien lorsque
### l'utilisateur cliquera sur Combiner

def fonction_principale():
    '''Ouvre la fenêtre de résultat de la tentative d'interaction entre les deux molécules sélectionnées'''

    molecule1 = norma_name(menu_choix_molecule1.get(tkinter.ACTIVE))
    molecule2 = norma_name(menu_choix_molecule2.get(tkinter.ACTIVE))

    print(molecule1, molecule2)

    fichiers = ouverture(molecule1, molecule2)
    fichier_molecule1 = fichiers[0]
    fichier_molecule2 = fichiers[1]
    
    
    principale = interaction_principale(comparaison_et_selection_des_OF(fichier_molecule1, fichier_molecule2))
    secondaire = interaction_secondaire(comparaison_et_selection_des_OF(fichier_molecule1, fichier_molecule2))


    fenetre_result = tkinter.Toplevel()
    fenetre_result.title("Fuuuuuuuuuuuusion !")
    #fenetre_result.geometry("800x600+100+50")


    # Ligne 1 : on affiche les noms des molécules
    
    tkinter.Label(fenetre_result, text = fichier_molecule1.molecule.nom, font=("Arial",25)).grid(row = 0, column = 0)
    tkinter.Label(fenetre_result, text = fichier_molecule2.molecule.nom, font=("Arial",25)).grid(row = 0, column = 1)


    # Ligne 2 : on affiche par quelle orbitale (HO ou BV) elles pourraient réagir

    tkinter.Label(fenetre_result, text = "(Réaction par sa " + principale[0][0].nature + ")").grid(row = 1, column = 0)
    tkinter.Label(fenetre_result, text = "(Réaction par sa " + principale[1][0].nature + ")").grid(row = 1, column = 1)


    # Ligne 3 : on affiche les images des molécules
    
    pre_image1 = tkinter.PhotoImage(file = "images/" + molecule1 + ".gif")
    largeur1 = pre_image1.width() ; hauteur1 = pre_image1.height()
    fond1 = tkinter.Canvas(fenetre_result, width = 300, height = 300, bg = 'white')
    fond1.grid(row = 2, column = 0)
    image1 = fond1.create_image(150, 150, image = pre_image1)
    
    pre_image2 = tkinter.PhotoImage(file = "images/" + molecule2 + ".gif")
    largeur2 = pre_image2.width() ; hauteur2 = pre_image2.height()
    fond2 = tkinter.Canvas(fenetre_result, width = 300, height = 300, bg = 'white')
    fond2.grid(row = 2, column = 1)
    image2 = fond2.create_image(150, 150, image = pre_image2)


    # Lignes 4 et 5 : On indique quels atomes de chaque molécule interagissent

    prim1 = 'Interaction principale : atome ' + principale[0][1].nature_lettres() + ' n°' + str(principale[0][1].numero)
    tkinter.Label(fenetre_result, text = prim1).grid(row = 4, column = 0)

    prim2 = 'Interaction principale : atome ' + principale[1][1].nature_lettres() + ' n°' + str(principale[1][1].numero)
    tkinter.Label(fenetre_result, text = prim2).grid(row = 4, column = 1)

    sec1 = 'Interaction secondaire (' + secondaire[0] + ') : atome ' + secondaire[1][1].nature_lettres() + \
           ' n°' + str(secondaire[1][1].numero)
    tkinter.Label(fenetre_result, text = sec1).grid(row = 5, column = 0)
    
    sec2 = 'Interaction secondaire (' + secondaire[0] + ') : atome ' + secondaire[2][1].nature_lettres() + \
           ' n°' + str(secondaire[2][1].numero)
    tkinter.Label(fenetre_result, text = sec2).grid(row = 5, column = 1)

    # Ligne 6 : on qualifie l'intensité de l'interaction envisagée

    ecart_E = abs(principale[0][0].energie - principale[1][0].energie)
    proba = "NaN"
    if ecart_E < 2:
        proba = "tout à fait possible"
    elif ecart_E < 4:
        proba = "possible"
    else:
        proba = "assez improbable"
    texte_interaction = "L'écart énergétique est de " + str(ecart_E) + " eV, une interaction orbitalaire est donc " + proba + "."
    txt = tkinter.Label(fenetre_result, text = texte_interaction)
    txt.grid(row = 6, column = 0, columnspan = 2)

    #Ligne 7 : Bouton retour pour quitter la fenêtre

    tkinter.Button(fenetre_result, text = "Retour", command = fenetre_result.destroy).grid(row = 7, column = 0, columnspan = 2)

    
    fenetre_result.mainloop() # Sans cette mainloop l'image n'est pas affichée...
    

##################################################################
    
### Création de la structure de l'interface

## Création de la fenêtre "conteneur"

fenetre = tkinter.Tk()
fenetre.title("Simulateur de réactions")
fenetre.geometry("800x600+100+50")

## Ligne 1 : bouton quitter en haut à droite

bouton_quitter = tkinter.Button(fenetre, text="Quitter", command=fenetre.destroy)
bouton_quitter.config(cursor = 'hand2')
bouton_quitter.grid(row = 1, column = 7, sticky = (tkinter.N, tkinter.E))

## Ligne 3 : Les deux menus déroulants de choix des molécules

# Les fonctions servant à afficher les noms en dessous

def sel_mol1(rien):
    try:
        fenetre.grid_slaves(row = 4, column = 2)[0].destroy()
        tkinter.Label(fenetre, text = menu_choix_molecule1.get(menu_choix_molecule1.curselection()[0]), font = ("Arial",18)).grid(row = 4, column = 2)
    except IndexError:
        tkinter.Label(fenetre, text = menu_choix_molecule1.get(menu_choix_molecule1.curselection()[0]), font = ("Arial",18)).grid(row = 4, column = 2)
            
    print(menu_choix_molecule1.get(menu_choix_molecule1.curselection()[0]))

def sel_mol2(rien):
    try:
        fenetre.grid_slaves(row = 4, column = 4)[0].destroy()
        tkinter.Label(fenetre, text = menu_choix_molecule2.get(menu_choix_molecule2.curselection()[0]), font = ("Arial",18)).grid(row = 4, column = 4)
    except IndexError:
        tkinter.Label(fenetre, text = menu_choix_molecule2.get(menu_choix_molecule2.curselection()[0]), font = ("Arial",18)).grid(row = 4, column = 4)
        
    print(menu_choix_molecule2.get(menu_choix_molecule2.curselection()[0]))

# Les listes déroulantes en elles-mêmes

cadre_liste1 = tkinter.Frame(fenetre)
#cadre_liste1.config
cadre_liste1.grid(row = 3, column = 2, padx = 20, pady = 10)

scrollbar1 = tkinter.Scrollbar(cadre_liste1)
menu_choix_molecule1 = tkinter.Listbox(cadre_liste1) 
menu_choix_molecule1.insert(1, *menu.menu)
scrollbar1.config(command = menu_choix_molecule1.yview)
menu_choix_molecule1.config(yscrollcommand = scrollbar1.set, width = 40, cursor = "hand2", activestyle = "dotbox")
scrollbar1.grid(row = 3, column = 3, sticky = (tkinter.N, tkinter.S))
menu_choix_molecule1.grid(row = 3, column = 2)
menu_choix_molecule1.bind('<<ListboxSelect>>', sel_mol1)

#tkinter.Label(fenetre, text = "+").grid(row = 3, column = 4)

cadre_liste2 = tkinter.Frame(fenetre)
#cadre_liste1.config
cadre_liste2.grid(row = 3, column = 4, padx = 20, pady = 10)

scrollbar2 = tkinter.Scrollbar(cadre_liste2)
menu_choix_molecule2 = tkinter.Listbox(cadre_liste2) 
menu_choix_molecule2.insert(1, *menu.menu)
scrollbar2.config(command = menu_choix_molecule2.yview)
menu_choix_molecule2.config(yscrollcommand = scrollbar2.set, width = 40, cursor = "hand2", activestyle = "dotbox")
scrollbar2.grid(row = 3, column = 6, sticky = (tkinter.N, tkinter.S))
menu_choix_molecule2.grid(row = 3, column = 5)
menu_choix_molecule2.bind('<<ListboxSelect>>', sel_mol2)


## Ligne 4 : les noms des molécules (affichage géré par les fonctions de la ligne 3) et un gros "+" au milieu

tkinter.Label(fenetre, text='+', font = ("Arial",25)).grid(row = 4, column = 3)

# Ligne 5 : Le bouton combiner qui va lancer la fonction centrale du programme

bouton_combiner = tkinter.Button(fenetre, text = "Combiner", \
                                 command = fonction_principale)
bouton_combiner.config(cursor = "hand2")
bouton_combiner.grid(row = 5, column = 3)

# Ligne 6 : Liens pour ouvrir des fenêtres explicatives des limites du programme et de la conception

lien_limites = tkinter.Label(fenetre, text = "Limites du programme", font = ("Arial", 10, 'underline'))
lien_limites.config(fg = 'blue', cursor = 'hand2')
lien_limites.grid(row = 6, column = 2, sticky = (tkinter.W))
lien_limites.bind('<Button-1>', limites.principale)


lien_apropos = tkinter.Label(fenetre, text = "À propos", font = ("Arial", 10, 'underline'))
lien_apropos.config(fg = 'blue', cursor = 'hand2')
lien_apropos.grid(row = 6, column = 5)
lien_apropos.bind('<Button-1>', apropos.principale)


# Très important, la fonction mainloop() permet d'éviter la fermeture de la
# fenêtre avant que ne soient exécutées des actions

fenetre.mainloop()
