# -*- coding: utf-8 -*-
"""
Created on Thu Mar 14 17:26:27 2024

@author: zilya
"""


import tkinter as tk
import numpy as np
import json
from datetime import datetime
from seqgen_TSE_NIRSII import seqgen_TSE_NIRSII
from types import SimpleNamespace

from utilities.param_constants import param_rf_inv

from utilities.param_constants import param_hardware
from utilities.param_constants import param_rf_SE
from utilities.param_constants import param_default


def calc_lims(): 

    # reading params from GUI
    global param
    param = SimpleNamespace()
    param = param_dj_get(M0_dj)
    
     # reading hardware constants
    hw = param_hardware()

    
    # calculating limits
    lim = SimpleNamespace()
    param.sl_nb = 1
    param.sl_thkn = 5e-3
    param.sl_gap = 100
    param.FoV_p = 256e-3
    param.Nf = 128
    param.Np = 128
    param.BW_pixel = 400
    param.ETL = 16
    param.concats = 1
    param.FA = 90
    param.RA = 180
    param.average = 1
    param.D_scans = 0
    param.HASTE = False
    param.diffusion = False
    param.Dixon = False
    param.PI_factor = 1
    
    rf = param_rf_SE(param.sl_thkn)
    BW_ex_pulse = rf.t_BW_product_ex/rf.t_ex 
    t_exwd = rf.t_ex + 2 *hw.rf_dead_time
    t_refwd = rf.t_ref + 2 * hw.rf_dead_time 
     
    # ---- Вычисление основных площадей и длительностей ------
    param.dG = 250e-6
    A_ex = BW_ex_pulse*(t_exwd+param.dG)/param.sl_thkn
    A_read = param.Nf/param.FoV_f
    A_ph = param.Np/2/param.FoV_p
    A_reph = 0.25*A_ex
    A_pre = 1.5*A_read
    A_crs = 0.75*A_ex
    A_crr = A_read
    A_sps = 4*A_ex
    
    t_reph_min = A_reph/hw.G_amp_max + param.dG
    t_pre_min = A_pre/hw.G_amp_max + param.dG
    t_pre_min = max(t_reph_min,t_pre_min)
    t_pre_min = np.ceil(t_pre_min / hw.grad_raster_time)*hw.grad_raster_time 
      
    t_crs_min = A_crs/hw.G_amp_max + param.dG
    t_crr_min = A_crr/hw.G_amp_max  + param.dG
    t_ph_min = A_ph/hw.G_amp_max  + param.dG
    t_cr_min = max(t_crs_min,t_crr_min,t_ph_min)
    t_cr_min = np.ceil(t_cr_min / hw.grad_raster_time)*hw.grad_raster_time
    
    t_read = 1/param.BW_pixel
    t_read = np.ceil(t_read / hw.grad_raster_time)*hw.grad_raster_time
    param.BW_pixel = 1/t_read
    
    t_sps = A_sps/hw.G_amp_max  + hw.tau_max
    t_sps = np.ceil(t_sps / hw.grad_raster_time)*hw.grad_raster_time  
       
    ES_1 = t_refwd + t_read + 2*t_cr_min 
    ES_2 = t_refwd + t_exwd + 2*t_cr_min + 2*t_pre_min 
    ES = max(ES_1,ES_2) 
    param.ES = np.ceil(ES/ hw.grad_raster_time)*hw.grad_raster_time
    
    t_cr = 0.5*(ES-rf.t_ref-t_read)  
    t_cr = np.ceil(t_cr / hw.grad_raster_time)*hw.grad_raster_time
    
    t_pre = 0.5*(ES-rf.t_ref-t_exwd)  
    t_pre = np.ceil(t_pre / hw.grad_raster_time)*hw.grad_raster_time   
    
    param.TE = param.N_TE*ES

    lim.N_TE_min = 1
    lim.N_TE_max = param.ETL
 
    lim.TR_min = ES*(param.ETL) + t_exwd/2 + t_read/2 + t_sps + param.dG + hw.rf_dead_time      
    lim.TR_max = 10
    lim.TR_min = np.ceil(lim.TR_min/ hw.grad_raster_time)*hw.grad_raster_time
   
    
    lim.Nf_min = 1
    lim.Nf_max = min(param.FoV_f*hw.G_amp_max*(t_read),1600)
    
    lim.Np_min = 1
    Np_max1 = param.Nf
    Np_max2 = 2*param.FoV_p*hw.G_amp_max*(t_cr-param.dG)
    lim.Np_max = min(Np_max1,Np_max2,lim.Nf_max)

    FoV_min = 25e-3
    lim.FoV_f_max = 450e-3
    FoV_f_min = param.Nf/(hw.G_amp_max*t_read)
    lim.FoV_f_min = max(FoV_f_min,FoV_min)
      
    lim.TI_min, lim.TI_max = 0, 0
    param.magn_prep = "off"
        
    if param.IR == True:
            param.magn_prep = "IR"
            rf_inv = param_rf_inv()
            rf = SimpleNamespace(**rf.__dict__, **rf_inv.__dict__)
            A_sp_inv = 7000
            t_sp_inv = A_sp_inv/hw.G_amp_max + param.dG
            t_sp_inv = np.ceil(t_sp_inv/ hw.grad_raster_time)*hw.grad_raster_time 
              
            lim.TR_min = rf.t_inv/2 + param.TI + lim.TR_min  - rf.t_ex/2
            lim.TI_min = rf.t_inv/2 + t_sp_inv + rf.t_ex/2  + 2*param.dG 
            lim.TI_max = param.TR - (rf.t_inv/2  + lim.TR_min  - rf.t_ex/2)
            param.TI_calc_block = param.TI - (rf.t_ex/2 + param.dG)                 
            param.magn_prep_spoiler_area = A_sp_inv
            param.TI_calc_block = np.ceil(param.TI_calc_block/ hw.grad_raster_time)*hw.grad_raster_time
     
    lim.concats_min = np.ceil(param.sl_nb/(np.floor(param.TR/lim.TR_min)))
    lim.concats_max = param.sl_nb
    
    # database of params
    param = SimpleNamespace(**param.__dict__, **hw.__dict__, **rf.__dict__)
    
    # showing limits on GUI
    param_set_lim(M0_dj,lim,param)
    
 
