#      _      __ _      __
#     | | /| / /| | /| / /      GREEN ANALYTICAL INDEX GENERATOR
#     | |/ |/ / | |/ |/ /       W.Wojnowski 2020
#     |__/|__/  |__/|__/        v.0.3 (alpha)
#
#

from tkinter import *
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.colors import LinearSegmentedColormap
from tkinter import ttk
from tkinter import filedialog
import tkinter.messagebox
import webbrowser
from math import log
from fpdf import FPDF
import os
from datetime import datetime

#***************** SPLASH SCREEN *******************************

root = Tk()

# show no frame
root.overrideredirect(True)
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
root.geometry('%dx%d+%d+%d' % (width*0.5, height*0.5, width*0.1, height*0.1))
image_file = "zaprawa_klejowa.gif"
#assert os.path.exists(image_file)
# use Tkinter's PhotoImage for .gif files
image = PhotoImage(file=image_file)
canvas = Canvas(root, height=height*0.5, width=width*0.5, bg="black")
canvas.create_image(width*0.5/2, height*0.5/2, image=image)
canvas.pack()
# show the splash screen for 5000 milliseconds then destroy
root.after(5000, root.destroy)
root.mainloop()

# **************** MAIN PROGRAM *******************************

root = Tk()

# app title:
root.title('Analytical Method Green Index Calculator')

# default app size:
root.geometry('800x500')
root.minsize(800, 500)
# root.configure(bg='white')
# create the small icon in the task bar:
root.iconbitmap('PG_favicon.ico')

# *********************** Functions ****************************

def clearFrame(frame):
    frame.destroy()
    global rightFrame
    rightFrame = Frame(root, width=300, height=450, padx=20)
    rightFrame.pack(side=RIGHT)

# Image save dialog:
def saveImage():
    ftypes = [('PNG file', '.png'), ('All files', '*')] #, ('JPG file', '.jpg') - seems that .jpg is not supported by some module
    filename = filedialog.asksaveasfilename(filetypes=ftypes, defaultextension='.png')
    # save the plot in the specified path; the 'tight' option removes the whitespace from around the figure:
    plt.savefig(filename, bbox_inches='tight')

# temporary placeholder function:
def doNothing():
    print("ok ok I won't...")

# create the popup window with some additional information:
def popup_bonus():
    win = Toplevel()
    win.wm_title("About Green Index")
    win.iconbitmap('PG_favicon.ico')

    def callback(event):
        webbrowser.open_new(event.widget.cget("text"))

    popup_label1 = Label(win, text='v. 0.1 2020 \n(c) Gdańsk University of Technology', justify=LEFT)
    popup_label1.grid(row=0, column=0, padx=8, pady=8)

    popup_label2 = Label(win, text=r'http://www.chem.pg.edu.pl/kcha', fg='blue', cursor='hand2', justify=LEFT)
    popup_label2.grid(row=1, column=0, padx=8, pady=8)
    popup_label2.bind('<Button-1>', callback)

    popup_label3 = Label(win, text='Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus convallis non sem ut aliquet. Praesent tempus fringilla suscipit. Phasellus tellus massa, semper et bibendum quis, rhoncus id neque. Sed euismod consectetur elit id tristique. Sed eu nibh id ante malesuada condimentum. Phasellus luctus finibus luctus. Pellentesque mi tellus, condimentum sit amet porta sit amet, ullamcorper quis elit. Pellentesque eu mollis nulla. Quisque vulputate, sem at iaculis vehicula, dui orci aliquet lectus, in facilisis odio dolor ut leo. Vivamus convallis hendrerit est luctus ornare. Nullam augue nisi, aliquam sit amet scelerisque hendrerit, pretium vel dui. Pellentesque sed tortor mollis, imperdiet quam quis, scelerisque erat. Vestibulum quis mollis dolor.',
                         wraplength=300, justify=LEFT, bg='white')
    popup_label3.grid(row=2, column=0, padx=8, pady=8)

    popup_button = Button(win, text="Close", command=win.destroy)
    popup_button.grid(row=3, column=0, padx=8, pady=8)

# connect a float in range 0.0 : 1.0 to a colour in a spectrum from red to yellow to green (256 discrete colour values):
def colorMapper(value):
    cmap = LinearSegmentedColormap.from_list('rg', ["red", "yellow", "green"], N=256)
    mapped_color = int(value * 255)
    color = cmap(mapped_color)
    return color

# function for refreshing the canvas:
def destroyCanvas(canvas):
    canvas.destroy()

# the final score variable:
entry_text = StringVar()

def printScore():
    try:
        global score
        score = (var_1 * weight_1.get()
                 + var_2 * weight_2.get()
                 + var_3 * weight_3.get()
                 + var_4 * weight_4.get()
                 + var_5 * weight_5.get()
                 + var_6 * weight_6.get()
                 + var_7 * weight_7.get()
                 + var_8 * weight_8.get()
                 + var_9 * weight_9.get()
                 + var_10 * weight_10.get()
                 + var_11 * weight_11.get()
                 + var_12 * weight_12.get())/(weight_1.get() + weight_2.get() + weight_3.get() + weight_4.get() + weight_5.get() +
                                                                                weight_6.get() + weight_7.get() +
                 weight_8.get() + weight_9.get() + weight_10.get() + weight_11.get() + weight_12.get())

        # set the final score as a text string rounded to 2 decimals:
        entry_text.set(str(round(score, 2)))

        print(' \n Total score: %s, rounded: %s' % (str(score), str(round(score, 2))))
        print('Criteria scores:')
    except NameError:
        tkinter.messagebox.showerror(title='Name Error', message='Please set all 12 variables.')

# a function to refresh the chart:
def chartPlotter(event=None):
    printScore(), clearFrame(rightFrame), pieChart(), print_variables()

# interface for assigning custom weights to the 12 variables:
def weightChoice(row, column, tab, weightVar):

    chckbxVar = StringVar()
    chckbxVar.set('disabled')

    radioVar = IntVar()
    radioVar.set(1)
    radio_1 = ttk.Radiobutton(tab, text='1', variable=radioVar, value=1)
    radio_2 = ttk.Radiobutton(tab, text='2', variable=radioVar, value=2)
    radio_3 = ttk.Radiobutton(tab, text='3', variable=radioVar, value=3)
    radio_4 = ttk.Radiobutton(tab, text='4', variable=radioVar, value=4)

    radio_1.grid(row=row + 1, column=column, sticky='sw', padx=(70, 0))
    radio_2.grid(row=row + 1, column=column, sticky='sw', padx=(100, 0))
    radio_3.grid(row=row + 1, column=column, sticky='sw', padx=(130, 0))
    radio_4.grid(row=row + 1, column=column, sticky='sw', padx=(160, 0))

    radio_1.config(state = DISABLED)
    radio_2.config(state = DISABLED)
    radio_3.config(state = DISABLED)
    radio_4.config(state = DISABLED)

    def printRadioVar():
        weightVar.set(radioVar.get())
        chartPlotter()

    weight_button = ttk.Button(tab, text='Set weight', command=printRadioVar)
    weight_button.grid(row=row + 1, column=column, sticky='sw', padx=(190, 0))
    weight_button.config(state = DISABLED)

    def printCheckbox():
        radios = (radio_1, radio_2, radio_3, radio_4)
        if chckbxVar.get() == 'disabled':
            radioVar.set(1)
            weightVar.set(1)
        for radio in radios:
            radio.config(state = DISABLED if chckbxVar.get() == 'disabled' else NORMAL)
        weight_button.config(state = DISABLED if chckbxVar.get() == 'disabled' else NORMAL)

    ttk.Checkbutton(tab, text='Modify default weights', command=lambda: [printCheckbox()], variable=chckbxVar, onvalue='enabled', offvalue='disabled').grid(row=row, column=column,
                                                                                columnspan=4, sticky='w', padx=8,
                                                                                pady=(60, 0))
    Label(tab, text='Weight: ').grid(row=row + 1, column = column, sticky='sw', padx=8)

# ********** Main menu ***********************************************************************************
menu = Menu(root)

# configure the menu:
root.config(menu=menu)

FileMenu = Menu(menu)
editMenu = Menu(menu)

# add drop-down functionality:
menu.add_cascade(label='File', menu=FileMenu)
FileMenu.add_command(label='Info', command=popup_bonus)
FileMenu.add_separator()
FileMenu.add_command(label='Save image', command=saveImage)

