# This is where the optimization is done.
import tkinter as tk
from _tkinter import TclError
from tkinter.ttk import Progressbar
from tkinter import messagebox
import pickle
from tkinter.filedialog import askopenfilenames
from multiprocessing import cpu_count
from tkinter import filedialog
from matplotlib import pyplot as plt

try:
    import any_files.main_application
    import any_files.optimize as op
    import any_files.example_data as test
    from any_files.calc_structure import *
    import any_files.calc_structure
    from any_files.helper import *
except ModuleNotFoundError:
    import ANYstructure.any_files.main_application
    import ANYstructure.any_files.optimize as op
    import ANYstructure.any_files.example_data as test
    from ANYstructure.any_files.calc_structure import *
    import ANYstructure.any_files.calc_structure
    from ANYstructure.any_files.helper import *


class CreateOptGeoWindow():
    '''
    This class initiates the MultiOpt window.
    '''

    def __init__(self, master, app=None):
        super(CreateOptGeoWindow, self).__init__()
        if __name__ == '__main__':
            self._load_objects = {}
            self._load_comb_dict = {}
            self._line_dict = test.get_line_dict()
            self._load_count = 0
            self._point_dict = test.get_point_dict()
            self._canvas_scale = 20


            self._line_to_struc = test.get_line_to_struc()

            self._opt_frames = {}
            self._active_points = ['point1','point4','point8','point5']
            self._ML_buckling = dict()  # Buckling machine learning algorithm
            for name, file_base in zip(['cl SP buc int predictor', 'cl SP buc int scaler',
                                        'cl SP ult int predictor', 'cl SP ult int scaler',
                                        'cl SP buc GLGT predictor', 'cl SP buc GLGT scaler',
                                        'cl SP ult GLGT predictor', 'cl SP ult GLGT scaler',
                                        'cl UP buc int predictor', 'cl UP buc int scaler',
                                        'cl UP ult int predictor', 'cl UP ult int scaler',
                                        'cl UP buc GLGT predictor', 'cl UP buc GLGT scaler',
                                        'cl UP ult GLGT predictor', 'cl UP ult GLGT scaler',
                                        'CSR predictor', 'CSR scaler'
                                        ],
                                       ["ml_files\\CL_output_cl_buc_predictor_In-plane_support_cl_1_SP",
                                        "ml_files\\CL_output_cl_buc_scaler_In-plane_support_cl_1_SP",
                                        "ml_files\\CL_output_cl_ult_predictor_In-plane_support_cl_1_SP",
                                        "ml_files\\CL_output_cl_ult_scaler_In-plane_support_cl_1_SP",
                                        "ml_files\\CL_output_cl_buc_predictor_In-plane_support_cl_2,_3_SP",
                                        "ml_files\\CL_output_cl_buc_scaler_In-plane_support_cl_2,_3_SP",
                                        "ml_files\\CL_output_cl_ult_predictor_In-plane_support_cl_2,_3_SP",
                                        "ml_files\\CL_output_cl_ult_scaler_In-plane_support_cl_2,_3_SP",
                                        "ml_files\\CL_output_cl_buc_predictor_In-plane_support_cl_1_UP",
                                        "ml_files\\CL_output_cl_buc_scaler_In-plane_support_cl_1_UP",
                                        "ml_files\\CL_output_cl_ult_predictor_In-plane_support_cl_1_UP",
                                        "ml_files\\CL_output_cl_ult_scaler_In-plane_support_cl_1_UP",
                                        "ml_files\\CL_output_cl_buc_predictor_In-plane_support_cl_2,_3_UP",
                                        "ml_files\\CL_output_cl_buc_scaler_In-plane_support_cl_2,_3_UP",
                                        "ml_files\\CL_output_cl_ult_predictor_In-plane_support_cl_2,_3_UP",
                                        "ml_files\\CL_output_cl_ult_scaler_In-plane_support_cl_2,_3_UP",
                                        "CL_CSR-Tank_req_cl_predictor",
                                        "CL_CSR-Tank_req_cl_UP_scaler",
                                        "CL_CSR_plate_cl,_CSR_web_cl,_CSR_web_flange_cl,_CSR_flange_cl_predictor",
                                        "CL_CSR_plate_cl,_CSR_web_cl,_CSR_web_flange_cl,_CSR_flange_cl_SP_scaler"]):
                    self._ML_buckling[name] = None
                    if os.path.isfile(file_base + '.pickle'):
                        file = open(file_base + '.pickle', 'rb')
                        self._ML_buckling[name] = pickle.load(file)
                        file.close()
        else:
            self.app = app
            self._load_objects = app._load_dict
            self._load_comb_dict = app._new_load_comb_dict
            self._line_dict = app._line_dict
            self._load_count = 0
            self._point_dict = app._point_dict
            self._canvas_scale = app._canvas_scale
            self._line_to_struc = app._line_to_struc
            self._opt_frames = {}
            self._active_points = []
            self._root_dir = app._root_dir
            self._ML_buckling = app._ML_buckling


        self._opt_structure = {}
        self._opt_frames_obj = []
        self._frame = master
        self._frame.wm_title("Optimize structure")
        self._frame.geometry('1800x950')
        self._frame.grab_set()
        self._canvas_origo = (50, 720 - 50)
        
        self._canvas_base_origo = self._canvas_origo
        self._canvas_draw_origo = list(self._canvas_base_origo)
        self._previous_drag_mouse = list(self._canvas_draw_origo)

        self._active_lines = []
        self._add_to_lines = True
        self._lines_add_to_load = []
        self._active_point = None
        self._point_is_active = False

        # ----------------------------------COPIED FROM OPTIMIZE_WINDOW----------------------------------------------- #

        self._opt_resutls = {}
        self._geo_results = None
        self._opt_actual_running_time = tk.Label(self._frame, text='')

        tk.Frame(self._frame, width=770, height=5, bg="grey", colormap="new").place(x=20, y=95)
        tk.Frame(self._frame, width=770, height=5, bg="grey", colormap="new").place(x=20, y=135)

        algorithms = ('anysmart',' ')

        tk.Label(self._frame, text='-- Plate field span optimizer for plate fields separated by frames. --',
                 font='Verdana 15 bold').place(x=10, y=10)


        # upper and lower bounds for optimization
        # [0.6, 0.012, 0.3, 0.01, 0.1, 0.01]
        self._new_spacing_upper = tk.DoubleVar()
        self._new_spacing_lower = tk.DoubleVar()
        self._new_pl_thk_upper = tk.DoubleVar()
        self._new_pl_thk_lower = tk.DoubleVar()
        self._new_web_h_upper = tk.DoubleVar()
        self._new_web_h_lower = tk.DoubleVar()
        self._new_web_thk_upper = tk.DoubleVar()
        self._new_web_thk_lower = tk.DoubleVar()
        self._new_fl_w_upper = tk.DoubleVar()
        self._new_fl_w_lower = tk.DoubleVar()
        self._new_fl_thk_upper = tk.DoubleVar()
        self._new_fl_thk_lower = tk.DoubleVar()
        self._new_span = tk.DoubleVar()
        self._new_width_lg = tk.DoubleVar()
        self._new_algorithm = tk.StringVar()
        self._new_algorithm_random_trials = tk.IntVar()
        self._new_delta_spacing = tk.DoubleVar()
        self._new_delta_pl_thk = tk.DoubleVar()
        self._new_delta_web_h = tk.DoubleVar()
        self._new_delta_web_thk = tk.DoubleVar()
        self._new_delta_fl_w = tk.DoubleVar()
        self._new_delta_fl_thk = tk.DoubleVar()
        self._new_swarm_size = tk.IntVar()
        self._new_omega = tk.DoubleVar()
        self._new_phip = tk.DoubleVar()
        self._new_phig = tk.DoubleVar()
        self._new_maxiter = tk.IntVar()
        self._new_minstep = tk.DoubleVar()
        self._new_minfunc = tk.DoubleVar()
        self._new_processes = tk.IntVar()
        self._new_opt_girder_thk = tk.DoubleVar()
        self._new_opt_girder_stf_web_h = tk.DoubleVar()
        self._new_opt_girder_stf_web_thk = tk.DoubleVar()
        self._new_opt_girder_stf_flange_b = tk.DoubleVar()
        self._new_opt_girder_stf_flange_thk = tk.DoubleVar()
        self._new_opt_girder_scale_high = tk.DoubleVar()
        self._new_opt_girder_scale_low = tk.DoubleVar()
        self._new_opt_span_max = tk.DoubleVar()
        self._new_opt_span_min = tk.DoubleVar()
        self._new_option_fraction = tk.IntVar()
        self._new_option_panel = tk.IntVar()


        ent_w = 10
        self._ent_spacing_upper = tk.Entry(self._frame, textvariable=self._new_spacing_upper, width=ent_w)
        self._ent_spacing_lower = tk.Entry(self._frame, textvariable=self._new_spacing_lower, width=ent_w)
        self._ent_pl_thk_upper = tk.Entry(self._frame, textvariable=self._new_pl_thk_upper, width=ent_w)
        self._ent_pl_thk_lower = tk.Entry(self._frame, textvariable=self._new_pl_thk_lower, width=ent_w)
        self._ent_web_h_upper = tk.Entry(self._frame, textvariable=self._new_web_h_upper, width=ent_w)
        self._ent_web_h_lower = tk.Entry(self._frame, textvariable=self._new_web_h_lower, width=ent_w)
        self._ent_web_thk_upper = tk.Entry(self._frame, textvariable=self._new_web_thk_upper, width=ent_w)
        self._ent_web_thk_lower = tk.Entry(self._frame, textvariable=self._new_web_thk_lower, width=ent_w)
        self._ent_fl_w_upper = tk.Entry(self._frame, textvariable=self._new_fl_w_upper, width=ent_w)
        self._ent_fl_w_lower = tk.Entry(self._frame, textvariable=self._new_fl_w_lower, width=ent_w)
        self._ent_fl_thk_upper = tk.Entry(self._frame, textvariable=self._new_fl_thk_upper, width=ent_w)
        self._ent_fl_thk_lower = tk.Entry(self._frame, textvariable=self._new_fl_thk_lower, width=ent_w)
        self._ent_span = tk.Entry(self._frame, textvariable=self._new_span, width=ent_w)
        self._ent_width_lg = tk.Entry(self._frame, textvariable=self._new_width_lg, width=ent_w)
        self._ent_algorithm = tk.OptionMenu(self._frame, self._new_algorithm, command=self.selected_algorithm,
                                            *algorithms)
        self._ent_random_trials = tk.Entry(self._frame, textvariable=self._new_algorithm_random_trials)
        self._ent_delta_spacing = tk.Entry(self._frame, textvariable=self._new_delta_spacing, width=ent_w)
        self._ent_delta_pl_thk = tk.Entry(self._frame, textvariable=self._new_delta_pl_thk, width=ent_w)
        self._ent_delta_web_h = tk.Entry(self._frame, textvariable=self._new_delta_web_h, width=ent_w)
        self._ent_delta_web_thk = tk.Entry(self._frame, textvariable=self._new_delta_web_thk, width=ent_w)
        self._ent_delta_fl_w = tk.Entry(self._frame, textvariable=self._new_delta_fl_w, width=ent_w)
        self._ent_delta_fl_thk = tk.Entry(self._frame, textvariable=self._new_delta_fl_thk, width=ent_w)

        pso_width = 10
        self._ent_swarm_size = tk.Entry(self._frame, textvariable=self._new_swarm_size, width=pso_width)
        self._ent_omega = tk.Entry(self._frame, textvariable=self._new_omega, width=pso_width)
        self._ent_phip = tk.Entry(self._frame, textvariable=self._new_phip, width=pso_width)
        self._ent_phig = tk.Entry(self._frame, textvariable=self._new_phig, width=pso_width)
        self._ent_maxiter = tk.Entry(self._frame, textvariable=self._new_maxiter, width=pso_width)
        self._ent_minstep = tk.Entry(self._frame, textvariable=self._new_minstep, width=pso_width)
        self._ent_minfunc = tk.Entry(self._frame, textvariable=self._new_minfunc, width=pso_width)

        self._ent_opt_girder_thk = tk.Entry(self._frame, textvariable=self._new_opt_girder_thk, width=ent_w)
        self._ent_opt_girder_stf_web_h = tk.Entry(self._frame, textvariable=self._new_opt_girder_stf_web_h,
                                                  width=ent_w)
        self._ent_opt_girder_stf_web_thk = tk.Entry(self._frame, textvariable=self._new_opt_girder_stf_web_thk,
                                                    width=ent_w)
        self._ent_opt_girder_stf_fl_b = tk.Entry(self._frame, textvariable=self._new_opt_girder_stf_flange_b,
                                                 width=ent_w)
        self._ent_opt_girder_stf_fl_thk = tk.Entry(self._frame, textvariable=self._new_opt_girder_stf_flange_thk,
                                                   width=ent_w)

        self._ent_opt_girder_scale_high = tk.Entry(self._frame, textvariable=self._new_opt_girder_scale_high,
                                                   width=int(ent_w/2))
        self._ent_opt_girder_scale_low = tk.Entry(self._frame, textvariable=self._new_opt_girder_scale_low,
                                                   width=int(ent_w/2))

        self._ent_opt_max_span = tk.Entry(self._frame, textvariable=self._new_opt_span_max,
                                                   width=int(ent_w/2))
        self._ent_opt_min_span = tk.Entry(self._frame, textvariable=self._new_opt_span_min,
                                                   width=int(ent_w/2))


        start_x, start_y, dx, dy = 20, 70, 100, 40

        tk.Label(self._frame, text='Processes\n (CPUs)', font='Verdana 9 bold', bg = 'silver')\
            .place(x=start_x + 8 * dx, y=start_y + 0.5 * dy)
        tk.Entry(self._frame, textvariable=self._new_processes, width = 12, bg = 'silver')\
            .place(x=start_x + 8 * dx, y=start_y + 1.4 * dy)

        self._prop_canvas_dim = (500, 450)
        self._draw_scale = 500
        self._canvas_opt = tk.Canvas(self._frame, width=self._prop_canvas_dim[0], height=self._prop_canvas_dim[1],
                                     background='azure', relief='groove', borderwidth=2)
        self._canvas_opt.place(x=start_x + 10.5 * dx, y=start_y + 3.5 * dy)
        self._select_canvas_dim = (1000, 720)
        self._canvas_select = tk.Canvas(self._frame, width=self._select_canvas_dim[0],
                                        height=self._select_canvas_dim[1],
                                        background='azure', relief='groove', borderwidth=2)
        self._canvas_select.place(x=start_x + 0 * dx, y=start_y + 3.5 * dy)

        # Labels for the pso
        self._lb_swarm_size = tk.Label(self._frame, text='swarm size')
        self._lb_omega = tk.Label(self._frame, text='omega')
        self._lb_phip = tk.Label(self._frame, text='phip')
        self._lb_phig = tk.Label(self._frame, text='phig')
        self._lb_maxiter = tk.Label(self._frame, text='maxiter')
        self._lb_minstep = tk.Label(self._frame, text='minstep')
        self._lb_minfunc = tk.Label(self._frame, text='minfunc')

        tk.Label(self._frame, text='Upper bounds [mm]', font='Verdana 9').place(x=start_x, y=start_y)
        tk.Label(self._frame, text='Iteration delta [mm]', font='Verdana 9').place(x=start_x, y=start_y + dy)
        tk.Label(self._frame, text='Lower bounds [mm]', font='Verdana 9').place(x=start_x, y=start_y + 2 * dy)
        tk.Label(self._frame, text='Spacing [mm]', font='Verdana 7 bold').place(x=start_x + 1.97 * dx,
                                                                                y=start_y - 0.6 * dy)
        tk.Label(self._frame, text='Plate thk. [mm]', font='Verdana 7 bold').place(x=start_x + 2.97 * dx,
                                                                                   y=start_y - 0.6 * dy)
        tk.Label(self._frame, text='Web height [mm]', font='Verdana 7 bold').place(x=start_x + 3.97 * dx,
                                                                                   y=start_y - 0.6 * dy)
        tk.Label(self._frame, text='Web thk. [mm]', font='Verdana 7 bold').place(x=start_x + 4.97 * dx,
                                                                                 y=start_y - 0.6 * dy)
        tk.Label(self._frame, text='Flange width [mm]', font='Verdana 7 bold').place(x=start_x + 5.97 * dx,
                                                                                     y=start_y - 0.6 * dy)
        tk.Label(self._frame, text='Flange thk. [mm]', font='Verdana 7 bold').place(x=start_x + 6.97 * dx,
                                                                                    y=start_y - 0.6 * dy)
        tk.Label(self._frame, text='Estimated running time for algorithm not calculated.',
                 font='Verdana 9 bold').place(x=start_x, y=start_y + 2.8 * dy)
        tk.Label(self._frame, text='- Harmonize stiffener spacing for section.', font='Verdana 9 bold')\
            .place(x=start_x + 5*dx, y=start_y + 2.8 * dy)
        # self._runnig_time_label = tk.Label(self._frame, text='', font='Verdana 9 bold')
        # self._runnig_time_label.place(x=start_x + 2.7 * dx, y=start_y + 2.8 * dy)
        # tk.Label(self._frame, text='seconds ', font='Verdana 9 bold').place(x=start_x + 3.3 * dx, y=start_y + 2.8 * dy)
        self._result_label = tk.Label(self._frame, text='', font='Verdana 9 bold')
        self._result_label.place(x=start_x, y=start_y + 3.4 * dy)

        self._ent_spacing_upper.place(x=start_x + dx * 2, y=start_y)
        self._ent_delta_spacing.place(x=start_x + dx * 2, y=start_y + dy)
        self._ent_spacing_lower.place(x=start_x + dx * 2, y=start_y + 2 * dy)
        self._ent_pl_thk_upper.place(x=start_x + dx * 3, y=start_y)
        self._ent_delta_pl_thk.place(x=start_x + dx * 3, y=start_y + dy)
        self._ent_pl_thk_lower.place(x=start_x + dx * 3, y=start_y + 2 * dy)
        self._ent_web_h_upper.place(x=start_x + dx * 4, y=start_y)
        self._ent_delta_web_h.place(x=start_x + dx * 4, y=start_y + dy)
        self._ent_web_h_lower.place(x=start_x + dx * 4, y=start_y + 2 * dy)
        self._ent_web_thk_upper.place(x=start_x + dx * 5, y=start_y)
        self._ent_delta_web_thk.place(x=start_x + dx * 5, y=start_y + dy)
        self._ent_web_thk_lower.place(x=start_x + dx * 5, y=start_y + 2 * dy)
        self._ent_fl_w_upper.place(x=start_x + dx * 6, y=start_y)
        self._ent_delta_fl_w.place(x=start_x + dx * 6, y=start_y + dy)
        self._ent_fl_w_lower.place(x=start_x + dx * 6, y=start_y + 2 * dy)
        self._ent_fl_thk_upper.place(x=start_x + dx * 7, y=start_y)
        self._ent_delta_fl_thk.place(x=start_x + dx * 7, y=start_y + dy)
        self._ent_fl_thk_lower.place(x=start_x + dx * 7, y=start_y + 2 * dy)

        # setting default values
        init_dim = float(100)  # mm
        init_thk = float(5)  # mm
        self._new_delta_spacing.set(init_dim)
        self._new_delta_pl_thk.set(init_thk)
        self._new_delta_web_h.set(init_dim)
        self._new_delta_web_thk.set(init_thk)
        self._new_delta_fl_w.set(init_dim)
        self._new_delta_fl_thk.set(init_thk)
        self._new_spacing_upper.set(round(800, 5))
        self._new_spacing_lower.set(round(600, 5))
        self._new_pl_thk_upper.set(round(25, 5))
        self._new_pl_thk_lower.set(round(10, 5))
        self._new_web_h_upper.set(round(500, 5))
        self._new_web_h_lower.set(round(300, 5))
        self._new_web_thk_upper.set(round(25, 5))
        self._new_web_thk_lower.set(round(10, 5))
        self._new_fl_w_upper.set(round(250, 5))
        self._new_fl_w_lower.set(round(50, 5))
        self._new_fl_thk_upper.set(round(30, 5))
        self._new_fl_thk_lower.set(round(10, 5))
        self._new_algorithm.set('anysmart')
        self._new_algorithm_random_trials.set(10000)
        self._new_processes.set(max(cpu_count() - 1, 1))
        self._new_opt_girder_thk.set(0.018)
        self._new_opt_girder_stf_web_h.set(0.250)
        self._new_opt_girder_stf_web_thk.set(0.015)
        self._new_opt_girder_stf_flange_b.set(0)
        self._new_opt_girder_stf_flange_thk.set(0)
        self._new_opt_girder_scale_high.set(1.1)
        self._new_opt_girder_scale_low.set(0.9)
        self._new_opt_span_max.set(6)
        self._new_opt_span_min.set(2)

        self._new_swarm_size.set(100)
        self._new_omega.set(0.5)
        self._new_phip.set(0.5)
        self._new_phig.set(0.5)
        self._new_maxiter.set(100)
        self._new_minstep.set(1e-8)
        self._new_minfunc.set(1e-8)

        # self._new_delta_spacing.trace('w', self.update_running_time)
        # self._new_delta_pl_thk.trace('w', self.update_running_time)
        # self._new_delta_web_h.trace('w', self.update_running_time)
        # self._new_delta_web_thk.trace('w', self.update_running_time)
        # self._new_delta_fl_w.trace('w', self.update_running_time)
        # self._new_delta_fl_thk.trace('w', self.update_running_time)
        # self._new_spacing_upper.trace('w', self.update_running_time)
        # self._new_spacing_lower.trace('w', self.update_running_time)
        # self._new_pl_thk_upper.trace('w', self.update_running_time)
        # self._new_pl_thk_lower.trace('w', self.update_running_time)
        # self._new_web_h_upper.trace('w', self.update_running_time)
        # self._new_web_h_lower.trace('w', self.update_running_time)
        # self._new_web_thk_upper.trace('w', self.update_running_time)
        # self._new_web_thk_lower.trace('w', self.update_running_time)
        # self._new_fl_w_upper.trace('w', self.update_running_time)
        # self._new_fl_w_lower.trace('w', self.update_running_time)
        # self._new_fl_thk_upper.trace('w', self.update_running_time)
        # self._new_fl_thk_lower.trace('w', self.update_running_time)
        # self._new_algorithm_random_trials.trace('w', self.update_running_time)
        # self._new_algorithm.trace('w', self.update_running_time)


        self.running_time_per_item = 4e-05
        #self._runnig_time_label.config(text=str(self.get_running_time()))
        self._ent_algorithm.place(x=start_x + dx * 10, y=start_y + dy)
        self.algorithm_random_label = tk.Label(self._frame, text='Number of trials')
        tk.Button(self._frame, text='algorith information', command=self.algorithm_info, bg='white') \
            .place(x=start_x + dx * 10, y=start_y + dy * 2)

        self.run_button = tk.Button(self._frame, text='RUN OPTIMIZATION!', command=self.run_optimizaion, bg='red',
                                    font='Verdana 10', fg='Yellow')
        self.run_button.place(x=start_x + dx * 10, y=start_y)
        self._opt_actual_running_time.place(x=start_x + dx * 8, y=start_y + dy * 1.5)
        # self.close_and_save = tk.Button(self._frame, text='Return and replace with selected optimized structure',
        #                                 command=self.save_and_close, bg='green', font='Verdana 10 bold', fg='yellow')
        # self.close_and_save.place(x=start_x + dx * 10, y=10)


        tk.Button(self._frame, text='Open predefined stiffeners example',
                  command=self.open_example_file, bg='white', font='Verdana 10')\
            .place(x=start_x+dx*10,y=10)

        # Selection of constraints
        self._new_check_sec_mod = tk.BooleanVar()
        self._new_check_min_pl_thk = tk.BooleanVar()
        self._new_check_shear_area = tk.BooleanVar()
        self._new_check_buckling = tk.BooleanVar()
        self._new_check_fatigue = tk.BooleanVar()
        self._new_check_slamming = tk.BooleanVar()
        self._new_check_local_buckling = tk.BooleanVar()
        self._new_harmonize_spacing = tk.BooleanVar()
        self._new_check_buckling_ml_cl = tk.BooleanVar()

        self._new_check_sec_mod.set(True)
        self._new_check_min_pl_thk.set(True)
        self._new_check_shear_area.set(True)
        self._new_check_buckling.set(True)
        self._new_check_fatigue.set(True)
        self._new_check_slamming.set(True)
        self._new_check_local_buckling.set(True)
        self._new_option_fraction.set(None)
        self._new_option_panel.set(None)
        self._new_harmonize_spacing.set(False)
        self._new_check_buckling_ml_cl.set(False)

        self._new_check_buckling_ml_cl.trace('w', self.update_running_time)

        start_y, start_x, dy  = 570, 100, 25
        tk.Label(self._frame,text='Check for minimum section modulus').place(x=start_x+dx*9.7,y=start_y+4*dy)
        tk.Label(self._frame, text='Check for minimum plate thk.').place(x=start_x+dx*9.7,y=start_y+5*dy)
        tk.Label(self._frame, text='Check for minimum shear area').place(x=start_x+dx*9.7,y=start_y+6*dy)
        tk.Label(self._frame, text='Check for buckling (RP-C201)').place(x=start_x+dx*9.7,y=start_y+7*dy)
        tk.Label(self._frame, text='Check for fatigue (RP-C203)').place(x=start_x + dx * 9.7, y=start_y + 8 * dy)
        tk.Label(self._frame, text='Check for bow slamming').place(x=start_x + dx * 9.7, y=start_y + 9 * dy)
        tk.Label(self._frame, text='Check for local stf. buckling').place(x=start_x + dx * 9.7, y=start_y + 10 * dy)
        tk.Label(self._frame, text='Check for buckling, ML-CL').place(x=start_x + dx * 9.7, y=start_y + 11 * dy)

        tk.Label(self._frame, text='Frame (girder data) for weight calculation:', font = 'Verdana 9 bold')\
            .place(x=start_x + dx * 13,
                                                                                       y=start_y + 4 * dy)
        tk.Label(self._frame, text='Girder thickness').place(x=start_x + dx * 13, y=start_y + 5 * dy)
        tk.Label(self._frame, text='Stiffener height').place(x=start_x + dx * 13, y=start_y + 6 * dy)
        tk.Label(self._frame, text='Stiffener thickness').place(x=start_x + dx * 13, y=start_y + 7 * dy)
        tk.Label(self._frame, text='Stf. flange width').place(x=start_x + dx * 13, y=start_y + 8 * dy)
        tk.Label(self._frame, text='Stf. flange thickenss').place(x=start_x + dx * 13, y=start_y + 9 * dy)
        tk.Label(self._frame, text='For weight calculation of girder: Max span mult / Min span mult')\
            .place(x=start_x + dx * 13,y=start_y + 10 * dy)
        tk.Label(self._frame, text='Maximum span / Minimum span ->')\
            .place(x=start_x + dx * 13,y=start_y + 12 * dy)

        self._ent_opt_girder_thk.place(x=start_x + dx * 15, y=start_y + 5 * dy)
        self._ent_opt_girder_stf_web_h.place(x=start_x + dx * 15, y=start_y + 6 * dy)
        self._ent_opt_girder_stf_web_thk.place(x=start_x + dx * 15, y=start_y + 7 * dy)
        self._ent_opt_girder_stf_fl_b.place(x=start_x + dx * 15, y=start_y + 8 * dy)
        self._ent_opt_girder_stf_fl_thk.place(x=start_x + dx * 15, y=start_y + 9 * dy)
        self._ent_opt_girder_scale_high.place(x=start_x + dx * 15, y=start_y + 11 * dy)
        self._ent_opt_girder_scale_low.place(x=start_x + dx * 15.5, y=start_y + 11 * dy)
        self._ent_opt_max_span.place(x=start_x + dx * 15, y=start_y + 12 * dy)
        self._ent_opt_min_span.place(x=start_x + dx * 15.5, y=start_y + 12 * dy)

        tk.Checkbutton(self._frame,variable=self._new_check_sec_mod).place(x=start_x+dx*12,y=start_y+4*dy)
        tk.Checkbutton(self._frame, variable=self._new_check_min_pl_thk).place(x=start_x+dx*12,y=start_y+5*dy)
        tk.Checkbutton(self._frame, variable=self._new_check_shear_area).place(x=start_x+dx*12,y=start_y+6*dy)
        tk.Checkbutton(self._frame, variable=self._new_check_buckling).place(x=start_x+dx*12,y=start_y+7*dy)
        tk.Checkbutton(self._frame, variable=self._new_check_fatigue).place(x=start_x + dx * 12, y=start_y + 8 * dy)
        tk.Checkbutton(self._frame, variable=self._new_check_slamming).place(x=start_x + dx * 12, y=start_y + 9 * dy)
        tk.Checkbutton(self._frame, variable=self._new_check_local_buckling).place(x=start_x + dx * 12,
                                                                                   y=start_y + 10 * dy)
        tk.Checkbutton(self._frame, variable=self._new_check_buckling_ml_cl).place(x=start_x + dx * 12,
                                                                                   y=start_y + 11 * dy)


        tk.Checkbutton(self._frame, variable=self._new_harmonize_spacing).place(x=start_x + 3.9*dx, y=180)

        # Stress scaling
        self._new_fup = tk.DoubleVar()
        self._new_fup.set(0.5)
        self._new_fdwn = tk.DoubleVar()
        self._new_fdwn.set(1)

        tk.Label(self._frame, text='Factor when scaling stresses up, fup')\
            .place(x=start_x + dx * 9.7, y=start_y + 12 * dy)
        ent_fup = tk.Entry(self._frame, textvariable=self._new_fup, width = 5)
        ent_fup.place(x=start_x + dx * 12, y=start_y + 12 * dy)
        tk.Label(self._frame, text='Factor when scaling stresses up, fdown')\
            .place(x=start_x + dx * 9.7, y=start_y + 13 * dy)
        ent_fdwn = tk.Entry(self._frame, textvariable=self._new_fdwn, width = 5)
        ent_fdwn.place(x=start_x + dx * 12, y=start_y + 13 * dy)

        self._toggle_btn = tk.Button(self._frame, text="Iterate predefiened stiffeners", relief="raised",
                                     command=self.toggle, bg = 'salmon')
        self._toggle_btn.place(x=start_x+dx*10.5, y=start_y - dy * 16.8)
        self._toggle_object, self._filez = None, None

        self._options_fractions = (None, )
        self._options_panels = (None, )
        tk.Label(self._frame, text='Select number of panels:').place(x=start_x+dx*12, y=start_y - dy * 20.5)
        tk.Label(self._frame, text='Select panel to plot:   ').place(x=start_x+dx*12, y=start_y - dy * 19.5)
        self._ent_option_fractions = tk.OptionMenu(self._frame, self._new_option_fraction, *self._options_fractions,
                                                   command=self.get_plate_field_options)
        self._ent_option_field = tk.OptionMenu(self._frame, self._new_option_panel, *self._options_panels,
                                               command=self.get_plate_field_options)
        self._option_fractions_place = [start_x+dx*13.5, start_y - dy * 20.5]
        self._options_panels_place = [start_x+dx*13.5, start_y - dy * 19.5]
        self._ent_option_fractions.place(x=self._option_fractions_place[0], y=self._option_fractions_place[1])
        self._ent_option_field.place(x=self._options_panels_place[0], y=self._options_panels_place[1])

        self.run_results = tk.Button(self._frame,text='show calculated', command=self.plot_results, bg='white',
                                    font='Verdana 10',fg='black')
        self.run_results.place(x=start_x+dx*13, y=start_y - dy * 18)

        self.run_results_prev = tk.Button(self._frame,text='Show previous\n'
                                                      'results', command=self.show_previous_results, bg='white',
                                    font='Verdana 10',fg='black')
        self.run_results_prev.place(x=start_x+dx*15, y=start_y - dy * 20)


        # ----------------------------------END OF OPTIMIZE SINGLE COPY-----------------------------------------------
        self.progress_count = tk.IntVar()
        self.progress_count.set(0)
        self.progress_bar = Progressbar(self._frame, orient="horizontal",length=200, mode="determinate",
                                        variable=self.progress_count)
        #self.progress_bar.place(x=start_x+dx*10.5,y=start_y-dy*16.5)

        self._active_lines = []
        self.controls()
        self.draw_select_canvas()
        # if __name__ == '__main__':
        #     self.run_optimizaion(load_pre = True, save_results=True)

    def selected_algorithm(self, event):
        '''
        Action when selecting an algorithm in the optionm menu.
        :return:
        '''
        start_x, start_y, dx, dy = 20, 100, 100, 40
        if self._new_algorithm.get() == 'random' or self._new_algorithm.get() == 'random_no_delta':
            self._ent_random_trials.place_forget()
            self.algorithm_random_label.place_forget()
            self._lb_swarm_size.place_forget()
            self._lb_omega.place_forget()
            self._lb_phip.place_forget()
            self._lb_phig.place_forget()
            self._lb_maxiter.place_forget()
            self._lb_minstep.place_forget()
            self._lb_minfunc.place_forget()
            self._ent_swarm_size.place_forget()
            self._ent_omega.place_forget()
            self._ent_phip.place_forget()
            self._ent_phig.place_forget()
            self._ent_maxiter.place_forget()
            self._ent_minstep.place_forget()
            self._ent_minfunc.place_forget()
            self._ent_random_trials.place(x=start_x + dx * 11.3, y=start_y + 1.2 * dy)
            self.algorithm_random_label.place(x=start_x + dx * 11.3, y=start_y + 0.5 * dy)

        elif self._new_algorithm.get() == 'anysmart' or self._new_algorithm.get() == 'anydetail':
            self._ent_random_trials.place_forget()
            self.algorithm_random_label.place_forget()
            self._lb_swarm_size.place_forget()
            self._lb_omega.place_forget()
            self._lb_phip.place_forget()
            self._lb_phig.place_forget()
            self._lb_maxiter.place_forget()
            self._lb_minstep.place_forget()
            self._lb_minfunc.place_forget()
            self._ent_swarm_size.place_forget()
            self._ent_omega.place_forget()
            self._ent_phip.place_forget()
            self._ent_phig.place_forget()
            self._ent_maxiter.place_forget()
            self._ent_minstep.place_forget()
            self._ent_minfunc.place_forget()

        elif self._new_algorithm.get() == 'pso':
            y_place_label = 11.2
            y_place = 12.2
            self._ent_random_trials.place_forget()
            start_x = 150

            self._lb_swarm_size.place(x=start_x + dx * 11, y=start_y - 1 * dy)
            self._lb_omega.place(x=start_x + dx * 11, y=start_y - 0 * dy)
            self._lb_phip.place(x=start_x + dx * 11, y=start_y + 1 * dy)
            self._lb_phig.place(x=start_x + dx * 11, y=start_y + 2 * dy)

            self._lb_maxiter.place(x=start_x + dx * 14, y=start_y - 1 * dy)
            self._lb_minstep.place(x=start_x + dx * 14, y=start_y + 0 * dy)
            self._lb_minfunc.place(x=start_x + dx * 14, y=start_y + 1 * dy)

            self._ent_swarm_size.place(x=start_x + dx * 12, y=start_y - 1 * dy)
            self._ent_omega.place(x=start_x + dx * 12, y=start_y - 0 * dy)
            self._ent_phip.place(x=start_x + dx * 12, y=start_y + 1 * dy)
            self._ent_phig.place(x=start_x + dx * 12, y=start_y + 2 * dy)

            self._ent_maxiter.place(x=start_x + dx * 15, y=start_y - 1 * dy)
            self._ent_minstep.place(x=start_x + dx * 15, y=start_y + 0 * dy)
            self._ent_minfunc.place(x=start_x + dx * 15, y=start_y + 1 * dy)

    def show_previous_results(self):
        # if type(self._geo_results) is not list():
        #     if self._geo_results is not None:
        #         return
        # else:
        #     if self._geo_results[0] is not None:
        #         return
        self.draw_select_canvas(opt_results=self._geo_results)

    def run_optimizaion(self, load_pre = False, save_results = False, harmonize = False):
        '''
        Function when pressing the optimization botton inside this window.
        :return:
        '''
        frames, distances = self.opt_create_frames(self.opt_get_fractions())
        self.opt_create_main_structure(frames,
                                       self._active_points[0], self._active_points[1],
                                       self._active_points[2], self._active_points[3])

        contraints = (self._new_check_sec_mod.get(), self._new_check_min_pl_thk.get(),
                      self._new_check_shear_area.get(), self._new_check_buckling.get(),
                      self._new_check_fatigue.get(), self._new_check_slamming.get(),
                      self._new_check_local_buckling.get(), False, self._new_check_buckling_ml_cl.get(), False)

        self.pso_parameters = (self._new_swarm_size.get(), self._new_omega.get(), self._new_phip.get(),
                               self._new_phig.get(),self._new_maxiter.get(), self._new_minstep.get(),
                               self._new_minfunc.get())

        opt_girder_prop = (self._new_opt_girder_thk.get(), self._new_opt_girder_stf_web_h.get(),
                           self._new_opt_girder_stf_web_thk.get(), self._new_opt_girder_stf_flange_b.get(),
                           self._new_opt_girder_stf_flange_thk.get(),self._new_opt_girder_scale_high.get(),
                           self._new_opt_girder_scale_low.get())
        min_max_span = (self._new_opt_span_min.get(), self._new_opt_span_max.get())


        init_objects, fatigue_objects, fat_press_ext_int, slamming_pressures, lateral_press, fatigue_objects, \
        slamming_press = [list() for dummy in range(7)]

        broke = False
        pressure_side = 'both sides'  # default value
        for line,coord in self._opt_structure.items():
            if self.opt_create_struc_obj(self._opt_structure[line]) is None:
                broke = True
                break
            else:
                init_objects.append(self.opt_create_struc_obj(self._opt_structure[line])[0])
                fat_obj_single = self.opt_create_struc_obj(self._opt_structure[line])[2]
                fatigue_objects.append(fat_obj_single)


            if __name__ == '__main__':
                import example_data as ex
                lateral_press.append(0.2)  # for testing
                slamming_press.append(0)
                fatigue_objects.append(ex.get_fatigue_object())
                for pressure in ex.get_geo_opt_fat_press():
                    fat_press_ext_int.append(((pressure['p_ext']['loaded'], pressure['p_ext']['ballast'],
                                               pressure['p_ext']['part']),
                                              (pressure['p_int']['loaded'], pressure['p_int']['ballast'],
                                               pressure['p_int']['part'])))
                pressure_side = 'both sides'

            else:
                p1, p2 = self._opt_structure[line]
                # Check if line is horizontal or vertical
                if p2[0] == p1[0]: # Vertical
                    to_find = [p2[0], min(p2[1], p1[1]) + abs((p2[1]-p1[1])*0.5)]
                elif p2[1] == p1[1]: # Horizontal
                    to_find = [min(p2[0], p1[0])+ (p2[0]-p1[0])*0.5, p2[1]]
                else: # Other orientations
                    to_find = [min(p2[0],p1[0])+abs((p2[0]-p1[0])*0.5), min(p2[1]-p1[1])+abs((p2[1]-p1[1])*0.5)]

                # Taking properites from the closest line.
                closet_line = self.opt_find_closest_orig_line(to_find)
                pressure_side = self._line_to_struc[closet_line].overpressure_side
                #print('Closest line', closet_line, p1, p2, to_find)
                gotten_lat_press = self.app.get_highest_pressure(closet_line)
                lateral_press.append(gotten_lat_press['normal'] / 1e6)
                slamming_press.append(gotten_lat_press['slamming'])
                if fat_obj_single is not None:
                    fat_press_single = self.app.get_fatigue_pressures(closet_line, fat_obj_single.get_accelerations())
                    fat_press_tuple = ((fat_press_single['p_ext']['loaded'], fat_press_single['p_ext']['ballast'],
                                        fat_press_single['p_ext']['part']),
                                       (fat_press_single['p_int']['loaded'], fat_press_single['p_int']['ballast'],
                                        fat_press_single['p_int']['part']))
                    fat_press_ext_int.append(fat_press_tuple)
                else:
                    fat_press_ext_int.append(((0, 0, 0), (0, 0,0)))

                # except AttributeError:
                #     print('AttributeError')
                #     fat_press_ext_int.append(None)
        if broke:
            messagebox.showinfo(title='Selection error.',
                                message='This field cannot be subdivided or there are no loads. Error.')
            return None

        if not load_pre:
            min_var = self.get_lower_bounds()
            max_var = self.get_upper_bounds()
            deltas = self.get_deltas()
            spacings = np.arange(min_var[0], max_var[0],
                                 deltas[0])
            resulting_geo = list()


            if self._new_harmonize_spacing.get():
                geo_results = dict()
                for spacing in spacings:
                    this_min_var = copy.deepcopy(min_var)
                    this_min_var[0] = spacing
                    this_max_var = copy.deepcopy(max_var)
                    this_max_var[0] = spacing

                    geo_results = op.run_optmizataion(initial_structure_obj=init_objects,min_var=this_min_var,
                                                      max_var=this_max_var,lateral_pressure=lateral_press,
                                                      deltas=self.get_deltas(), algorithm='anysmart',side=pressure_side,
                                                      const_chk = contraints,pso_options = self.pso_parameters,
                                                      is_geometric=True,fatigue_obj= fatigue_objects,
                                                      fat_press_ext_int=fat_press_ext_int,
                                                      min_max_span=min_max_span, tot_len=self.opt_get_length(),
                                                      frame_height=self.opt_get_distance(), frame_distance = distances,
                                                      predefined_stiffener_iter=self._filez,
                                                      processes = self._new_processes.get(),
                                                      slamming_press=slamming_press, opt_girder_prop=opt_girder_prop,
                                                      fdwn = self._new_fdwn.get(), fup = self._new_fdwn.get(),
                                                      ml_algo=self._ML_buckling)
                    resulting_geo.append(geo_results)

                #need to find the lowest
                for fraction in resulting_geo[0].keys():
                    weight = float('inf')
                    best_idx = None
                    for idx, geo_res in enumerate(resulting_geo):
                        this_sub_fraction_weight = geo_res[fraction][0]
                        if this_sub_fraction_weight < weight:

                            best_idx = idx
                            weight = this_sub_fraction_weight

                    geo_results[fraction] = resulting_geo[best_idx][fraction]

            else:
                geo_results = op.run_optmizataion(initial_structure_obj=init_objects, min_var=self.get_lower_bounds(),
                                                  max_var=self.get_upper_bounds(), lateral_pressure=lateral_press,
                                                  deltas=self.get_deltas(), algorithm='anysmart', side=pressure_side,
                                                  const_chk=contraints, pso_options=self.pso_parameters,
                                                  is_geometric=True, fatigue_obj=fatigue_objects,
                                                  fat_press_ext_int=fat_press_ext_int,
                                                  min_max_span=min_max_span, tot_len=self.opt_get_length(),
                                                  frame_height=self.opt_get_distance(), frame_distance=distances,
                                                  predefined_stiffener_iter=self._filez,
                                                  processes=self._new_processes.get(),
                                                  slamming_press=slamming_press, opt_girder_prop=opt_girder_prop,
                                                  fdwn=self._new_fdwn.get(), fup=self._new_fdwn.get())

            self._geo_results = geo_results

            if len([val*2 for val in self._geo_results.keys()]) != 0:
                self._ent_option_fractions.destroy()
                self._ent_option_fractions = tk.OptionMenu(self._frame, self._new_option_fraction,
                                                           *tuple([val*2 for val in self._geo_results.keys()]),
                                                           command=self.get_plate_field_options)
                self._ent_option_fractions.place(x=self._option_fractions_place[0], y=self._option_fractions_place[1])

            #SAVING RESULTS
            if save_results:
                with open('geo_opt_2.pickle', 'wb') as file:
                    pickle.dump(geo_results, file)
        else:
            with open('geo_opt_2.pickle', 'rb') as file:
                self._geo_results = pickle.load(file)

            self._ent_option_fractions.destroy()
            self._ent_option_fractions = tk.OptionMenu(self._frame, self._new_option_fraction,
                                                       *tuple([val*2 for val in self._geo_results.keys()]),
                                                       command=self.get_plate_field_options)
            self._ent_option_fractions.place(x=self._option_fractions_place[0], y=self._option_fractions_place[1])

        save_file, filename = None, None
        if save_results:
            save_file = filedialog.asksaveasfile(mode="w", defaultextension=".txt", title = 'Save results to file')
            if save_file is None:  # ask saveasfile return `None` if dialog closed with "cancel".
                filename = None
            else:
                filename = save_file.name

        save_file, xplot, yplot = self.draw_result_text(self._geo_results, save_to_file=filename)
        self.draw_select_canvas(opt_results=self._geo_results, save_file = save_file)

        plt.axes(facecolor='lightslategray')
        plt.plot(xplot, yplot,color='yellow', linestyle='solid', marker='o',markerfacecolor='white', markersize=6)
        plt.xlabel('Length of plate fields [m]')
        plt.ylabel('Weight / max weight')
        plt.title('Length of plate fields vs. total weight')
        plt.grid()
        plt.show()

    def opt_get_fractions(self):
        ''' Finding initial number of fractions '''
        init_fractions = 0
        # finding number of fractions
        for dummy_i in range(1, 100):
            if 3.8 < self.opt_get_length() / dummy_i < 4.2:
                init_fractions = dummy_i
                break
        to_return = []
        for dummy_i in range(init_fractions):
            to_return.append(1/init_fractions)

        return to_return

    def opt_create_struc_obj(self,opt_line):
        ''' Creating preliminary stucture object from selected optimized line. 
        The properties of the new line oto be optimized is taken from the closest original line.'''

        pt1 = opt_line[0]
        pt2 = opt_line[1]

        vector = [pt2[0] - pt1[0], pt2[1] - pt1[1]]
        point = [pt1[0]+vector[0]*0.5, pt1[1]+vector[1]*0.5]
        if self.opt_find_closest_orig_line(point) == None:
            return None
        objects = [copy.deepcopy(x) if x != None else None for x in
                   self._line_to_struc[self.opt_find_closest_orig_line(point)]]
        objects[0].Plate.set_span(dist(pt1,pt2))
        objects[0].Stiffener.set_span(dist(pt1, pt2))

        return objects

    def opt_find_closest_orig_line(self,coord):
        ''' Find the closest original line to the optimized line.
            Used to create initial structure objects. '''

        for key,value in self._line_dict.items():

            pt1 = list(self._point_dict['point'+str(value[0])])
            pt2 = list(self._point_dict['point'+str(value[1])])
            distance = dist(pt2,pt1)
            vector = [pt2[0]-pt1[0],pt2[1]-pt1[1]]
            current = list(self._point_dict['point'+str(value[0])])
            for dummy_i in range(1000):
                delta = distance/1000
                current[0] += (vector[0]/distance) * delta
                current[1] += (vector[1]/distance) * delta
                if dist(coord,current) <= 0.1:
                    if self._line_to_struc[key][0].Plate.get_structure_type() not in ('GENERAL_INTERNAL_NONWT', 'FRAME'):
                        return key
                    else:
                        return None

    def opt_get_distance(self):
        ''' Getting the largest disctance between the two lines to be optimized. '''
        if len(self._active_points) == 4:
            return dist(self._point_dict[self._active_points[0]],self._point_dict[self._active_points[2]])
        else:
            return None

    def opt_get_length(self):
        ''' Getting the length of the lines to be optimized. '''
        if len(self._active_points)==4:
            return dist(self._point_dict[self._active_points[0]],self._point_dict[self._active_points[1]])

    def opt_get_fraction_bounds(self, max_len = 6, min_len = 2):
        ''' Return the fraction bounds(basis upper/lower) to be considered. '''
        return int(self.opt_get_length()/max_len), int(self.opt_get_length()/min_len)

    def opt_create_frames(self,fractions):
        ''' Creating frames between the the two lines to be optimized. '''
        count = 1

        self._opt_frames['opt_frame_start'] = [[self._point_dict[self._active_points[0]][0],
                                                self._point_dict[self._active_points[0]][1]],
                                               [self._point_dict[self._active_points[2]][0],
                                                self._point_dict[self._active_points[2]][1]]]

        self._opt_frames['opt_frame_stop'] = [[self._point_dict[self._active_points[1]][0],
                                               self._point_dict[self._active_points[1]][1]],
                                              [self._point_dict[self._active_points[3]][0],
                                               self._point_dict[self._active_points[3]][1]]]

        start =  0
        for fraction in fractions:
            start += fraction
            if start != 1:
                self._opt_frames['opt_frame'+str(count)] = [[self._point_dict[self._active_points[0]][0] +
                                                             round(self.opt_get_length()*start,5),
                                                             self._point_dict[self._active_points[0]][1]],
                                                            [self._point_dict[self._active_points[2]][0] +
                                                             round(self.opt_get_length() * start,5),
                                                             self._point_dict[self._active_points[2]][1]]]
            count+=1
        distances = {'start_dist': dist(self._opt_frames['opt_frame_start'][0], self._opt_frames['opt_frame_start'][1]),
                     'stop_dist': dist(self._opt_frames['opt_frame_stop'][0], self._opt_frames['opt_frame_stop'][1])}

        return self._opt_frames, distances

    def opt_create_main_structure(self,frames,start1,stop1,start2,stop2):
        ''' This creates line definition for the new structure objects.
         The scipt searches the line to find frames.'''
        line1_coord = self._point_dict[start1],self._point_dict[stop1]
        line2_coord = self._point_dict[start2],self._point_dict[stop2]

        structure = {}


        p1_low,p1_high = list(line1_coord[0]),list(line2_coord[0])
        p2_low,p2_high = list(line1_coord[1]),list(line2_coord[1])
        vector_low,vector_high = [p2_low[0]-p1_low[0],p2_low[1]-p1_low[1]],[p2_high[0]-p1_high[0],p2_high[1]-p1_high[1]]

        # Starting search on the lower or inner line
        count = 1
        tmp_struc = [p1_low] # starting point defined.
        found = None
        for frame, coords in frames.items():
            current = list(p1_low)
            if frame!='opt_frame_start' and frame!='opt_frame_stop':
                for jump in range(100):
                    current[0] += vector_low[0] / 100
                    current[1] += vector_low[1] / 100
                    if dist(current,coords[0]) < 0.1 and frame != found:
                        found = frame
                        tmp_struc.append(coords[0])
                        self._opt_structure['opt_struc'+str(count)] = tmp_struc # adding found line
                        tmp_struc = [coords[0]]
                        count += 1
        tmp_struc.append(p2_low)
        self._opt_structure['opt_struc'+str(count)] = tmp_struc # adding found line (end)
        count += 1

        # Starting search of upper or outer line.
        tmp_struc = [p1_high] # starting point defined.
        found = None
        for frame, coords in frames.items():
            current = list(p1_high)
            if frame!='opt_frame_start' and frame!='opt_frame_stop':
                for jump in range(100):
                    current[0] += vector_high[0] / 100
                    current[1] += vector_high[1] / 100
                    if dist(current,coords[1]) < 0.1 and frame != found:
                        found = frame
                        tmp_struc.append(coords[1])

                        self._opt_structure['opt_struc'+str(count)] = tmp_struc # adding found line
                        tmp_struc = [coords[1]]
                        count += 1
        tmp_struc.append(p2_high)
        self._opt_structure['opt_struc'+str(count)] = tmp_struc # adding found line (end)

        return self._opt_structure

    def get_running_time(self):
        '''
        Estimate the running time of the algorithm.
        :return:
        '''
        if self._new_algorithm.get() in ['anysmart', 'anydetail']:
            try:
                number_of_combinations = \
                    max((self._new_spacing_upper.get() - self._new_spacing_lower.get()) / self._new_delta_spacing.get(),
                        1) * \
                    max((self._new_pl_thk_upper.get() - self._new_pl_thk_lower.get()) / self._new_delta_pl_thk.get(),
                        1) * \
                    max((self._new_web_h_upper.get() - self._new_web_h_lower.get()) / self._new_delta_web_h.get(), 1) * \
                    max((self._new_web_thk_upper.get() - self._new_web_thk_lower.get()) / self._new_delta_web_thk.get(),
                        1) * \
                    max((self._new_fl_w_upper.get() - self._new_fl_w_lower.get()) / self._new_delta_fl_w.get(), 1) * \
                    max((self._new_fl_thk_upper.get() - self._new_fl_thk_lower.get()) / self._new_delta_fl_thk.get(), 1)
                return int(number_of_combinations * self.running_time_per_item) * len(self._active_lines)
            except TclError:
                return 0
        else:
            try:
                return int(self._new_algorithm_random_trials.get() * self.running_time_per_item) * len(
                    self._active_lines)
            except TclError:
                return 0

    def get_deltas(self):
        '''
        Return a numpy array of the deltas.
        :return:
        '''
        return np.array([float(self._ent_delta_spacing.get()) / 1000, float(self._new_delta_pl_thk.get()) / 1000,
                         float(self._new_delta_web_h.get()) / 1000, float(self._new_delta_web_thk.get()) / 1000,
                         float(self._new_delta_fl_w.get()) / 1000, float(self._new_delta_fl_thk.get()) / 1000])

    def update_running_time(self, *args):
        '''
        Estimate the running time of the algorithm.
        :return:
        '''

        # try:
        #     self._runnig_time_label.config(text=str(self.get_running_time()))
        # except ZeroDivisionError:
        #     pass  # _tkinter.TclError: pass

        if self._new_check_buckling_ml_cl.get():
            self._new_check_buckling.set(False)
            self._new_check_local_buckling.set(False)

    def get_upper_bounds(self):
        '''
        Return an numpy array of upper bounds.
        :return:
        '''
        return np.array([self._new_spacing_upper.get() / 1000, self._new_pl_thk_upper.get() / 1000,
                         self._new_web_h_upper.get() / 1000, self._new_web_thk_upper.get() / 1000,
                         self._new_fl_w_upper.get() / 1000, self._new_fl_thk_upper.get() / 1000,
                         6, 10])

    def get_lower_bounds(self):
        '''
        Return an numpy array of lower bounds.
        :return:
        '''
        return np.array([self._new_spacing_lower.get() / 1000, self._new_pl_thk_lower.get() / 1000,
                         self._new_web_h_lower.get() / 1000, self._new_web_thk_lower.get() / 1000,
                         self._new_fl_w_lower.get() / 1000, self._new_fl_thk_lower.get() / 1000,
                         1, 10])

    def checkered(self, line_distance):
        '''
        Creates a grid in the properties canvas.
        :param line_distance:
        :return:
        '''
        # vertical lines at an interval of "line_distance" pixel
        for x in range(line_distance, self._prop_canvas_dim[0], line_distance):
            self._canvas_opt.create_line(x, 0, x, self._prop_canvas_dim[0], fill="grey", stipple='gray50')
        # horizontal lines at an interval of "line_distance" pixel
        for y in range(line_distance, self._prop_canvas_dim[1], line_distance):
            self._canvas_opt.create_line(0, y, self._prop_canvas_dim[0], y, fill="grey", stipple='gray50')

    def draw_properties(self, init_obj=None, opt_obj=None, line=None):
        '''
        Drawing properties in the canvas.
        :return:
        '''
        ctr_x = self._prop_canvas_dim[0] / 2
        ctr_y = self._prop_canvas_dim[1] / 2 + 200
        opt_color, opt_stippe = 'red', 'gray12'
        m = self._draw_scale

        if init_obj != None:
            self._canvas_opt.delete('all')
            self.checkered(10)
            init_color, init_stipple = 'blue', 'gray12'

            self._canvas_opt.create_rectangle(0, 0, self._prop_canvas_dim[0] + 10, 80, fill='white')
            self._canvas_opt.create_line(10, 10, 30, 10, fill=init_color, width=5)
            self._canvas_opt.create_text(270, 10, text='Initial    - Pl.: ' + str(init_obj.get_s() * 1000) + 'x' + str(
                init_obj.get_pl_thk() * 1000) +
                                                       ' Stf.: ' + str(init_obj.get_web_h() * 1000) + 'x' + str(
                init_obj.get_web_thk() * 1000) + '+' +
                                                       str(init_obj.get_fl_w() * 1000) + 'x' + str(
                init_obj.get_fl_thk() * 1000),
                                         font='Verdana 8',
                                         fill=init_color)
            self._canvas_opt.create_text(120, 30, text='Weight (per Lg width): ' +
                                                       str(int(op.calc_weight([init_obj.get_s(),
                                                                               init_obj.get_pl_thk(),
                                                                               init_obj.get_web_h(),
                                                                               init_obj.get_web_thk(),
                                                                               init_obj.get_fl_w(),
                                                                               init_obj.get_fl_thk(),
                                                                               init_obj.get_span(),
                                                                               init_obj.get_lg()]))),
                                         font='Verdana 8', fill=init_color)

            self._canvas_opt.create_rectangle(ctr_x - m * init_obj.get_s() / 2, ctr_y, ctr_x + m * init_obj.get_s() / 2,
                                              ctr_y - m * init_obj.get_pl_thk(), fill=init_color, stipple=init_stipple)
            self._canvas_opt.create_rectangle(ctr_x - m * init_obj.get_web_thk() / 2, ctr_y - m * init_obj.get_pl_thk(),
                                              ctr_x + m * init_obj.get_web_thk() / 2,
                                              ctr_y - m * (init_obj.get_web_h() + init_obj.get_pl_thk())
                                              , fill=init_color, stipple=init_stipple)
            if init_obj.get_stiffener_type() not in ['L', 'L-bulb']:
                self._canvas_opt.create_rectangle(ctr_x - m * init_obj.get_fl_w() / 2,
                                                  ctr_y - m * (init_obj.get_pl_thk() + init_obj.get_web_h()),
                                                  ctr_x + m * init_obj.get_fl_w() / 2,
                                                  ctr_y - m * (
                                                  init_obj.get_pl_thk() + init_obj.get_web_h() + init_obj.get_fl_thk()),
                                                  fill=init_color, stipple=init_stipple)
            else:
                self._canvas_opt.create_rectangle(ctr_x - m * init_obj.get_web_thk() / 2,
                                                  ctr_y - m * (init_obj.get_pl_thk() + init_obj.get_web_h()),
                                                  ctr_x + m * init_obj.get_fl_w(),
                                                  ctr_y - m * (
                                                  init_obj.get_pl_thk() + init_obj.get_web_h() + init_obj.get_fl_thk()),
                                                  fill=init_color, stipple=init_stipple)

        if opt_obj != None:
            self._canvas_opt.create_rectangle(ctr_x - m * opt_obj.get_s() / 2, ctr_y,
                                              ctr_x + m * opt_obj.get_s() / 2,
                                              ctr_y - m * opt_obj.get_pl_thk(), fill=opt_color,
                                              stipple=opt_stippe)

            self._canvas_opt.create_rectangle(ctr_x - m * opt_obj.get_web_thk() / 2, ctr_y -
                                              m * opt_obj.get_pl_thk(),
                                              ctr_x + m * opt_obj.get_web_thk() / 2,
                                              ctr_y - m * (
                                                  opt_obj.get_web_h() + opt_obj.get_pl_thk())
                                              , fill=opt_color, stipple=opt_stippe)
            if init_obj.get_stiffener_type() not in ['L', 'L-bulb']:
                self._canvas_opt.create_rectangle(ctr_x - m * opt_obj.get_fl_w() / 2, ctr_y
                                                  - m * (
                                                      opt_obj.get_pl_thk() + opt_obj.get_web_h()),
                                                  ctr_x + m * opt_obj.get_fl_w() / 2, ctr_y -
                                                  m * (
                                                      opt_obj.get_pl_thk() + opt_obj.get_web_h() +
                                                      opt_obj.get_fl_thk()),
                                                  fill=opt_color, stipple=opt_stippe)
            else:
                self._canvas_opt.create_rectangle(ctr_x - m * opt_obj.get_web_thk() / 2, ctr_y
                                                  - m * (
                                                      opt_obj.get_pl_thk() + opt_obj.get_web_h()),
                                                  ctr_x + m * opt_obj.get_fl_w(), ctr_y -
                                                  m * (
                                                      opt_obj.get_pl_thk() + opt_obj.get_web_h() +
                                                      opt_obj.get_fl_thk()),
                                                  fill=opt_color, stipple=opt_stippe)

            self._canvas_opt.create_line(10, 50, 30, 50, fill=opt_color, width=5)
            self._canvas_opt.create_text(270, 50,
                                         text='Optimized - Pl.: ' + str(round(opt_obj.get_s() * 1000,1)) + 'x' +
                                              str(round(opt_obj.get_pl_thk() * 1000,1)) + ' Stf.: '
                                              + str(round(opt_obj.get_web_h() * 1000,1)) +
                                              'x' + str(round(opt_obj.get_web_thk() * 1000,1)) + '+' +
                                              str(round(opt_obj.get_fl_w() * 1000,1)) +
                                              'x' + str(round(opt_obj.get_fl_thk() * 1000,1)),
                                         font='Verdana 8', fill=opt_color)
            self._canvas_opt.create_text(120, 70, text='Weight (per Lg width): '
                                                       + str(int(op.calc_weight([opt_obj.get_s(),
                                                                                 opt_obj.get_pl_thk(),
                                                                                 opt_obj.get_web_h(),
                                                                                 opt_obj.get_web_thk(),
                                                                                 opt_obj.get_fl_w(),
                                                                                 opt_obj.get_fl_thk(),
                                                                                 opt_obj.get_span(),
                                                                                 opt_obj.get_lg()]))),
                                         font='Verdana 8', fill=opt_color)
        else:
            self._canvas_opt.create_text(150, 60, text='No optimized solution found.')

        if line != None:
            if __name__ == '__main__':
                lateral_press = 0.2  # for testing
            else:
                lateral_press = self.app.get_highest_pressure(line)['normal'] /1e6
            self._canvas_opt.create_text(250, self._prop_canvas_dim[1] - 10,
                                         text='Lateral pressure: ' + str(lateral_press) + ' kPa',
                                         font='Verdana 10 bold', fill='red')

    def draw_select_canvas(self, opt_results = None, save_file = None):
        '''
        Making the lines canvas.
        :return:
        '''
        self._canvas_select.delete('all')
        text_type = 'Verdana 8'

        if opt_results is None:
            # stippled lines and text.

            self._canvas_select.create_line(self._canvas_draw_origo[0], 0, self._canvas_draw_origo[0], self._select_canvas_dim[1],
                                            stipple='gray50')
            self._canvas_select.create_line(0, self._canvas_draw_origo[1], self._select_canvas_dim[0], self._canvas_draw_origo[1],
                                            stipple='gray50')
            self._canvas_select.create_text(self._canvas_draw_origo[0] - 30,
                                            self._canvas_draw_origo[1] + 20, text='(0,0)',
                                            font='Text 10')
            self._canvas_select.create_text([700, 50],
                                            text='How to:\n'
                                                 'For a double bottom structure: \n'
                                                 'Click start point 1 -> click en point 1 (for example bottom plate)\n'
                                                 'Click start point 2 -> click en point 2 (for example inner bottom\n'
                                                 'Run optimization! Wait for the results...... wait.... wait....\n',
                                            font='Verdana 8 bold',
                                            fill='red')
            # drawing the line dictionary.
            if len(self._line_dict) != 0:
                for line, value in self._line_dict.items():
                    color = 'black'
                    coord1 = self.get_point_canvas_coord('point' + str(value[0]))
                    coord2 = self.get_point_canvas_coord('point' + str(value[1]))
                    vector = [coord2[0] - coord1[0], coord2[1] - coord1[1]]
                    # drawing a bold line if it is selected
                    if self._line_to_struc[line][0].Plate.get_structure_type() not in ('GENERAL_INTERNAL_NONWT','FRAME'):

                        if line in self._active_lines:
                            self._canvas_select.create_line(coord1, coord2, width=6, fill=color,stipple='gray50')
                            self._canvas_select.create_text(coord1[0] + vector[0] / 2 + 5, coord1[1] + vector[1] / 2 + 10,
                                                            text='Line ' + str(get_num(line)), font='Verdand 10 bold',
                                                            fill='red')
                        else:
                            self._canvas_select.create_line(coord1, coord2, width=3, fill=color,stipple='gray25')
                            self._canvas_select.create_text(coord1[0] - 20 + vector[0] / 2 + 5, coord1[1] + vector[1] / 2 +

                                                            10,text='line' + str(get_num(line)),font="Text 8", fill='black')

                if len(self._opt_frames) != 0:
                    for key,value in self._opt_frames.items():
                        coord1 = self.get_canvas_coord(value[0])
                        coord2 = self.get_canvas_coord(value[1])
                        vector = [coord2[0] - coord1[0], coord2[1] - coord1[1]]
                        self._canvas_select.create_line(coord1, coord2, width=3, fill='SkyBlue1')
                else:
                    pass

            if len(self._active_points)>1:
                color = 'blue'
                coord1 = self.get_point_canvas_coord(self._active_points[0])
                coord2 = self.get_point_canvas_coord(self._active_points[1])
                vector = [coord2[0] - coord1[0], coord2[1] - coord1[1]]
                # drawing a bold line if it is selected
                self._canvas_select.create_line(coord1, coord2, width=6, fill=color)
                if len(self._active_points) > 3:
                    coord1 = self.get_point_canvas_coord(self._active_points[2])
                    coord2 = self.get_point_canvas_coord(self._active_points[3])
                    vector = [coord2[0] - coord1[0], coord2[1] - coord1[1]]
                    self._canvas_select.create_line(coord1, coord2, width=6, fill=color)

                    # self._canvas_select.create_polygon(points, outline='#f11',
                    #                       fill='#1f1', width=2)

            # drawing the point dictionary

            for key,value in self._point_dict.items():
                pt_size = 6
                if key in self._active_points:
                    self._canvas_select.create_oval(self.get_point_canvas_coord(key)[0] - pt_size + 2,
                                                  self.get_point_canvas_coord(key)[1] - pt_size + 2,
                                                  self.get_point_canvas_coord(key)[0] + pt_size + 2,
                                                  self.get_point_canvas_coord(key)[1] + pt_size + 2, fill='blue')
                    if self._active_points.index(key) == 0:
                        self._canvas_select.create_text(self.get_point_canvas_coord(key)[0] - 5,
                                                        self.get_point_canvas_coord(key)[1] - 14, text='START 1',
                                                        font=text_type, fill = 'blue')
                    elif self._active_points.index(key) == 1:
                        self._canvas_select.create_text(self.get_point_canvas_coord(key)[0] - 5,
                                                        self.get_point_canvas_coord(key)[1] - 14,
                                                        text='STOP 1',font=text_type, fill='blue')
                    elif self._active_points.index(key) == 2:
                        self._canvas_select.create_text(self.get_point_canvas_coord(key)[0] - 5,
                                                        self.get_point_canvas_coord(key)[1] - 14,
                                                        text='START 2',font=text_type, fill='blue')
                    elif self._active_points.index(key) == 3:
                        self._canvas_select.create_text(self.get_point_canvas_coord(key)[0] - 5,
                                                        self.get_point_canvas_coord(key)[1] - 14,
                                                        text='STOP 2',font=text_type, fill='blue')
                    else:
                        pass
                else:
                    self._canvas_select.create_oval(self.get_point_canvas_coord(key)[0] - pt_size,
                                                  self.get_point_canvas_coord(key)[1] - pt_size,
                                                  self.get_point_canvas_coord(key)[0] + pt_size,
                                                  self.get_point_canvas_coord(key)[1] + pt_size, fill='red')

                    self._canvas_select.create_text(self.get_point_canvas_coord(key)[0] - 5,
                                                    self.get_point_canvas_coord(key)[1] - 14, text='pt.'+str(get_num(key)),
                                                    font='Verdana 8', fill='blue')
        else:
            self._canvas_select.create_text([20, 20], text='Results are presented here. '
                                                           'All results may not fit the screen. '
                                                           'All results are seen in your saved result file.',
                                            font='Verdana 12 bold',
                                            fill='red', anchor = 'w')

            delta, start_x, y_loc = 20, 10, 40

            for key, values in opt_results.items():
                # if y_loc > 700:
                #     start_x = 400
                #     y_loc = 40

                y_loc = y_loc + delta
                check_ok = [val[2] is True for val in values[1]]

                if save_file is not None:
                    save_file.write('\n')
                    save_file.write('--------------------------------------------------------------------------' + '\n')
                    save_file.write('Plate fields: '+str(len(values[2]['objects']))+ ' Frames: '+
                                    str(len(values[2]['frames'])) + '\n')
                self._canvas_select.create_text([start_x + delta, y_loc],
                                                text=str(len(check_ok))+' panels with weight '+ str(round(values[0],1)),
                                                anchor='w', font=text_type)
                y_loc += delta
                item_count, endstring = 0, ''

                for data_idx, data in enumerate(values[1]):
                    for idx, stuc_info in enumerate(data):
                        if type(stuc_info) == calc_structure.AllStructure:

                            if y_loc > 700:
                                y_loc = 120
                                start_x += 350
                            if item_count == 0:
                                endstring = ' START 1'+' OK!\n' if values[1][data_idx][3] else ' START 1'+' NOT OK!\n'
                            elif item_count > 0 and item_count < len(values[1]) / 2-1 and len(values[1]) != 4:
                                endstring = ' -------'+' OK!\n' if values[1][data_idx][3] else ' -------'+' NOT OK!\n'
                            elif item_count == len(values[1])/2-1:
                                endstring = ' -END 1-'+' OK!\n' if values[1][data_idx][3] else ' -END 1-'+' NOT OK!\n'
                            elif item_count == len(values[1])/2:
                                endstring = ' START 2'+' OK!\n' if values[1][data_idx][3] else ' START 2'+' NOT OK!\n'
                            elif item_count > len(values[1])/2 and item_count < len(values[1])-1:
                                endstring = ' -------'+' OK!\n' if values[1][data_idx][3] else ' -------'+' NOT OK!\n'
                            elif item_count == len(values[1])-1:
                                endstring = ' -END 2-'+' OK!\n' if values[1][data_idx][3] else ' -END 2-'+' NOT OK!\n'
                            self._canvas_select.create_text([start_x + delta, y_loc],
                                                            text=stuc_info.get_one_line_string_mixed()+endstring,
                                                            anchor='w', font=text_type)
                            y_loc += 15

                            if save_file is not None:
                                save_file.write(stuc_info.get_one_line_string_mixed()+' ' +
                                                stuc_info.get_extended_string_mixed() +
                                                ' | ' + stuc_info.Plate.get_report_stresses() +
                                                endstring)
                            item_count += 1

                if save_file is not None:
                    save_file.write('Weight details for this solution:\n')
                    save_file.write('Weight of main structure: ' + str([str(round(val, 1))
                                                                        for val in values[2]['objects']]) + '\n')
                    save_file.write('Weight of frames:         ' + str([str(round(val, 1))
                                                                        for val in values[2]['frames']]) + '\n')
                    save_file.write('Scales used on frames:    ' +
                                    str([str(round(val, 3)) for val in values[2]['scales']]) + '\n')
                    save_file.write('----------------------------------------------------------------------------'+'\n')

            if save_file is not None:
                save_file.write('\n -------------  END  ---------------')
                save_file.close()

    def draw_result_text(self, geo_opt_obj, save_to_file = None):
        ''' Textual version of the results. '''

        self._canvas_opt.delete('all')
        start_x = 20
        delta = 25
        start_y = 60
        y_loc = delta + start_y
        xplot = list()
        yplot = list()

        self._canvas_opt.create_text([start_x, 40],
                                     text='Results seen next. Weight index is tot_weight / max_weight \n'
                                          'max_weight is the highest total weight of the checked variations.\n'
                                          'Weight index of 1 is the heaviest calculated variation.',
                                     font='Verdana 10', fill='Blue', anchor='w')

        self._canvas_opt.create_text([start_x, y_loc],
                                     text='| Plate fields | Fields length | Weight index | All OK? |',
                                     font='Verdana 10 bold', fill='red', anchor = 'w')
        y_loc += delta / 2
        self._canvas_opt.create_text([start_x, y_loc],
                                     text='************************************************', anchor='w',
                                     font='Verdana 10 bold')
        text_type = 'Verdana 10 bold'
        weights = [self._geo_results[key][0] for key in self._geo_results.keys()]

        max_weight = 0
        for weight in weights:
            if weight != float('inf'):
                max_weight = weight if weight > max_weight else max_weight


        if save_to_file is not None:
            save_file = open(save_to_file, 'w')
            save_file.write('| Plate fields | Fields length | Weight index | All OK? |\n')
            save_file.write('*********************************************************\n')

        for key, value in self._geo_results.items():
            y_loc = y_loc + delta

            check_ok = [val[2] is True for val in value[1]]

            self._canvas_opt.create_text([start_x + 20, y_loc ], text=str(len(check_ok)),
                                         anchor='w', font=text_type)

            self._canvas_opt.create_text([start_x + 120, y_loc ], text=str('No results\n' if
                                                                           self._geo_results[key][1][0][0] is None else
                                                                           round(self._geo_results[key][1][0][0].
                                                                                 Plate.get_span(),4)),
                                         anchor='w', font=text_type)
            self._canvas_opt.create_text([start_x + 220, y_loc ],
                                         text=str(round(self._geo_results[key][0] / max_weight, 3))
                                         if max_weight != 0 else '',
                                         anchor='w', font=text_type)

            self._canvas_opt.create_text([start_x + 330, y_loc ], text=str(all(check_ok)),
                                         anchor='w', font=text_type)

            if save_to_file is not None:
                save_file.write(str(len(check_ok))+ ' ' + 'No results\n' if self._geo_results[key][1][0][0] is None
                                                             else str(round(self._geo_results[key][1][0][0].Plate.get_span(),
                                                                            4)) + ' ' +
                                                                  str(round(self._geo_results[key][0] / max_weight, 3))
                                                                  + '\n' if max_weight != 0 else
                '' + ' ' + str(all(check_ok))+'\n')
            if self._geo_results[key][1][0][0] is not None:
                xplot.append(round(self._geo_results[key][1][0][0].Plate.get_span(),4))
                yplot.append(round(self._geo_results[key][0] / max_weight, 4))

        if save_to_file is not None:
            return save_file, xplot, yplot
        else:
            return None, xplot, yplot

    def algorithm_info(self):
        ''' When button is clicked, info is displayed.'''

        messagebox.showinfo(title='Algorith information',
                            message='The algorithms currently included is:\n'
                                    'ANYSMART:  \n'
                                    '           Calculates all alternatives using upper and lower bounds.\n'
                                    '           The step used inside the bounds is defined in deltas.\n\n'
                                    'RANDOM:    \n'
                                    '           Uses the same bounds and deltas as in ANYSMART.\n'
                                    '           Number of combinations calculated is defined in "trials",\n'
                                    '           which selects withing the bounds and deltas defined.\n\n'
                                    'RANDOM_NO_BOUNDS:\n'
                                    '           Same as RANDOM, but does not use the defined deltas.\n'
                                    '           The deltas is set to 1 mm for all dimensions/thicknesses.\n\n'
                                    'ANYDETAIL:\n'
                                    '           Same as for ANYSMART, but will take some more time and\n'
                                    '           provide a chart of weight development during execution.\n\n'
                                    'PSO - Particle Swarm Search:\n'
                                    '           The information can be found on \n'
                                    '           http://pythonhosted.org/pyswarm/ \n'
                                    '           For further information google it!\n'
                                    '           Parameters:\n'
                                    '           swarmsize : The number of particles in the swarm (Default: 100)\n'
                                    '           omega : Particle velocity scaling factor (Default: 0.5)\n'
                                    '           phip : Scaling factor to search away from the particle’s \n'
                                    '                           best known position (Default: 0.5)\n'
                                    '           phig : Scaling factor to search away from the swarm’s best \n'
                                    '                           known position (Default: 0.5)\n'
                                    '           maxiter : The maximum number of iterations for the swarm \n'
                                    '                           to search (Default: 100)\n'
                                    '           minstep : The minimum stepsize of swarm’s best position \n'
                                    '                           before the search terminates (Default: 1e-8)\n'
                                    '           minfunc : The minimum change of swarm’s best objective value\n'
                                    '                           before the search terminates (Default: 1e-8)\n\n'

                                    '\n'
                                    'All algorithms calculates local scantling and buckling requirements')

    def slider_used(self, event):
        '''
        Action when slider is activated.
        :return:
        '''
        self._canvas_scale = self.slider.get()
        self.draw_canvas()

    def on_closing(self):
        '''
        Action when closing the window without saving.
        :return:
        '''
        if __name__ == '__main__':
            self._frame.destroy()
            return

        mess = tk.messagebox.showwarning('Closed without saving', 'Closing will not save loads you have created',
                                         type='okcancel')
        if mess == 'ok':
            self._frame.grab_release()
            self._frame.destroy()
            self.app.on_aborted_load_window()

    def get_point_canvas_coord(self, point_no):
        '''
        Returning the canvas coordinates of the point. This value will change with slider.
        :param point_no:
        :return:
        '''
        point_coord_x = self._canvas_draw_origo[0] + self._point_dict[point_no][0]* self._canvas_scale
        point_coord_y = self._canvas_draw_origo[1] - self._point_dict[point_no][1]* self._canvas_scale

        return [point_coord_x, point_coord_y]

    def get_canvas_coord(self, coord):
        '''
        Returning the canvas coordinates of the point. This value will change with slider.
        :param point_no:
        :return:
        '''
        point_coord_x = self._canvas_draw_origo[0] + coord[0] * self._canvas_scale
        point_coord_y = self._canvas_draw_origo[1] - coord[1] * self._canvas_scale

        return [point_coord_x, point_coord_y]

    def controls(self):
        '''
        Specifying the controls to be used.
        :return:
        '''
        self._canvas_select.bind('<Button-1>', self.button_1_click)
        self._canvas_select.bind('<Button-2>', self.button_2_click)
        self._canvas_select.bind('<Button-3>', self.button_3_click)

        self._frame.bind('<Shift_L>', self.shift_pressed)
        self._frame.bind('<Shift_R>', self.shift_pressed)
        self._frame.bind('<Control_L>', self.ctrl_pressed)
        self._frame.bind('<Control_R>', self.ctrl_pressed)

        self._frame.bind("<MouseWheel>", self.mouse_scroll)
        self._frame.bind("<B2-Motion>", self.button_2_click_and_drag)

    def shift_pressed(self, event=None):
        '''
        Event is executed when shift key pressed.
        :return:
        '''
        self._add_to_lines = True

    def ctrl_pressed(self, event=None):
        '''
        Event when control is pressed.
        :param event:
        :return:
        '''
        self._add_to_lines = False

    def button_1_click(self, event):
        '''
        When clicking the right button, this method is called.
        method is referenced in
        '''
        self._previous_drag_mouse = [event.x, event.y]
        # if type(self._geo_results) is not list():
        #     if self._geo_results is not None:
        #         return
        # else:
        #     if self._geo_results[0] is not None:
        #         return
        click_x = self._canvas_select.winfo_pointerx() - self._canvas_select.winfo_rootx()
        click_y = self._canvas_select.winfo_pointery() - self._canvas_select.winfo_rooty()

        self._point_is_active = False
        margin = 10
        self._active_point = ''
        for point, coords in self._point_dict.items():
            point_coord = self.get_point_canvas_coord(point)
            if point_coord[0]-margin < click_x < point_coord[0]+margin and\
                point_coord[1]-margin < click_y < point_coord[1]+margin:
                self._active_point = point
                self._point_is_active = True
                if len(self._active_points)<4:
                    self._active_points.append(self._active_point)

        if len(self._active_points)==4:
            self.opt_create_frames(self.opt_get_fractions())

        self.draw_select_canvas()

    def button_3_click(self, event):
        '''
        Event when right click.
        :param evnet:
        :return:
        '''
        self._previous_drag_mouse = [event.x, event.y]
        self._active_lines = []
        self._active_points = []
        self.draw_select_canvas()

    def button_2_click(self, event):
        '''
        Event when right click.
        :param evnet:
        :return:
        '''
        self._previous_drag_mouse = [event.x, event.y]

        if self._opt_resutls =={}:
            return
        click_x = self._canvas_select.winfo_pointerx() - self._canvas_select.winfo_rootx()
        click_y = self._canvas_select.winfo_pointery() - self._canvas_select.winfo_rooty()

        if len(self._line_dict) > 0:
            for key, value in self._line_dict.items():

                coord1x = self.get_point_canvas_coord('point' + str(value[0]))[0]
                coord2x = self.get_point_canvas_coord('point' + str(value[1]))[0]
                coord1y = self.get_point_canvas_coord('point' + str(value[0]))[1]
                coord2y = self.get_point_canvas_coord('point' + str(value[1]))[1]

                vector = [coord2x - coord1x, coord2y - coord1y]
                click_x_range = [ix for ix in range(click_x - 10, click_x + 10)]
                click_y_range = [iy for iy in range(click_y - 10, click_y + 10)]
                distance = int(dist([coord1x, coord1y], [coord2x, coord2y]))

                # checking along the line if the click is witnin +- 10 around the click
                for dist_mult in range(1, distance - 1):
                    dist_mult = dist_mult / distance
                    x_check = int(coord1x) + int(round(vector[0] * dist_mult, 0))
                    y_check = int(coord1y) + int(round(vector[1] * dist_mult, 0))
                    if x_check in click_x_range and y_check in click_y_range:
                        self._canvas_select.delete('all')
                        self._active_lines = []
                        self._active_lines.append(key)
                        if key in self._opt_resutls.keys() and self._opt_resutls[key] != None:
                            self.draw_properties(init_obj=self._line_to_struc[key][0],
                                                opt_obj=self._opt_resutls[key][0],
                                                 line=key)
                        else:
                            self.draw_properties(init_obj=self._line_to_struc[key][0], line=key)
                        break
                self.draw_select_canvas()
        self.draw_select_canvas()
        self.update_running_time()

        #############################
        self.opt_create_main_structure(self.opt_create_frames(self.opt_get_fractions())[0],self._active_points[0],
                                       self._active_points[1],self._active_points[2],self._active_points[3])

    def save_and_close(self):
        '''
        Save and close
        :return:
        '''
        if __name__ == '__main__':
            self._frame.destroy()
            return
        try:
            to_return = {}
            for line in self._active_lines:
                to_return[line] = self._opt_resutls[line]
            self.app.on_close_opt_multiple_window(to_return)
            messagebox.showinfo(title='Return info', message='Returning: ' + str(self._active_lines))
        except IndexError:
            messagebox.showinfo(title='Nothing to return', message='No results to return.')
            return
        self._frame.destroy()

    def toggle(self, found_files = None, obj = None, iterating = False, given_path: str = None):
        '''
        On off button.
        :param found_files:
        :param obj:
        :return:
        '''
        # if iterating:
        #     if found_files is not None:
        #         predefined_structure = hlp.helper_read_section_file(files=found_files, obj=obj)
        # else:
        predefined_structure = None
        if self._toggle_btn.config('relief')[-1] == 'sunken':
            self._toggle_btn.config(relief="raised")
            self._toggle_btn.config(bg = 'salmon')
            self._ent_spacing_upper.config(bg = 'white')
            self._ent_spacing_lower.config(bg = 'white')
            self._ent_delta_spacing.config(bg = 'white')
            self._filez = None
        else:
            self._toggle_btn.config(relief="sunken")
            self._toggle_btn.config(bg='lightgreen')
            self._ent_spacing_upper.config(bg = 'lightgreen')
            self._ent_spacing_lower.config(bg = 'lightgreen')
            self._ent_delta_spacing.config(bg = 'lightgreen')
            self._ent_pl_thk_upper.config(bg = 'lightgreen')
            self._ent_pl_thk_lower.config(bg = 'lightgreen')
            self._ent_delta_pl_thk.config(bg = 'lightgreen')
            if given_path is None:
                self._filez = list(askopenfilenames(parent=self._frame, title='Choose files to open',
                                                    initialdir=self._root_dir))
            else:
                self._filez = [given_path]
            if self._filez == []:
                self._toggle_btn.config(relief="raised")
                self._toggle_btn.config(bg='salmon')
                self._ent_spacing_upper.config(bg='white')
                self._ent_spacing_lower.config(bg='white')
                self._ent_delta_spacing.config(bg='white')
                self._ent_pl_thk_upper.config(bg='white')
                self._ent_pl_thk_lower.config(bg='white')
                self._ent_delta_pl_thk.config(bg='white')

        return found_files, predefined_structure

    def open_example_file(self):
        import os
        if os.path.isfile('sections.csv'):
            os.startfile('sections.csv')
        else:
            os.startfile(self._root_dir + '/' + 'sections.csv')

    def plot_results(self):
        'Plotting a selected panel'
        if self._geo_results is not None \
                and type(self._new_option_fraction.get()) == int and type(self._new_option_panel.get()) == int:
            op.plot_optimization_results(self._geo_results[int(self._new_option_fraction.get()/2)][1]
                                         [self._new_option_panel.get()])

    def get_plate_field_options(self, event):

        if self._geo_results is not None:
            self._ent_option_field.destroy()
            to_add = tuple([val for val in range(len(self._geo_results[int(self._new_option_fraction.get()/2)][1]))])
            self._ent_option_field = tk.OptionMenu(self._frame, self._new_option_panel, *to_add)
            self._ent_option_field.place(x=self._options_panels_place[0], y=self._options_panels_place[1])

    def mouse_scroll(self,event):
        self._canvas_scale +=  event.delta/50
        self._canvas_scale = 0 if self._canvas_scale < 0 else self._canvas_scale

        self.draw_select_canvas()

    def button_2_click_and_drag(self,event):

        self._canvas_draw_origo = (self._canvas_draw_origo[0]-(self._previous_drag_mouse[0]-event.x),
                                  self._canvas_draw_origo[1]-(self._previous_drag_mouse[1]-event.y))

        self._previous_drag_mouse = (event.x,event.y)

        self.draw_select_canvas()

if __name__ == '__main__':
    root = tk.Tk()
    my_app = CreateOptGeoWindow(master=root)

    root.mainloop()