def save():
    
    param_lists = param_dj_get_lists(M0_dj)
    array = []
    for i_FoV in range((param_lists.FoV_f_N)):
        for i_N_TE in range((param_lists.N_TE_N)):
            for i_TR in range((param_lists.TR_N)):
                for i_TI in range((param_lists.TI_N)):
                    param.FoV_f = param_lists.FoV_f_from + (param_lists.FoV_f_to - param_lists.FoV_f_from)/(param_lists.FoV_f_N+1)* (i_FoV+1)
                    param.N_TE = param_lists.N_TE_from + (param_lists.N_TE_to - param_lists.N_TE_from)/(param_lists.N_TE_N+1)* (i_N_TE+1)
                    param.TR = param_lists.TR_from + (param_lists.TR_to - param_lists.TR_from)/(param_lists.TR_N+1)* (i_TR+1)
                    if param.IR == True:
                        param.TI = param_lists.TI_from + (param_lists.TI_to - param_lists.TI_from)/(param_lists.TI_N+1)* (i_TI+1)
                        param.TI_calc_block = param.TI - (param.t_ex/2 + param.dG)
                    else:
                        param.TI = 0
                        param.TI_calc_block = 0
                    
                    
                    param.FoV_f = np.ceil(param.FoV_f*10000)/10000
                    param.N_TE = np.ceil(param.N_TE)
                    param.TR = np.ceil(param.TR/ param.grad_raster_time)*param.grad_raster_time
                    param.TI = np.ceil(param.TI/ param.grad_raster_time)*param.grad_raster_time
                    param.TI_calc_block = np.ceil(param.TI_calc_block/ param.grad_raster_time)*param.grad_raster_time
                    
                    try:
                        output_folderename = "sequences\\"
                        code = str(i_FoV) + str(i_N_TE) + str(i_TR) + str(i_TI)
                        output_filename = str(M0_dj.textBox_name.get()) + '_'
                        
                        output_sequence, k_space_order_filing  = seqgen_TSE_NIRSII(param)
                        output_sequence.plot()    
                        output_sequence.write(output_folderename + output_filename + code)
                        
                        file = open(output_folderename + output_filename + code + '.json', 'w')
                        json.dump(param.__dict__, file, indent = 4)
                        file.close()
                         
                        k_space_save = {'k_space_order': k_space_order_filing}
                        kspace_output_filename = output_folderename + output_filename + code + "_k_space_order_filing" 
                        with open(kspace_output_filename + ".json", 'w') as outfile:
                            json.dump(k_space_save, outfile)    
                        array.append([param.FoV_f, param.N_TE, param.TR, param.TI])
                        print("work for ", str([param.FoV_f, param.N_TE, param.TR, param.TI]))
                    except:
                        print("error for ", str([param.FoV_f, param.N_TE, param.TR, param.TI]))
    
            