# FileMenu.add_command(label='Exit', command=doNothing)

# menu.add_cascade(label='Edit', menu=editMenu)
# editMenu.add_command(label='Redo', command=doNothing)

# ******** Statusbar *************
def createStatusBar():
    status = ttk.Label(root, textvariable=status_string, bd=1, relief=SUNKEN, anchor=W)
    status.pack(side=BOTTOM, fill=X)

status_string = StringVar()
# status_string.trace('w', createStatusBar)
status_string.set('test test')
status = ttk.Label(root, textvariable=status_string, borderwidth=1, relief=SUNKEN, anchor=W)
status.pack(side=BOTTOM, fill=X)
# status = Label(root, text=status_string.get(), bd=1, relief=SUNKEN, anchor=W)
# status.pack(side=BOTTOM, fill=X)

# ******** Two separate frames ******
leftFrame = Frame(root, bd=1, width=300, height=450)
rightFrame = Frame(root, width=300, height=450, padx=20)
bottomFrame = Frame(root, bd=1)

leftFrame.pack(side=LEFT, anchor=N)
rightFrame.pack(side=RIGHT)
bottomFrame.pack(side=BOTTOM, anchor=W)

# ************************* Tabs ***************************
# create tabs:
tab_parent = ttk.Notebook(leftFrame, height=400)
tab1 = ttk.Frame(tab_parent)
tab2 = ttk.Frame(tab_parent)
tab3 = ttk.Frame(tab_parent)
tab4 = ttk.Frame(tab_parent)
tab5 = ttk.Frame(tab_parent)
tab6 = ttk.Frame(tab_parent)
tab7 = ttk.Frame(tab_parent)
tab8 = ttk.Frame(tab_parent)
tab9 = ttk.Frame(tab_parent)
tab10 = ttk.Frame(tab_parent)
tab11 = ttk.Frame(tab_parent)
tab12 = ttk.Frame(tab_parent)

# add tabs to the tab parent:
tab_parent.add(tab1, text="1")
tab_parent.add(tab2, text="2")
tab_parent.add(tab3, text="3")
tab_parent.add(tab4, text="4")
tab_parent.add(tab5, text="5")
tab_parent.add(tab6, text="6")
tab_parent.add(tab7, text="7")
tab_parent.add(tab8, text="8")
tab_parent.add(tab9, text="9")
tab_parent.add(tab10, text="10")
tab_parent.add(tab11, text="11")
tab_parent.add(tab12, text="12")

# ****** matplotlib figure ********

weight_1 = IntVar()
weight_2 = IntVar()
weight_3 = IntVar()
weight_4 = IntVar()
weight_5 = IntVar()
weight_6 = IntVar()
weight_7 = IntVar()
weight_8 = IntVar()
weight_9 = IntVar()
weight_10 = IntVar()
weight_11 = IntVar()
weight_12 = IntVar()

weight_1.set(1)
weight_2.set(1)
weight_3.set(1)
weight_4.set(1)
weight_5.set(1)
weight_6.set(1)
weight_7.set(1)
weight_8.set(1)
weight_9.set(1)
weight_10.set(1)
weight_11.set(1)
weight_12.set(1)

# Begin with default values of variables to be able to generate the chart right away:
var_1 = 1.0
var_2 = 1.0
var_3 = 1.0
var_4 = 1.0
var_5 = 1.0
var_6 = 1.0
var_7 = 1.0
var_8 = 1.0
var_9 = 1.0
var_10 = 1.0
var_11 = 1.0
var_12 = 1.0

# function for updating the status bar:
def updateStatusBar():
    global status_string
    status_string.set(' scores: |  (1) %s  |  (2) %s  |  (3) %s  |  (4) %s  |  (5) %s  |  (6) %s  |  (7) %s  |  (8) %s  |  (9) %s  |  (10) %s  |  (11) %s  |  (12) %s  |'
                      % (str(round(var_1, 2)),
                        str(round(var_2, 2)),
                        str(round(var_3, 2)),
                        str(round(var_4, 2)),
                        str(round(var_5, 2)),
                        str(round(var_6, 2)),
                        str(round(var_7, 2)),
                        str(round(var_8, 2)),
                        str(round(var_9, 2)),
                        str(round(var_10, 2)),
                        str(round(var_11, 2)),
                        str(round(var_12, 2))
                        ))

# generate the pie chart (plot) with a circle decal in the middle:
def pieChart():  #weights, labels, colors

    colors = [colorMapper(var_1), colorMapper(var_2), colorMapper(var_3), colorMapper(var_4), colorMapper(var_5), colorMapper(var_6), colorMapper(var_7), colorMapper(var_8), colorMapper(var_9),
              colorMapper(var_10), colorMapper(var_11), colorMapper(var_12)]

    weights = [weight_1.get(), weight_2.get(), weight_3.get(), weight_4.get(), weight_5.get(), weight_6.get(), weight_7.get(), weight_8.get(), weight_9.get(), weight_10.get(), weight_11.get(),
               weight_12.get()]
    labels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']

    index_value = float(entry_text.get())

    fig, ax = plt.subplots(figsize=(3, 3), dpi=150)
    ax.clear()
    ax.axis('equal')
    radius = 1.0
    pie2 = ax.pie(weights, radius=radius, colors=colors, labeldistance=(radius * 0.875), labels=labels,
                  rotatelabels=False, startangle=90, counterclock=False,
                  wedgeprops={"edgecolor": "black", 'linewidth': 1}, textprops={'fontsize': (radius * 10)})

    plt.setp(pie2[1], rotation_mode="anchor", ha="center", va="center")
    for tx in pie2[1]:
        rot = tx.get_rotation()
        tx.set_rotation(rot)
        # if you want rotated labels:
        # tx.set_rotation(rot+ 90 + (1 - rot // 180) * 180)

    circle = plt.Circle(xy=(0, 0), radius=(radius * 0.75), facecolor=colorMapper(index_value), edgecolor='black',
                        linewidth=1)
    plt.gca().add_artist(circle)

    ax.text(0.5, 0.5, str(index_value),
            verticalalignment='center', horizontalalignment='center',
            transform=ax.transAxes,
            color='black', fontsize=(radius * 40))

    fig.tight_layout()  # for exporting a compact figure

    # Pack the figure into a canvas:
    canvas = FigureCanvasTkAgg(fig, master=rightFrame)  # A tk.DrawingArea.
    plot_widget = canvas.get_tk_widget()

    plot_widget.pack(side=TOP)
    # print(weight_12.get())

# **************************************

# define a temporary function to test the printing of global variables:
def print_variables():
    try:
        print ('var_1: ' + str(var_1))
        print('var_2: ' + str(var_2))
        print('var_3: ' + str(var_3))
        print('var_5: ' + str(var_5))
        print('var_6: ' + str(var_6))
        print('var_7: ' + str(var_7))
        print('var_8: ' + str(var_8))
        print('var_9: ' + str(var_9))
        print('var_10: ' + str(var_10))
        print('var_11: ' + str(var_11))
        print('var_12: ' + str(var_12))

        updateStatusBar()

    except NameError:
        tkinter.messagebox.showerror(title='Name error',
                                     message='Please fill all the variables')

# generate tab captions and prompt captions:
def tab(tab_no, text1, text2):
    Label(tab_no, text=text1, wraplength=300, justify=LEFT).grid(sticky='w', row=0, column=0, padx=8, pady=8)
    Label(tab_no, text=text2, wraplength=300, justify=LEFT).grid(sticky='w', row=1, column=0, padx=8, pady=8)

# *****************************************************************************************************************
#               TAB 1
# *****************************************************************************************************************
content_1 = tab(tab1, text1='Direct analytical techniques should be applied to avoid sample treatment.',
                text2='Select the sampling procedure:')

# Create a Tkinter variable
var_1_text = StringVar(tab1)
# Dictionary with options
var_1_text_choices = {'Remote sensing without sample damage': 1.0,
                      'Remote sensing with little physical damage': 0.95,
                      'Non-invasive analysis': 0.9,
                      'In-field sampling and direct analysis': 0.85,
                      'In-field sampling and on-line analysis': 0.78,
                      'On-line analysis': 0.70,
                      'At-line analysis': 0.60,
                      'Off-line analysis': 0.48,
                      'External sample pre- and treatment and batch analysis (reduced number of steps)': 0.30,
                      'External sample pre- and treatment and batch analysis (large number of steps)': 0.0}
var_1_text.set('SAMPLING PROCEDURE')

dropDown_1 = OptionMenu(tab1, var_1_text, *var_1_text_choices.keys())
dropDown_1.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w')
dropDown_1.grid(sticky='w', row=2, column=0, padx=8, pady=8)

# on change dropdown value, get the dictionary value and modify the global variable
def change_dropdown_1(*args):
    # Define the global variable for Principle 1:
    global var_1
    var_1 = None
    var_1 = var_1_text_choices[var_1_text.get()]
    print('var_1:' + str(var_1))

    chartPlotter()

# link function to change dropdown
# The trace method of the StringVar allows to detect the change in the variable that activate a call to a function
var_1_text.trace('w', change_dropdown_1)

W_1 = weightChoice(10, 0, tab1, weight_1)

# *****************************************************************************************************************
#               TAB 2
# *****************************************************************************************************************

content_2 = tab(tab2, text1='Minimal sample size and minimal number of samples are goals.',
                text2='Enter the amount of sample in either g or mL:')

amount_var = StringVar()
amount_var.set('input')
sample_amount_entry = ttk.Entry(tab2, textvariable=amount_var, width=15)
sample_amount_entry.grid(sticky='w', row=2, column=0, padx=8, pady=8)

# the event=None is passed so that the entry.bind does not return a positional argument
def change_entry_2(event=None):
    global var_2
    var_2 = None

    try:
        if float(amount_var.get()) > 100:
            var_2 = 0
        elif float(amount_var.get()) < 0.1:
            var_2 = 1.0
        else:
            var_2 = abs(-0.142 * log(float(amount_var.get())) + 0.65)   # absolute value to avoid negative values
        print('var_2:' + str(var_2))

    except ValueError:
        tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an intiger, e.g. 0.14 or 21.')

    chartPlotter()

# bind the <Return> key to the entry window, so that the function gets called as an alternative to the 'set' button:
sample_amount_entry.bind('<Return>', change_entry_2)
# insert a button that does the same:
ttk.Button(tab2, text='Set', command=change_entry_2).grid(row=2, column=0, padx=8, pady=8)

W_2 = weightChoice(10, 0, tab2, weight_2)

# *****************************************************************************************************************
#               TAB 3
# *****************************************************************************************************************

content_3 = tab(tab3, 'If possible, measurements should be performed in situ.',
                'What is the positioning of the analytical device?')

# Create a Tkinter variable
var_3_text = StringVar(tab3)
# Dictionary with options
var_3_text_choices = {'off-line': 0.0,
                      'at-line': 0.33,
                      'on-line': 0.66,
                      'in-line': 1.0}

var_3_text.set('select')

dropDown_3 = OptionMenu(tab3, var_3_text, *var_3_text_choices.keys())
dropDown_3.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w')
dropDown_3.grid(sticky='w', row=2, column=0, padx=8, pady=8)

# on change dropdown value, get the dictionary value and modify the global variable
def change_dropdown_3(*args):
    global var_3
    var_3 = None
    var_3 = var_3_text_choices[var_3_text.get()]
    print('var_3:' + str(var_3))
    chartPlotter()

# link function to change dropdown
# The trace method of the StringVar allows to detect the change in the variable that activate a call to a function
var_3_text.trace('w', change_dropdown_3)

W_3 = weightChoice(10, 0, tab3, weight_3)

# *************************** TAB 4 ************************************************************************************
content_4 = tab(tab4, text1='Integration of analytical processes and operations saves energy and reduces the use of reagents.',
                text2='How many major, distinct steps are there in the sample preparation procedure? These include e.g. sonication,'
                      ' mineralization, centrifugation, derivatization, extraction, etc.')

var_4_text = StringVar(tab4)
# Dictionary with options
var_4_text_choices = {'3 or fewer': 1.0,
                      '4': 0.8,
                      '5': 0.6,
                      '6': 0.4,
                      '7': 0.2,
                      '8 or more': 0.0}

var_4_text.set('select')

dropDown_4 = OptionMenu(tab4, var_4_text, *var_4_text_choices.keys())
dropDown_4.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w')
dropDown_4.grid(sticky='w', row=2, column=0, padx=8, pady=8)

# on change dropdown value, get the dictionary value and modify the global variable
def change_dropdown_4(*args):
    global var_4
    var_4 = None
    var_4 = var_4_text_choices[var_4_text.get()]
    print('var_4:' + str(var_4))
    chartPlotter()

# link function to change dropdown
# The trace method of the StringVar allows to detect the change in the variable that activate a call to a function
var_4_text.trace('w', change_dropdown_4)

W_4 = weightChoice(10, 0, tab4, weight_4)

# *****************************************************************************************************************
#               TAB 5
# *****************************************************************************************************************
content_5 = tab(tab5, text1='Automated and miniaturized methods should be selected.', text2='Degree of automation:')

# Create a Tkinter variable
var_5a_text = StringVar(tab5)
# Dictionary with options
var_5a_text_choices = {'automatic': 1.0,
                      'semi-automatic': 0.5,
                      'manual': 0.0}

var_5a_text.set('select')

dropDown_5a = OptionMenu(tab5, var_5a_text, *var_5a_text_choices.keys())
dropDown_5a.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w')
dropDown_5a.grid(sticky='w', row=2, column=0, padx=8, pady=8)

var_5a = DoubleVar(tab5)
var_5a.set(1.0)
var_5b = DoubleVar(tab5)
var_5b.set(1.0)

def variableFive():
    global var_5
    a = var_5a.get()
    b = var_5b.get()
    if a == 1.0 and b == 1.0:
        var_5 = 1.0
    elif a == 0.5 and b == 1.0:
        var_5 = 0.75
    elif a == 0.0 and b == 1.0:
        var_5 = 0.5
    elif a == 1.0 and b == 0.0:
        var_5 = 0.5
    elif a == 0.5 and b == 0.0:
        var_5 = 0.25
    elif a == 0.0 and b == 0.0:
        var_5 = 0.0

# on change dropdown value, get the dictionary value and modify the global variable
def change_dropdown_5a(*args):

    var_5a.set(var_5a_text_choices[var_5a_text.get()])
    print('var_5a:' + str(var_5a))
    variableFive()
    # global var_5
    # var_5 = var_5a.get() * var_5b.get()

    chartPlotter()

# link function to change dropdown
# The trace method of the StringVar allows to detect the change in the variable that activate a call to a function
var_5a_text.trace('w', change_dropdown_5a)

Label(tab5, text='Sample preparation:', wraplength=300, justify=LEFT).grid(sticky='w', row=3, column=0, padx=8, pady=8)

var_5b_text = StringVar(tab5)
var_5b_text_choices = {'miniaturized': 1.0,
                      'not miniaturized': 0.0}
var_5b_text.set('select')
dropDown_5b = OptionMenu(tab5, var_5b_text, *var_5b_text_choices.keys())
dropDown_5b.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w')
dropDown_5b.grid(sticky='w', row=4, column=0, padx=8, pady=8)

def change_dropdown_5b(*args):

    var_5b.set(var_5b_text_choices[var_5b_text.get()])
    # print('var_5b:' + str(var_5b))
    # global var_5
    # var_5 = var_5a.get() * var_5b.get()
    variableFive()
    print('var_5:' + str(var_5))

    chartPlotter()

var_5b_text.trace('w', change_dropdown_5b)

W_5 = weightChoice(10, 0, tab5, weight_5)

# *****************************************************************************************************************
#               TAB 6
# *****************************************************************************************************************
content_6 = tab(tab6, text1='Derivatization should be avoided.', text2='Select derivatization agents (if used):')

# combine the selected options into a single string to produce a label caption
def concatenate_text(list_):
    caption = ''
    for i in list_:
        caption = caption + i + '; '
    return caption

def Select():
    reslist = list()
    selecion = lbox.curselection()

    for i in selecion:
        entered = lbox.get(i)
        reslist.append(entered)
        global_list.append(entered)

    # update the label box with selected deriv. agents:
    print(reslist)
    print(global_list)
    v.set(concatenate_text(global_list))

    global var_6
    var_6 = 1.0

    # add a -0.2 penaly for using derivatization agents:
    for CAS in global_list:
        var_6 = var_6 * lbox_list[CAS]

    if var_6 > 0.2:
        var_6 = var_6 - 0.2
    else:
        var_6 = 0.0

    print(var_6)
    chartPlotter()