def param_digits_create(Main,M0_dj, default):   
    row = 0
    M0_dj.label_min = tk.Label(Main, text = "min", width = 10)
    M0_dj.label_min.grid(row=row, column=2)
    M0_dj.label_max = tk.Label(Main, text = "max", width = 10)
    M0_dj.label_max.grid(row=row, column=3)
    M0_dj.label_min = tk.Label(Main, text = "from", width = 10)
    M0_dj.label_min.grid(row=row, column=4)
    M0_dj.label_max = tk.Label(Main, text = "to", width = 10)
    M0_dj.label_max.grid(row=row, column=5)
    M0_dj.label_max = tk.Label(Main, text = "N", width = 10)
    M0_dj.label_max.grid(row=row, column=6)
    row += 1
    
    M0_dj.label_FoV_f = tk.Label(Main, text = 'FoV_f, m')
    M0_dj.label_FoV_f.grid(row=row , column=0,sticky = "E")
    M0_dj.textBox_FoV_f = tk.Entry(Main,width = 6)
    M0_dj.textBox_FoV_f.insert(0, default.FoV_f)
    M0_dj.textBox_FoV_f.grid(row=row, column=1)
    M0_dj.label_FoV_f_min = tk.Label(Main)
    M0_dj.label_FoV_f_min.grid(row=row, column=2)
    M0_dj.label_FoV_f_max = tk.Label(Main)
    M0_dj.label_FoV_f_max.grid(row=row, column=3)
    M0_dj.textBox_FoV_f_from = tk.Entry(Main,width = 6)
    M0_dj.textBox_FoV_f_from.insert(0, default.FoV_f)
    M0_dj.textBox_FoV_f_from.grid(row=row, column=4)
    M0_dj.textBox_FoV_f_to = tk.Entry(Main,width = 6)
    M0_dj.textBox_FoV_f_to.insert(0, default.FoV_f)
    M0_dj.textBox_FoV_f_to.grid(row=row, column=5)
    M0_dj.textBox_FoV_f_N = tk.Entry(Main,width = 6)
    M0_dj.textBox_FoV_f_N.insert(0, 1)
    M0_dj.textBox_FoV_f_N.grid(row=row, column=6)
    row += 1
    
    M0_dj.label_N_TE = tk.Label(Main, text = 'N_TE')
    M0_dj.label_N_TE.grid(row=row , column=0,sticky = "E")
    M0_dj.textBox_N_TE = tk.Entry(Main,width = 6)
    M0_dj.textBox_N_TE.insert(0, default.N_TE)
    M0_dj.textBox_N_TE.grid(row=row, column=1)
    M0_dj.label_N_TE_min = tk.Label(Main)
    M0_dj.label_N_TE_min.grid(row=row, column=2)
    M0_dj.label_N_TE_max = tk.Label(Main)
    M0_dj.label_N_TE_max.grid(row=row, column=3)
    M0_dj.textBox_N_TE_from = tk.Entry(Main,width = 6)
    M0_dj.textBox_N_TE_from.insert(0, default.N_TE)
    M0_dj.textBox_N_TE_from.grid(row=row, column=4)
    M0_dj.textBox_N_TE_to = tk.Entry(Main,width = 6)
    M0_dj.textBox_N_TE_to.insert(0, default.N_TE)
    M0_dj.textBox_N_TE_to.grid(row=row, column=5)
    M0_dj.textBox_N_TE_N = tk.Entry(Main,width = 6)
    M0_dj.textBox_N_TE_N.insert(0, 1)
    M0_dj.textBox_N_TE_N.grid(row=row, column=6)
    row += 1
    M0_dj.label_TE = tk.Label(Main, text = 'TE, c')
    M0_dj.label_TE.grid(row=row , column=0,sticky = "E")
    M0_dj.label_TE_v = tk.Label(Main)
    M0_dj.label_TE_v.grid(row=row, column=1)
    row += 1
    
    M0_dj.label_TR = tk.Label(Main, text = 'TR, s')
    M0_dj.label_TR.grid(row=row, column=0,sticky = "E")
    M0_dj.textBox_TR = tk.Entry(Main,width = 6)
    M0_dj.textBox_TR.insert(0, default.TR)
    M0_dj.textBox_TR.grid(row=row, column=1)
    M0_dj.label_TR_min = tk.Label(Main)
    M0_dj.label_TR_min.grid(row=row, column=2)
    M0_dj.label_TR_max = tk.Label(Main)
    M0_dj.label_TR_max.grid(row=row, column=3)
    M0_dj.textBox_TR_from = tk.Entry(Main,width = 6)
    M0_dj.textBox_TR_from.insert(0, default.TR)
    M0_dj.textBox_TR_from.grid(row=row, column=4)
    M0_dj.textBox_TR_to = tk.Entry(Main,width = 6)
    M0_dj.textBox_TR_to.insert(0, default.TR)
    M0_dj.textBox_TR_to.grid(row=row, column=5)
    M0_dj.textBox_TR_N = tk.Entry(Main,width = 6)
    M0_dj.textBox_TR_N.insert(0, 1)
    M0_dj.textBox_TR_N.grid(row=row, column=6)
    row += 1
    M0_dj.label_ETL = tk.Label(Main, text = 'ETL')
    M0_dj.label_ETL.grid(row=row , column=0,sticky = "E")
    M0_dj.label_ETL_v = tk.Label(Main)
    M0_dj.label_ETL_v.grid(row=row, column=1)
    row += 1
    
    M0_dj.radio_IR = tk.IntVar() 
    M0_dj.radio_IR.set(20)
    M0_dj.labelIR = tk.Label(Main, text = "IR:", width = 10)
    M0_dj.labelIR.grid(row=row, column=0,sticky = "E")
    M0_dj.IR_off = tk.Radiobutton(Main, text="off", variable=M0_dj.radio_IR, value=0) 
    M0_dj.IR_off.grid(row=row, column=1)
    M0_dj.IR_on = tk.Radiobutton(Main, text="on", variable=M0_dj.radio_IR, value=1) 
    M0_dj.IR_on.grid(row=row, column=2)
    row += 1
    
    M0_dj.label_TI = tk.Label(Main, text = 'TI, s')
    M0_dj.label_TI.grid(row=row, column=0,sticky = "E")
    M0_dj.textBox_TI = tk.Entry(Main,width = 6)
    M0_dj.textBox_TI.insert(0, default.TI)
    M0_dj.textBox_TI.grid(row=row, column=1)
    M0_dj.label_TI_min = tk.Label(Main)
    M0_dj.label_TI_min.grid(row=row, column=2)
    M0_dj.label_TI_max = tk.Label(Main)
    M0_dj.label_TI_max.grid(row=row, column=3)
    M0_dj.textBox_TI_from = tk.Entry(Main,width = 6)
    M0_dj.textBox_TI_from.insert(0, default.TI)
    M0_dj.textBox_TI_from.grid(row=row, column=4)
    M0_dj.textBox_TI_to = tk.Entry(Main,width = 6)
    M0_dj.textBox_TI_to.insert(0, default.TI)
    M0_dj.textBox_TI_to.grid(row=row, column=5)
    M0_dj.textBox_TI_N = tk.Entry(Main,width = 6)
    M0_dj.textBox_TI_N.insert(0, 1)
    M0_dj.textBox_TI_N.grid(row=row, column=6)
    row += 1
    