# update the list box
def update_list(*args):
    search_term = search_var.get()

    lbox.delete(0, END)

    for item in lbox_list.keys():
        if search_term.lower() in item.lower():
            lbox.insert(END, item)

# clear the selection and the displayed label caption
def clear_list():
    global v, global_list
    v.set('')
    global var_6
    var_6 = 1.0
    global_list = []
    chartPlotter()

# create global variables
global_list = []
# if derivatization should be avoided, then shouldn't the highest value in the case in which derivatization agents are used be lower than 1.0?
lbox_list = {
             # "None": 1.1,
             "5950-69-6": 1,
             "30084-90-3": 1,
             "12093-10-6": 1,
             "6283-74-5": 1,
             "119-53-9": 1,
             "126-81-8": 1,
             "24257-93-0": 1,
             "58068-85-2": 0.949158975023003,
             "1273-85-4": 0.949158975023003,
             "4233-33-4": 0.949158975023003,
             "100-10-7": 0.949158975023003,
             "38183-12-9": 0.945068038553621,
             "41468-25-1": 0.937140592655187,
             "1395920-13-4": 0.937140592655187,
             "521-24-4": 0.937140592655187,
             "56606-21-4": 0.935584744799731,
             "65-22-5": 0.925393321432741,
             "68123-33-1": 0.925393321432741,
             "913253-56-2": 0.913914155272091,
             "124522-09-4": 0.913914155272091,
             "223463-14-7": 0.902699986612441,
             "1118-68-9": 0.901394170230429,
             "952102-12-4": 0.901394170230429,
             "536-69-6": 0.901394170230429,
             "203256-20-6": 0.901394170230429,
             "86516-36-1": 0.899210326049394,
             "861881-76-7": 0.886368566581839,
             "56139-74-3": 0.869932201280637,
             "84806-27-9": 0.865490140567591,
             "91366-65-3": 0.865490140567591,
             "67229-93-0": 0.855427480281241,
             "1273-82-1": 0.855042238169516,
             "50632-57-0": 0.846792397075292,
             "10199-89-0": 0.839008465483774,
             "152111-91-6": 0.836037308222637,
             "7149-49-7": 0.830362674910287,
             "3029-19-4": 0.830362674910287,
             "68572-87-2": 0.829473879117877,
             "12152-94-2": 0.829473879117877,
             "29270-56-2": 0.829154698457689,
             "24463-19-2": 0.827803622060042,
             "100-39-0": 0.825375773537705,
             "550-44-7": 0.822230968349539,
             " 49759-20-8": 0.822230968349539,
             "38609-97-1": 0.822230968349539,
             "35661-51-9": 0.822230968349539,
             "10401-59-9": 0.822230968349539,
             "70402-14-1": 0.822230968349539,
             "131076-14-7": 0.822230968349539,
             "214147-22-5": 0.822230968349539,
             "4930-98-7": 0.822230968349539,
             "569355-30-2": 0.822230968349539,
             "53348-04-2": 0.820406195102248,
             "67580-39-6": 0.818423316626862,
             "68133-98-2": 0.814016502590708,
             "81864-15-5": 0.814016502590708,
             "113722-81-9": 0.814016502590708,
             "15537-71-0": 0.809079828950995,
             "33008-06-9": 0.809079828950995,
             "139332-64-2": 0.809079828950995,
             "62642-61-9": 0.806764775754175,
             "100139-54-6": 0.806764775754175,
             "62796-29-6": 0.797901423240715,
             "87-13-8": 0.783298381421747,
             "35231-44-8": 0.778837259389339,
             "88404-25-5": 0.778837259389339,
             "485-47-2": 0.77674392680131,
             "58520-45-9": 0.776282830117383,
             "107-91-5": 0.776282830117383,
             "139332-66-4": 0.776282830117383,
             "89-25-8": 0.776282830117383,
             "18428-76-7": 0.776282830117383,
             "20624-25-3": 0.763216179776723,
             "27072-45-3": 0.762516465156704,
             "1459205-36-7": 0.755628677634781,
             "96483-68-0": 0.747181595887401,
             "132098-76-1": 0.747181595887401,
             "98-59-9": 0.746227267824334,
             "7612-98-8": 0.744246233476037,
             "5415-58-7": 0.742560985030801,
             "76-83-5": 0.740506239181083,
             "1293-79-4": 0.740506239181083,
             "28920-43-6": 0.740506239181083,
             "100-07-2": 0.740506239181083,
             "99-73-0": 0.738962425018157,
             "22265-37-8": 0.737084384687495,
             "3731-51-9": 0.737084384687495,
             "141903-34-6": 0.737084384687495,
             "122-04-3": 0.732376041854033,
             "4755-50-4": 0.732376041854033,
             "99-33-2": 0.732376041854033,
             "605-65-2": 0.723192330411814,
             "56512-49-3": 0.723192330411814,
             "126565-42-2": 0.723192330411814,
             "7693-46-1": 0.721322673837572,
             "1711-06-4": 0.717883414280986,
             "93128-04-2": 0.717798274857161,
             "613-54-7": 0.716357636495872,
             "74367-78-5": 0.710065827927279,
             "119-26-6": 0.692633685424727,
             "2508-19-2": 0.692425832968952,
             "21614-17-5": 0.682522312223409,
             "80-11-5": 0.681782236352849,
             "100-46-9": 0.679263084173718,
             "55486-13-0": 0.666338980106273,
             "16315-59-6": 0.665281844920184,
             "5102-79-4": 0.664748970983542,
             "70-34-8": 0.664086673111964,
             "132-32-1": 0.659883743356088,
             "36410-81-8": 0.659179085176979,
             "100-16-3": 0.659159320154698,
             "104077-15-8": 0.659091847163412,
             "4083-64-1": 0.649947842697737,
             "21324-39-0": 0.634865149902982,
             "2978-11-2_": 0.629540812510628,
             "456-27-9": 0.628988106517093,
             "98-09-9": 0.628032387327697,
             "103-72-0": 0.606674230911606,
             "504-29-0": 0.587444277328904,
             "86-84-0": 0.566544585073271,
             "36877-69-7": 0.556132009449506,
             "103-71-9": 0.525453097624119,
             "551-06-4": 0.510591749035237,
             "643-79-8": 0.486298449205041,
             "98-88-4": 0.475562851988167,
             "5470-11-1": 0.466906948575218,
             "99-65-0": 0.414382740812551,
             "95-54-5": 0.409876625997181,
             "60-24-2": 0.380580959884422,
             "1118-71-4": 1,
             "4426-47-5": 0.98287765750619,
             "35342-88-2": 0.934408589712128,
             "13435-12-6": 0.934408589712128,
             "122-51-0": 0.90808769546171,
             "17455-13-9": 0.898290310316299,
             "7449-74-3": 0.896162794934563,
             "1188-33-6": 0.873968193624155,
             "1133-63-7": 0.845047181007906,
             "57981-02-9": 0.843544327015115,
             "3449-26-1": 0.831289869514086,
             "54925-64-3": 0.831289869514086,
             "7453-26-1": 0.831289869514086,
             "23231-91-6": 0.82477424558194,
             "423-39-2": 0.821174784952006,
             "3332-29-4": 0.817379220173597,
             "18297-63-7": 0.804205531712304,
             "13257-81-3": 0.796997494513717,
             "73980-71-9": 0.796226219175859,
             "828-73-9": 0.796226219175859,
             "36805-97-7": 0.785921382127458,
             "6006-65-1": 0.785921382127458,
             "4909-78-8": 0.785921382127458,
             "920-68-3": 0.785921382127458,
             "653-37-2": 0.78349900067157,
             "422-05-9": 0.78349900067157,
             "2182-66-3": 0.766534069464941,
             "354-64-3": 0.763789874990475,
             "58479-61-1": 0.763598909336104,
             "13154-24-0": 0.763598909336104,
             "70-11-1": 0.761090045768687,
             "723336-86-5": 0.761090045768687,
             "850418-19-8": 0.761090045768687,
             "850418-20-1": 0.761090045768687,
             "1546-79-8": 0.758242430472499,
             "24589-78-4": 0.758242430472499,
             "53296-64-3": 0.758242430472499,
             "77377-52-7": 0.758242430472499,
             "82112-21-8": 0.757927402509425,
             "375-22-4": 0.756114760094685,
             "336-59-4": 0.756114760094685,
             "356-42-3": 0.756114760094685,
             "420-37-1": 0.75205982910284,
             "77-76-9": 0.750711985826051,
             "20082-71-7": 0.749832128609721,
             "2251-50-5": 0.747481224283863,
             "100-11-8": 0.745015119615777,
             "18162-48-6": 0.743146183479067,
             "425-75-2": 0.742441949845756,
             "1765-40-8": 0.742441949845756,
             "76437-40-6": 0.742441949845756,
             "80522-42-5": 0.742441949845756,
             "1538-75-6": 0.74152936540163,
             "98-03-3": 0.739537905180287,
             "87020-42-6": 0.737007165264001,
             "589-15-1": 0.736264650708209,
             "2857-97-8": 0.736016815715654,
             "17950-40-2": 0.732111366794642,
             "407-25-0": 0.731258587142799,
             "115-20-8": 0.730613289210088,
             "823-96-1": 0.721670319376414,
             "71735-32-5": 0.7183910746808,
             "333-27-7": 0.7183910746808,
             "996-50-9": 0.714539433160182,
             "3768-58-9": 0.714539433160182,
             "685-27-8": 0.713300737795531,
             "25561-30-2": 0.713300737795531,
             "124-41-4": 0.70689269806413,
             "15933-59-2": 0.705803556150421,
             "18156-74-6": 0.705803556150421,
             "123-62-6": 0.703483768736821,
             "2083-91-2": 0.703043095426246,
             "10416-59-8": 0.700353286433786,
             "69739-34-0": 0.696757084764058,
             "107-46-0": 0.696026303459663,
             "541-88-8": 0.680085578563036,
             "994-30-9": 0.659639561940176,
             "75-26-3": 0.65077439166517,
             "543-27-1": 0.643008761928377,
             "6092-54-2": 0.619827404668639,
             "76-02-8": 0.618803077595292,
             "75-77-4": 0.606190113014358,
             "7719-09-7": 0.598432942089881,
             "1066-35-9": 0.590259358282054,
             "4637-24-5": 0.587695662266982,
             "920-66-1": 0.5835440122017,
             "8077-35-8": 0.580905093441462,
             "108-24-7": 0.56539851162607,
             "10294-34-5": 0.546920496297807,
             "999-97-3": 0.539120875551113,
             "7637-07-2": 0.536295783559384,
             "75-89-8": 0.517064147633066,
             "1899-02-1": 0.453968334570473,
             "17287-03-5": 0.450591161239778,
             "7664-93-9": 0.430740368201206,
             "132228-87-6": 0.389860157052623,
             "75-59-2": 0.35207841911058,
             "77-78-1": 0.185707987424391,
             "19132-06-0": 1,
             "1052236-86-8": 1,
             "135806-59-6": 1,
             "139658-04-1": 1,
             "108031-79-4": 1,
             "124529-02-8": 0.789788397239459,
             "124529-07-3": 0.789788397239459,
             "24277-43-8": 0.789788397239459,
             "958300-06-6": 0.789788397239459,
             "5978-70-1": 0.661143997568766,
             "3886-70-2": 0.62276366189702,
             "20445-31-2": 0.616318224518582,
             "17257-71-5": 0.616318224518582,
             "81655-41-6": 0.616318224518582,
             "21451-74-1": 0.616318224518582,
             "14645-24-0": 0.616318224518582,
             "147948-52-5": 0.581990910059596,
             "104371-20-2": 0.581990910059596,
             "132679-61-9": 0.56145194750795,
             "210529-62-7": 0.56145194750795,
             "3347-90-8": 0.550846501071722,
             "104530-16-7": 0.547959104197752,
             "39637-74-6": 0.547959104197752,
             "39262-22-1": 0.52022184149657,
             "1517-69-7": 0.474716248097616,
             "1445-91-6": 0.474716248097616,
             "107474-79-3": 0.437963083473382,
             "14602-86-9": 0.412055011328408,
             "3886-69-9": 0.358144912356212,
             "2627-86-3": 0.326740839342668,
             "24277-44-9": 0.288185973785988,
             "62414-75-9": 0.288185973785988,
             "14152-97-7": 0.288185973785988,
             "42340-98-7": 0.176714727821325,
             "14649-03-7": 0.132441393121765,
             "33375-06-3": 0.116078677380125,
             }
v = StringVar()
chckbxVar_tab6 = StringVar()
chckbxVar_tab6.set('disabled')

# set initial blank value of the StringVar
v.set('')

search_var = StringVar()
search_var.trace("w", update_list)
entry = ttk.Entry(tab6, textvariable=search_var, width=13)
# disable the lookup box initially:
# entry.config(state=DISABLED)

scrollbar = ttk.Scrollbar(tab6, orient='vertical')
scrollbar.grid(row=3, column=0, sticky='w', ipady=30, padx=(220, 0))
lbox = Listbox(tab6, width=34, height=6, yscrollcommand=scrollbar.set)  # selectmode=MULTIPLE
# disable the lbox initially:
# lbox.config(state=DISABLED)

# def lboxActivator():
#
#     # chckbxVar_tab6.set('disabled')
#     if chckbxVar_tab6.get() == 'enabled':
#         entry.config(state=ACTIVE)
#         lbox.config(state=NORMAL)
#     elif chckbxVar_tab6.get() == 'disabled':
#         entry.config(state=DISABLED)
#         lbox.config(state=DISABLED)
#
#     ttk.Checkbutton(tab6, text='Derivatisation agent is used', command=lboxActivator, variable=chckbxVar_tab6, onvalue='enabled', offvalue='disabled').grid(row=30, column=0)

# lboxActivator()

Label(tab6, text='CAS lookup: ').grid(row=2, column=0, padx=8, pady=3, sticky='w')
entry.grid(row=2, column=0, padx=(100, 0), pady=3, sticky='w')
lbox.grid(row=3, column=0, padx=8, pady=3, sticky='w')

# link the scrollbar to the list box
scrollbar.config(command=lbox.yview)

ttk.Button(tab6, text="Select", command=Select, width=8).grid(column=1, row=3, padx=4)
# clear the selection and the caption
ttk.Button(tab6, text='Clear', command=lambda:[clear_list(), update_list()], width=8).grid(column=1, row=4, padx=4)

Label(tab6, text='Selected CAS: ').grid(column=0, row=4, sticky='w', padx=8, pady=0)
ttk.Label(tab6, textvariable=v, wraplength=180, width=34, relief='groove').grid(column=0, row=5, sticky='w', padx=8, pady=4)

# call the function to populate the list at the beginning
update_list()

W_6 = weightChoice(10, 0, tab6, weight_6)

# *****************************************************************************************************************
#               TAB 7
# *****************************************************************************************************************
content_7 = tab(tab7, text1='Generation of a large volume of analytical waste should be avoided, and proper management'
                            'of analytical waste should be provided.', text2='Enter the amount of waste in g or mL:')

amount_var7 = StringVar()
amount_var7.set('input')

# the event=None is passed so that the entry.bind does not return a positional argument
def change_entry_7(event=None):
    global var_7
    var_7 = None

    try:
        if float(amount_var7.get()) > 150:
            var_7 = 0
        elif float(amount_var7.get()) < 0.1:
            var_7 = 1.0
        else:
            var_7 = abs(-0.134 * log(float(amount_var7.get())) + 0.6946)   # absolute value to avoid negative values
        print('var_7:' + str(var_7))

    except ValueError:
        tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an intiger, e.g. 0.14 or 21.')

    chartPlotter()

sample_amount_entry7 = ttk.Entry(tab7, textvariable=amount_var7, width=15)
sample_amount_entry7.grid(sticky='w', row=2, column=0, padx=8, pady=8)

# bind the <Return> key to the entry window, so that the function gets called as an alternative to the 'set' button:
sample_amount_entry7.bind('<Return>', change_entry_7)
# insert a button that does the same:
ttk.Button(tab7, text='Set', command=change_entry_7).grid(row=2, column=0, padx=8, pady=8)

W_7 = weightChoice(10, 0, tab7, weight_7)