def param_set_lim(M0_dj,lim,param):  
    M0_dj.label_FoV_f_min.configure(text = str(np.ceil(lim.FoV_f_min*1000)/1000))
    M0_dj.label_FoV_f_max.configure(text = str(np.floor(lim.FoV_f_max*1000)/1000))
    
    M0_dj.label_N_TE_min.configure(text = str(np.ceil(lim.N_TE_min)))
    M0_dj.label_N_TE_max.configure(text = str(np.floor(lim.N_TE_max)))
    M0_dj.label_TE_v.configure(text = str(np.floor(param.TE*1000)/1000))
    
    M0_dj.label_TR_min.configure(text = str(np.ceil(lim.TR_min*1000)/1000))
    M0_dj.label_TR_max.configure(text = str(np.floor(lim.TR_max*1000)/1000))
    M0_dj.label_ETL_v.configure(text = str(np.floor(param.ETL)))
    
    M0_dj.label_TI_min.configure(text = str(np.ceil(lim.TI_min*1000)/1000))
    M0_dj.label_TI_max.configure(text = str(np.floor(lim.TI_max*1000)/1000))
    
def param_dj_get(M0_dj):
    
    param_get = SimpleNamespace()
    param_get.FoV_f = float(M0_dj.textBox_FoV_f.get())
    param_get.N_TE = float(M0_dj.textBox_N_TE.get())
    param_get.TR = float(M0_dj.textBox_TR.get())
    if M0_dj.radio_IR.get() == 1:
        param_get.IR = True
    else:
        param_get.IR = False     
    param_get.TI = float(M0_dj.textBox_TI.get())
    return param_get