# *****************************************************************************************************************
#               TAB 8
# *****************************************************************************************************************
content_8 = tab(tab8, text1='Multi-analyte or multi-parameter methods are preferred '
                            'versus methods using one analyte at a time.',
                      text2='Number of analytes determined in a single run:')

amount_var8a = StringVar()
amount_var8a.set('input')
sample_amount_entry8a = ttk.Entry(tab8, textvariable=amount_var8a, width=15)
sample_amount_entry8a.grid(sticky='w', row=2, column=0, padx=8, pady=8)

Label(tab8, text='Sample throughput (samples analysed per hour):', wraplength=300, justify=LEFT).grid(sticky='w', row=3, column=0, padx=8, pady=8)

amount_var8b = StringVar()
amount_var8b.set('input')
sample_amount_entry8b = ttk.Entry(tab8, textvariable=amount_var8b, width=15)
sample_amount_entry8b.grid(sticky='w', row=4, column=0, padx=8, pady=8)

def change_entry_8(event=None):
    global var_8
    var_8 = None

    try:
        if (float(amount_var8a.get()) * float(amount_var8b.get())) < 1.0:
            var_8 = 0.0
        elif (float(amount_var8a.get()) * float(amount_var8b.get())) > 70.0:
            var_8 = 1.0
        else:
            var_8 = abs(0.2429 * log(float(amount_var8a.get()) * float(amount_var8b.get())) - 0.0517)   # absolute value to avoid negative values
        print('var_8:' + str(var_8))

    except ValueError:
        tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an intiger, e.g. 0.14 or 21.')
    # refresh the plot:
    chartPlotter()

sample_amount_entry8b.bind('<Return>', change_entry_8)
ttk.Button(tab8, text='Set', command=change_entry_8).grid(row=5, column=0, padx=8, pady=8)

W_8 = weightChoice(10, 0, tab8, weight_8)

# *****************************************************************************************************************
#               TAB 9
# *****************************************************************************************************************
content_9 = tab(tab9, text1='The use of energy should be minimized.',
                      text2='Select the most energy-intensive technique used in the method:')

var_9_text = StringVar(tab9)
amount_var9 = StringVar(tab9)
amount_var9.set('input')
# Dictionary with options
var_9_text_choices = { 'None': 1.0,
                       'FTIR': 1.0,           # what about vortexing, incubation, etc.? Mineralization?
                       'Immunoassay': 1.0,
                       'Spectrofluorometry': 1.0,
                       'Titration': 1.0,
                       'UPLC': 1.0,
                       'UV-Vis Spectrometry': 1.0,
                       'AAS': 0.5,
                       'GC': 0.5,
                       'ICP-MS': 0.5,
                       'LC': 0.5,
                       'NMR': 0.0,
                       'GC-MS': 0.0,
                       'LC-MS': 0.0,
                       'X-ray diffractometry': 0.0}

var_9_text.set('select')

dropDown_9 = OptionMenu(tab9, var_9_text, *var_9_text_choices.keys())
dropDown_9.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w')
dropDown_9.grid(sticky='w', row=2, column=0, padx=8, pady=8)

def change_dropdown_9(*args):
    global var_9
    var_9 = 1.0
    var_9 = var_9_text_choices[var_9_text.get()]
    print('var_9:' + str(var_9))
    chartPlotter()

var_9_text.trace('w', change_dropdown_9)

ttk.Label(tab9, text='Alternatively, estimate the total power consumption of a single analysis in kWh:', wraplength=250, justify=LEFT).grid(sticky='w', row=3, column=0, padx=8, pady=8)

sample_amount_entry9 = ttk.Entry(tab9, textvariable=amount_var9, width=15)
sample_amount_entry9.grid(sticky='w', row=4, column=0, padx=8, pady=8)

def change_entry_9(event=None):
    global var_9
    var_9 = 1.0

    try:
        if float(amount_var9.get()) > 1.5:
            var_9 = 0.0
        elif float(amount_var9.get()) < 0.1:
            var_9 = 1.0
        else:
            var_9 = abs(-0.7143 * (float(amount_var9.get())) + 1.0714)   # absolute value to avoid negative values
        print('var_9:' + str(var_9))

    except ValueError:
        tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an intiger, e.g. 0.14 or 21.')

    chartPlotter()

sample_amount_entry9.bind('<Return>', change_entry_9)
ttk.Button(tab9, text='Set', command=change_entry_9).grid(row=4, column=0, padx=8, pady=8)

W_9 = weightChoice(10, 0, tab9, weight_9)

# *****************************************************************************************************************
#               TAB 10
# *****************************************************************************************************************
content_10 = tab(tab10, text1='Reagents obtained from renewable sources should be preferred.',
                      text2='Select the type of reagents:')

var_10_text = StringVar(tab10)
# Dictionary with options
var_10_text_choices = {'No reagents': 1.0,
                       'All reagents are bio-based': 1.0,
                       'Some reagents are bio-based': 0.5,
                       'None of the reagents are from bio-based sources': 0.0
                       }

var_10_text.set('select')

dropDown_10 = OptionMenu(tab10, var_10_text, *var_10_text_choices.keys())
dropDown_10.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w')
dropDown_10.grid(sticky='w', row=2, column=0, padx=8, pady=8)

def change_dropdown_10(*args):
    global var_10
    var_10 = None
    var_10 = var_10_text_choices[var_10_text.get()]
    print('var_10:' + str(var_10))
    chartPlotter()

var_10_text.trace('w', change_dropdown_10)

W_10 = weightChoice(10, 0, tab10, weight_10)

# *****************************************************************************************************************
#               TAB 11
# *****************************************************************************************************************
content_11 = tab(tab11, text1='Toxic reagents should be eliminated or replaced.',
                      text2='Does the method involve the use of toxic reagents?')

var_11a_text = StringVar(tab11)
# Dictionary with options
var_11a_text_choices = {'No': 1.0,
                        'Yes': 0.0}
var_11a_text.set('Select')

dropDown_11a = OptionMenu(tab11, var_11a_text, *var_11a_text_choices.keys())
dropDown_11a.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w')
dropDown_11a.grid(sticky='w', row=2, column=0, padx=8, pady=8)

def enabler_11b():
    if float(var_11a_text_choices[var_11a_text.get()]) == 0.0:
        return 'enabled'
    else:
        return 'disabled'

amount_var11b = StringVar(tab11)
amount_var11b.set(0.0)

def change_dropdown_11a(*args):

    global var_11
    var_11 = 1.0
    var_11 = var_11a_text_choices[var_11a_text.get()]
    Label(tab11, text='Amount of toxic reagents in g or mL:', wraplength=300, justify=LEFT).grid(sticky='w', row=3, column=0, padx=8, pady=8)
    reagent_entry_11 = ttk.Entry(tab11, textvariable=amount_var11b, width=15, state=enabler_11b())
    reagent_entry_11.grid(sticky='w', row=4, column=0, padx=8, pady=8)

    reagent_entry_11.bind('<Return>', change_dropdown_11a)
    ttk.Button(tab11, text='Set', command=change_dropdown_11a).grid(row=5, column=0, padx=8, pady=8)

    if float(var_11a_text_choices[var_11a_text.get()]) != 1.0:
        try:

            if float(amount_var11b.get()) < 0.1:
                var_11 = 0.8
            elif float(amount_var11b.get()) > 50.0:
                var_11 = 0.0
            else:
                var_11 = abs(-0.129 * log(float(amount_var11b.get())) + 0.5012)  # absolute value to avoid negative

        except ValueError:
            tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an intiger, e.g. 0.14 or 21.')
    else:
        pass

    chartPlotter()
    print(var_11)

var_11a_text.trace('w', change_dropdown_11a)

W_11 = weightChoice(10, 0, tab11, weight_11)

# *****************************************************************************************************************
#               TAB 12
# *****************************************************************************************************************
content_12 = tab(tab12, text1='Operator\'s safety should be increased.',
                      text2='Select the threats which are not avoided:')

varA = IntVar()
varB = IntVar()
varC = IntVar()
varD = IntVar()
varE = IntVar()
varF = IntVar()
varG = IntVar()