def param_dj_get_lists(M0_dj):
    
    param_lists = SimpleNamespace()
    param_lists.FoV_f_from = float(M0_dj.textBox_FoV_f_from.get())
    param_lists.FoV_f_to = float(M0_dj.textBox_FoV_f_to.get())
    param_lists.FoV_f_N = int(M0_dj.textBox_FoV_f_N.get())
    
    param_lists.N_TE_from = float(M0_dj.textBox_N_TE_from.get())
    param_lists.N_TE_to = float(M0_dj.textBox_N_TE_to.get())
    param_lists.N_TE_N = int(M0_dj.textBox_N_TE_N.get())
    
    param_lists.TR_from = float(M0_dj.textBox_TR_from.get())
    param_lists.TR_to = float(M0_dj.textBox_TR_to.get())
    param_lists.TR_N = int(M0_dj.textBox_TR_N.get())
    
    param_lists.TI_from = float(M0_dj.textBox_TI_from.get())
    param_lists.TI_to = float(M0_dj.textBox_TI_to.get())
    param_lists.TI_N = int(M0_dj.textBox_TI_N.get())
    return param_lists
    
name = "TSE"
default = param_default()

default.FoV_f = 256e-3
default.N_TE = 1
default.TR = 3500e-3
default.TI = 100e-3



Main = tk.Tk()
Main.title(name)
Main.geometry("700x300+300+300")
Main.resizable(False,False)

M0_dj = SimpleNamespace()

param_digits_create(Main,M0_dj,default)

M0_dj.btn_set = tk.Button(Main, text = 'Set',command = calc_lims)
M0_dj.btn_set.grid(row=1, column=8,sticky = "W")


M0_dj.btn_save = tk.Button(Main, text = 'Save', command = save)
M0_dj.btn_save.grid(row=4, column=8,sticky = "W")
# filename
M0_dj.label_name= tk.Label(Main, text = 'Output file name',width = 13)
M0_dj.label_name.grid(row=5, column=8,sticky = "W")
M0_dj.textBox_name = tk.Entry(Main,width = 25)
M0_dj.textBox_name.insert(0, name + datetime.now().strftime("_%d%m%y_%H%M"))
M0_dj.textBox_name.grid(row=6, column=8, columnspan=3,sticky = "W")

calc_lims()

Main.mainloop()

# Main.mainloop()