ttk.Checkbutton(tab12, text='toxic to aquatic life', variable=varA).grid(row=2, sticky='w', padx=8)
ttk.Checkbutton(tab12, text='bioacumulative', variable=varB).grid(row=3, sticky='w', padx=8)
ttk.Checkbutton(tab12, text='persistent', variable=varC).grid(row=4, sticky='w', padx=8)
ttk.Checkbutton(tab12, text='highly flammable', variable=varD).grid(row=5, sticky='w', padx=8)
ttk.Checkbutton(tab12, text='highly oxidizable', variable=varE).grid(row=6, sticky='w', padx=8)
ttk.Checkbutton(tab12, text='exposive', variable=varF).grid(row=7, sticky='w', padx=8)
ttk.Checkbutton(tab12, text='corrosive', variable=varG).grid(row=8, sticky='w', padx=8)

def testPrint():
    # print(varA.get(), varB.get(), varC.get(), varD.get(), varE.get(), varF.get(), varG.get())
    global var_12a
    var_12a = (varA.get() + varB.get() + varC.get() + varD.get() + varE.get() + varF.get() + varG.get())
    # print(var_12a)
    global var_12
    if var_12a == 0:
        var_12 = 1.0
    elif var_12a == 1:
        var_12 = 0.8
    elif var_12a == 2:
        var_12 = 0.6
    elif var_12a == 3:
        var_12 = 0.4
    elif var_12a == 4:
        var_12 = 0.2
    else:
        var_12 = 0.0
    print ('var_12: %f' % var_12)

    chartPlotter()

ttk.Button(tab12, text='Set', command=testPrint).grid(row=9, column=0, padx=8, pady=8)

W_12 = weightChoice(10, 0, tab12, weight_12)

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

# pack the tab parent and its tabs:
tab_parent.pack(expand=1, fill='both')

# ttk.Button(leftFrame, text='Print score', command=printScore).pack(side=BOTTOM)

# generate the default chart at the beginning:
chartPlotter()

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

# generate the report in .pdf:
def generateReport():

    # connect a float in range 0.0 : 1.0 to a colour in a spectrum from red to yellow to green (256 discrete colour values):
    def colorMapper(value):
        cmap = LinearSegmentedColormap.from_list('rg', ["red", "yellow", "green"], N=256)
        mapped_color = int(value * 255)
        color = cmap(mapped_color)
        color_255 = []
        for band in color:
            color_255.append(int(band * 255))
        return tuple(color_255)

    pdf = FPDF('P', 'mm', 'A4')
    pdf.set_font('Arial', '', 10)
    pdf.add_page()
    pdf.set_margins(left=30, top=30)

    # save a temp image to the program's location:
    plt.savefig('temp_figure.png', bbox_inches='tight')
    # insert image (image, x, y, width):
    pdf.image('temp_figure.png', 107, 10, 80)
    # delete the temp file from drive:
    os.remove('temp_figure.png')

    # insert title (Arial, 'B'old, 14 pt):
    pdf.set_font('Arial', 'B', 14.0)
    pdf.ln(10)
    pdf.cell(100, 12, 'Green index report sheet')
    pdf.set_font('Arial', '', 12)
    pdf.ln(15)

    now = datetime.now()
    pdf.cell(100, 12, now.strftime("%d/%m/%Y %H:%M:%S"))

    # Text height is the same as current font size
    th = pdf.font_size + 2

    # a function to change the colour of a field based on the value:
    def fieldColor(score):
        x = colorMapper(score)[0]
        y = colorMapper(score)[1]
        z = colorMapper(score)[2]
        pdf.set_fill_color(x, y, z)

    pdf.ln(70)
    # populate the table
    # Table head:
    pdf.set_font('Arial', 'B', 10)
    pdf.cell(120, th, 'Criteria', border=0)
    pdf.cell(15, th, 'Score', border=0)
    pdf.cell(15, th, 'Weight', border=0)
    pdf.set_font('Arial', '', 10)
    pdf.ln(th)

    pdf.set_fill_color(240, 240, 240)

    # Rule 1
    # Save top coordinate
    top = pdf.y
    # Calculate x position of next cell
    offset = pdf.x + 120
    pdf.multi_cell(120, th * 0.8, '1. Direct analytical techniques should be applied to avoid sample treatment.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_1)
    pdf.cell(15, th * 1.6, str(round(var_1, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_1.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 2
    top = pdf.y
    offset = pdf.x + 120
    pdf.cell(120, th * 1.6, '2. Minimal sample size and minimal number of samples are goals.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_2)
    pdf.cell(15, th * 1.6, str(round(var_2, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_2.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 3
    top = pdf.y
    offset = pdf.x + 120
    pdf.cell(120, th * 1.6, '3. If possible, measurements should be performed in situ.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_3)
    pdf.cell(15, th * 1.6, str(round(var_3, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_3.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 4
    top = pdf.y
    offset = pdf.x + 120
    pdf.multi_cell(120, th * 0.8, '4. Integration of analytical processes and operations saves energy and reduces the use of reagents.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_4)
    pdf.cell(15, th * 1.6, str(round(var_4, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_4.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 5
    top = pdf.y
    offset = pdf.x + 120
    pdf.cell(120, th * 1.6, '5. Automated and miniaturized methods should be selected.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_5)
    pdf.cell(15, th * 1.6, str(round(var_5, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_5.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 6
    top = pdf.y
    offset = pdf.x + 120
    pdf.cell(120, th * 1.6, '6. Derivatization should be avoided.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_6)
    pdf.cell(15, th * 1.6, str(round(var_6, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_6.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 7
    top = pdf.y
    offset = pdf.x + 120
    pdf.multi_cell(120, th * 0.8, '7. Generation of a large volume of analytical waste should be avoided, and proper management of analytical waste should be provided.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_7)
    pdf.cell(15, th * 1.6, str(round(var_7, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_7.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 8
    top = pdf.y
    offset = pdf.x + 120
    pdf.multi_cell(120, th * 0.8, '8. Multi-analyte or multi-parameter methods are preferred versus methods using one analyte at a time.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_8)
    pdf.cell(15, th * 1.6, str(round(var_8, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_8.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 9
    top = pdf.y
    offset = pdf.x + 120
    pdf.cell(120, th * 1.6, '9. The use of energy should be minimized.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_9)
    pdf.cell(15, th * 1.6, str(round(var_9, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_9.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 10
    top = pdf.y
    offset = pdf.x + 120
    pdf.cell(120, th * 1.6, '10. Reagents obtained from renewable sources should be preferred.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_10)
    pdf.cell(15, th * 1.6, str(round(var_10, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_10.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 11
    top = pdf.y
    offset = pdf.x + 120
    pdf.cell(120, th *1.6, '11. Toxic reagents should be eliminated or replaced.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_11)
    pdf.cell(15, th * 1.6, str(round(var_11, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_11.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # Rule 12
    top = pdf.y
    offset = pdf.x + 120
    pdf.cell(120, th * 1.6, '12. Operator\'s safety should be increased.', border=1, fill=True)
    # Reset y coordinate
    pdf.y = top
    # Move to computed offset
    pdf.x = offset
    fieldColor(var_12)
    pdf.cell(15, th * 1.6, str(round(var_12, 2)), border=1, fill=True, align='C')
    pdf.set_fill_color(240, 240, 240)
    pdf.cell(15, th * 1.6, str(weight_12.get()), border=1, fill=True, align='C')
    pdf.ln(th * 2)

    # output the pdf:
    def savePDF():
        ftypes = [('PDF file', '.pdf'), ('All files', '*')]
        filename = filedialog.asksaveasfilename(filetypes=ftypes, defaultextension='.pdf')
        # save the pdf
        pdf.output(filename, 'F')

    savePDF()

# add the report functionality to the file menu:
FileMenu.add_command(label='Generate report', command=generateReport)
FileMenu.add_separator()

# add a button to refresh the chart:
refreshButton = ttk.Button(leftFrame, text='RE-GENERATE PLOT', width=20,
                           command=lambda: [printScore(),
                                            clearFrame(rightFrame),
                                            pieChart(),
                                            print_variables()
                                            ])
refreshButton.pack(side=BOTTOM, anchor=SE)

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

##################################################################################################
root.mainloop()  # to keep the window continuously on, otherwise it shall disappear


import serial
import KeyConfig as kc
import struct
import socket

IP = '192.168.1.200'
PORT = 12345

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.setblocking(False)

def read_data():
    d: bytes = ser.readline()

    if len(d) > 0:
        res = d.decode().replace('\r\n', '')
        return res

def send_color_data(color:tuple):
    packet = struct.pack("4f",*color,1)
    s.sendto(packet, (IP,PORT))
    print("send: %s" % packet)

def send_command_data(command:float):
    packet = struct.pack("f",command)
    s.sendto(packet, (IP,PORT))
    print("send: %s" % packet)

if __name__ == '__main__':

    ser = serial.Serial('COM5', 9600)
    while True:
        data: bytes = ser.read_until(expected=b"\r\n")
        data = data.replace(b"\r\n",b'')
        int_data = int.from_bytes(data,byteorder="big")
        print(int_data)

        if len(data) > 0:
            if kc.colors.get(int_data):
                print("ok")
                color_to_send = kc.colors[int_data]
                send_color_data(color_to_send)
            elif kc.commands.get(int_data):
                print("command")
                command_to_send = kc.commands[int_data]
                send_command_data(command_to_send)


import json

class SettingFile(object):
    def __init__(self, path, defaults):
        self._defaults = defaults
        self._path = path
        self._data = dict()
        self._callbacks = dict()
        for setting in defaults:
            self._callbacks[setting] = callback_assist()

        self.load()

    def load(self):
        '''Attempts to load the file'''
        try:
            new_data = json.load(open(self._path))
        except:
            json.dump(
                self._defaults,
                open(self._path, 'w'),
                indent=2,
                sort_keys=True
            )
            new_data = self._defaults.copy()

        for setting in self._defaults:
            if setting in new_data:
                self[setting] = new_data[setting]
            else:
                self[setting] = self._defaults[setting]

    def save(self):
        json.dump(
            self._data,
            open(self._path, 'w'),
            indent=2,
            sort_keys=True
        )

    def register_callback(self, setting_name, funct, args=None):
        self._callbacks[setting_name].add_callback(funct, args)

    def __getitem__(self, *args):
        return self._data.get(*args)

    def __setitem__(self, key, val):
        if key not in self._defaults:
            raise ValueError('Unknown Setting')
        else:
            self._data[key] = val
            self._callbacks[key].fire()
            self.save()

    def __str__(self):
        return "Settings in {}: {}".format(self._path, self._data)

    def __repr__(self):
        return "setting_file({}, {})".format(self._path, self._defaults)

class callback_assist(object):
    '''This class represents a function with arguments'''
    def __init__(self):
        self.callbacks = list()

    def add_callback(self, funct, args=None):
        '''Adds a new callback'''
        if args == None:
            args = list()
        self.callbacks.append((funct, args))

    def fire(self):
        '''Fires all the callbacks'''
        for funct, args in self.callbacks:
            funct(*args)


import numpy as np
from scipy import signal
from scipy.signal import iirfilter
from scipy.signal import lfilter

def Implement_Notch_Filter(fs: float, band: list, freq: float, ripple: float, order: int, filter_type: str, data: np.array):
    r"""

    Args:
        fs: frequency sampling
        band: the bandwidth around the center-line frequency that you wish to filter
        freq: the center - line frequency to be filtered
        ripple: the maximum pass-band ripple that is allowed in db
        order: the filter order.  For FIR notch filters this is best set to 2 or 3,
         IIR filters are best suited for high values of order.
        filter_type: the type of the filter; 'butter', 'bessel', 'cheby1', 'cheby2', 'ellip'
        data: the data to be filtered

    Returns:
        np.array: the filtered data
    """
    nyq = fs/2.0
    low = freq - band/2.0
    high = freq + band/2.0
    low = low/nyq
    high = high/nyq
    b, a = iirfilter(order, [low, high], rp=ripple, btype='bandstop', analog=False, ftype=filter_type)
    filtered_data = lfilter(b, a, data)
    return filtered_data

def iirnotch_filter(fs, notch_frequency, quality_factor, data):
    r"""

    Args:
        fs: frequency sampling, in Hz
        notch_freq: the center-line frequency to be filtered
        quality_factor: the quality factor. see details in scipy.signal.iirnotch()
        data: signal the filtered will be applied

    Returns:
        np.array: signal after applying the filter
    """

    b_notch, a_notch = signal.iirnotch(notch_frequency, quality_factor, fs)

    # apply notch filter to signal
    data_notched = signal.filtfilt(b_notch, a_notch, data)

    return data_notched

# freq, h = signal.freqz(b_notch, a_notch, fs = srate)
# plt.figure('filter')
# plt.plot( freq, 20*np.log10(abs(h)))
### Example
# import matplotlib.pyplot as plt
# import numpy as np
# from scipy import signal
# # Create/view notch filter
# samp_freq = 1000  # Sample frequency (Hz)
# notch_freq = 60.0  # Frequency to be removed from signal (Hz)
# quality_factor = 30.0  # Quality factor
# b_notch, a_notch = signal.iirnotch(notch_freq, quality_factor, samp_freq)
# freq, h = signal.freqz(b_notch, a_notch, fs = samp_freq)
# plt.figure('filter')
# plt.plot( freq, 20*np.log10(abs(h)))
#
# # Create/view signal that is a mixture of two frequencies
# f1 = 17
# f2 = 60
# t = np.linspace(0.0, 1, 1_000)
# y_pure = np.sin(f1 * 2.0*np.pi*t) + np.sin(f2 * 2.0*np.pi*t)
# plt.figure('result')
# plt.subplot(211)
# plt.plot(t, y_pure, color = 'r')
#
# # apply notch filter to signal
# y_notched = signal.filtfilt(b_notch, a_notch, y_pure)
#
# # plot notch-filtered version of signal
# plt.subplot(212)
# plt.plot(t, y_notched, color = 'r')


import gymnasium as gym
from IPython import display
import matplotlib.pyplot as plt
from utils.visualize import visualize_policy, visualize_q, visualize_model, visualize_v

class JupyterRender(gym.Wrapper):
    def __init__(self, env):
        super().__init__(env)
        self.env = env

    def render(self, title='Environment', v=None, q=None, policy=None, model_r=None, model_ns=None):
        viz_list = {}
        if v is not None:
            viz_list['v'] = v
        if q is not None:
            viz_list['q'] = q
        if policy is not None:
            viz_list['policy'] = policy
        if model_r is not None:
            viz_list['model_r'] = model_r
        if model_ns is not None:
            viz_list['model_ns'] = model_ns

        fig = plt.figure(figsize=(8, 8))

        ax_list = [fig.add_subplot(2, 2, 1)]
        img = ax_list[0].imshow(self.env.render())  # prepare to render the environment by using matplotlib and ipython display
        ax_list[0].set_title(title)

        pos = 2
        for i in range(pos, 2 + len(viz_list)):
            ax_list.append(fig.add_subplot(2, 2, i))

        ax_index = 1
        for key, value in viz_list.items():
            if key == 'policy':
                visualize_policy(value, ax_list[ax_index], self.env.nrow, self.env.ncol)
            elif key == 'v':
                visualize_v(value, ax_list[ax_index], self.env.nrow, self.env.ncol)
            elif key == 'q':
                visualize_q(value, ax_list[ax_index], self.env.nrow, self.env.ncol)
            else:
                if key == 'model_r':
                    title = 'Reward Model'
                elif key == 'model_ns':
                    title = 'Next State Model'
                visualize_model(value, ax_list[ax_index], self.env.nrow, self.env.ncol, title)

            ax_index += 1

        for ax in ax_list:
            ax.tick_params(bottom=False, left=False, labelbottom=False, labelleft=False)

        display.display(plt.gcf())
        display.clear_output(wait=True)
        plt.close()

if __name__ == '__main__':
    env = gym.make("FrozenLake-v1", render_mode='rgb_array', is_slippery=False)  # define the environment.
    env = JupyterRender(env)


from typing import List

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if root is None:
            return []

        vals = []
        current_vals = []
        current_queue = [root]
        next_queue = []
        while current_queue or next_queue:
            if not current_queue:
                current_queue = [*next_queue]
                next_queue = []
                vals.append(current_vals)
                current_vals = []

            current = current_queue.pop(0)
            current_vals.append(current.val)
            if current.left:
                next_queue.append(current.left)
            if current.right:
                next_queue.append(current.right)
        vals.append(current_vals)

        return vals

if __name__ == "__main__":
    from utils.binary_tree import from_nums
    nums = [3, 9, 20, None, None, 15, 7]
    root = from_nums(nums)
    vals = Solution().levelOrder(root)
    print(vals)