# To run the program
# Import our module
import numpy as np
import math
import webbrowser
import tkinter as tk
import pandas as pd
import matplotlib.pyplot as plt
from tkinter import filedialog
from tkinter import messagebox
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.backends.backend_pdf import PdfPages


# the main Tkinter window
ws = tk.Tk()
# setting the title 
ws.title("Fracture Mechanics Shear Capacity of SFRC")
# dimensions of the main window
ws.attributes('-zoomed', True)
# Note: Windows use below; *Not* -fullscreen
#ws.state('zoomed')
#ws.geometry("1200x1000")

# Define key variables common to all routines (called from bottom)
def variables():
    # section width (b), depth to reinf (d), steel area (As), conc (Fc)
    # fibre vol (eta), fibre length (fl), fibre dia (fd), fibre effective range (feff)
    # reduction factor (phi), counter (check_no)
    global b, d, As, Fc, eta, fl, fd, feff, phi, check_no
    b = 0
    d = 0
    As = 0
    Fc = 0
    eta = 0
    fl = 0
    fd = 0
    feff = 0
    phi = 0
    check_no = 0

# main program (called from bottom)
def main():
    global b, d, As, Fc, eta, fl, fd, feff, phi, check_no 
    def print_charts():
        global fig1, b, d, As, Fc, eta, fl, fd, feff, phi, check_no
        
        # get the values of variables from input frame
        # check of empty or zero input values
        def check_input():
            global counter
            counter = 0
                
            if not b_sect.get():
                counter = counter + 1
            if not d_sect.get():
                counter = counter + 1
            if not As_sect.get():
                counter = counter + 1
            if not Fc_conc.get():
                counter = counter + 1
            if not fl_fibre.get():
                counter = counter + 1
            if not fd_fibre.get():
                counter = counter + 1
            if not feff_fibre.get():
                counter = counter + 1
            if not eta_fibre.get():
                counter = counter + 1
            if not phi_const.get():
                counter = counter + 1
            return
        
        # call check input function
        check_input()
        
        # if there is input in all boxes get values
        if counter == 0:
            
            b = float(b_sect.get())
            d =  int(d_sect.get())
            As = float(As_sect.get())
            Fc = float(Fc_conc.get())
            fl = float(fl_fibre.get())
            fd = float(fd_fibre.get())
            feff = float(feff_fibre.get())
            eta = float(eta_fibre.get())
            phi = float(phi_const.get())
            
            # check if any input values are "0" otherwise program will not run
            # make list of variables and test with "if" statement
            check_zero = [b, d, As, Fc, fl, fd, feff, phi]
            check_no = 0
            if check_no in check_zero:
                win1 = tk.Tk()
                win1.withdraw()
                messagebox.showwarning("Warning","Only fibre content can be zero, other values must be non-zero")
                win1.destroy()
            else:
                check_no = 1
  
        else:
            win2 = tk.Tk()
            win2.withdraw()
            messagebox.showwarning("Warning","One or more inputs missing")
            win2.destroy()
      
        def calc_capacity():
            global b, d, As, Fc, eta, fl, fd, feff, phi, fig1, canvas
            # analysis and print results
            
            # constants used in this calculation
            # conc modulus (Econc), conc density (rho), steel modulus (Esteel)
            # reinf to conc E ratio (rho_alpha), elastic compression zone (k_comp_zone)
            
            Esteel = 200000.0            
            rho = 2400.0
            Econc = pow(rho, 1.5)*0.043*math.sqrt(Fc)
            rho_alpha = As*Esteel/(b*d*Econc)
            k_comp_zone = 2/(1+math.sqrt(1+2/rho_alpha))
            
            crack_w = []
            stress_G = []
            
            # Point 1 - concrete tensile capacity
            fct = 2.12*math.log(1+Fc/10)
            f1 = fct
            w1 = 0
            crack_w.append(w1)
            stress_G.append(f1)
            
            # Point 2 - tensile capacity at 0.05mm crack opening
            # concrete tensile capacity due to aggregate interlock - adopt 0.85MPa at 0.05mm opening
            fct2 = 0.85*(1-0.05/0.2)
            # proportion due to fibre - 240MPa ave fibre stress, 0.3 fibre effectiveness due to 3-D orientation
            f_ft2 = 0.3*240*eta/7850
            # combined tensile capacity
            f2 = fct2+f_ft2
            w2 = 0.05
            crack_w.append(w2)
            stress_G.append(f2)
            
            # Point 3 - tensile capacity of steel fibre only
            f3 = f_ft2
            w3 = 0.2
            crack_w.append(w3)
            stress_G.append(f3)
            
            # Point 4 - defined by user input
            f4 = 0
            w4 = feff
            crack_w.append(w4)
            stress_G.append(f4)
            
            # Fracture Energy G - area under the curve
            G = (f1-f2)*w2/2 + w2*f2 + (f2-f3)*(w3-w2)/2 + f3*(w3-w2) + f3*(w4-w3)/2
            
            # Characteristic length
            l_ch = Econc*G/fct**2
            
            # Plain concrete shear capacity above NA
            V_o = b*d*k_comp_zone*fct*0.001
            
            # Multiplier due to fibre energy absorption in fracture zone
            mf = (3*l_ch/d)**0.25
            #print("Multiplier due to FM = ", mf)
            
            # limit the fracture mechanics gain to double plain concrete
            if mf > 2:
                mf = 2
            
            # Fracture mechanics limit state shear capacity
            Vsr = mf*V_o
            # Reduced by reduction factor
            Vsr_r = phi*Vsr
            
         
            
            # Figure that will contain graphic output
            
            # the figure that will contain the plots and text, size in inches
            fig1 = Figure(figsize = (11.67, 8.27), dpi = 100)
                
            # set up a 21 x 30 grid of boxes
            ax = fig1.add_gridspec(nrows=21, ncols=35)
            
            # add some input text
            Col_input = fig1.add_subplot(ax[0:8, 0:8])
            Col_input.axis("off")
            Col_input.text(0.1,0.9,"Shear section width (mm) = %.0f" % b)
            Col_input.text(0.1,0.8,"Shear section depth = %.0f" % d)
            Col_input.text(0.1,0.7,"Tensile steel area (sq.mm) = %.0f" % As)
            Col_input.text(0.1,0.6,"Concrete strength (MPa) = %.0f" % Fc)
            Col_input.text(0.1,0.5,"Fibre length (mm) = %.0f" % fl)
            Col_input.text(0.1,0.4,"Fibre diameter (mm) = %.0f" % fd)
            Col_input.text(0.1,0.3,"Fibre effective range (mm) = %.0f" % feff)
            Col_input.text(0.1,0.2,"Fibre content (kg/cu.m) = %.0f" % eta)
            Col_input.text(0.1,0.1,"Capacity reduction = %.2f" % phi)
            
            # add some results from calculations
            Col_input = fig1.add_subplot(ax[12:20, 0:8])
            Col_input.axis("off")
            Col_input.text(0.1,0.7,"Results")
            Col_input.text(0.1,0.6,"Fracture Energy (N/mm) = %.3f" % G)
            Col_input.text(0.1,0.5,"Characteristic Length (mm) = %.0f" % l_ch)
            Col_input.text(0.1,0.4,"Shear capacity above NA (kN) = %.2f" % V_o)
            Col_input.text(0.1,0.3,"Fracture Process Zone Effect = %.2f" % mf)
            Col_input.text(0.1,0.2,"Shear Capacity - Limit State (kN) = %.2f" % Vsr)
            Col_input.text(0.1,0.1,"Shear Capacity - Reduced (kN) = %.2f" % Vsr_r)
                            
            # define plot1 and add the subplot
            # in rows 10 to 20 and columns 0 to 8
            plot1 = fig1.add_subplot(ax[0:21, 16:35])
            
            # plot the softening curve
            plot1.plot(crack_w,stress_G)
            #print("crack widths = ", crack_w)
            #print("stresses = ", stress_G)
            
            # Add a title
            plot1.set_title('Idealised Softening Curve')
            
            # Add x and y Label
            plot1.set_xlabel('Crack width (mm)')
            plot1.set_ylabel('Stress (MPa)')
            
            # Add a grid
            plot1.grid(alpha=.4,linestyle='--')
            
            # round up the axes range to max values
            w_int_max = (int(math.ceil(w4)))
            f_int_max = (int(math.ceil(f1)))
            
            # make axis at zero
            plot1.axis([0,w_int_max,0,f_int_max])
            
            # creating the Tkinter canvas containing the Matplotlib figures
            canvas = FigureCanvasTkAgg(fig1, master = ws)
            #canvas.config(width=100, height=50)
            canvas.draw()
          
            # placing the canvas on the Tkinter window
            canvas.get_tk_widget().pack()

        # calculate the charts using function above
        if check_no > 0 and counter == 0:
            calc_capacity()
    
    def recalculate():
        # remove graphic output ready for next iteration
        canvas.get_tk_widget().pack_forget()
        # go back to start of function
        print_charts()
    
    # save and quit the program
    def save():
        # tkinter window for file save dialog
        ws1 = tk.Tk()
        ws1.withdraw()
        
        # show a dialog box to save the file
        fname = filedialog.asksaveasfilename(title='Name a file', filetypes=[("pdf", ".pdf")], defaultextension='.pdf')
        pp = PdfPages(fname)
        pp.savefig(fig1)
        pp.close()
        
        # Destroy the tkinter instance once function is complete
        # this releases and stops hanging problems
        ws1.destroy()

    
    # Input-output
    # frame for numerical input data
    frame = tk.Frame(ws, padx=10, pady=10)
    frame.pack(expand=True)
    
    b_lbl = tk.Label(frame, text='Section width (mm)')
    b_lbl.grid(row=1, column=1)
    b_sect = tk.Entry(frame)
    b_sect.grid(row=1, column=2, pady=5)
    
    d_lbl = tk.Label(frame, text='Depth to reo bars (mm)')
    d_lbl.grid(row=2, column=1)
    d_sect = tk.Entry(frame)
    d_sect.grid(row=2, column=2, pady=5)
    
    As_lbl = tk.Label(frame, text='Steel area (sq.mm)')
    As_lbl.grid(row=3, column=1)
    As_sect = tk.Entry(frame)
    As_sect.grid(row=3, column=2, pady=5)
    
    Fc_lbl = tk.Label(frame, text='Concrete Fc (MPa)')
    Fc_lbl.grid(row=4, column=1)
    Fc_conc = tk.Entry(frame)
    Fc_conc.grid(row=4, column=2, pady=5)
    
    fl_lbl = tk.Label(frame, text='Fibre length (mm)')
    fl_lbl.grid(row=5, column=1)
    fl_fibre = tk.Entry(frame)
    fl_fibre.grid(row=5, column=2, pady=5)
    
    fd_lbl = tk.Label(frame, text='Fibre diameter (mm)')
    fd_lbl.grid(row=6, column=1)
    fd_fibre = tk.Entry(frame)
    fd_fibre.grid(row=6, column=2, pady=5)
    
    feff_lbl = tk.Label(frame, text='Fibre effective range (mm)')
    feff_lbl.grid(row=7, column=1)
    feff_fibre = tk.Entry(frame)
    feff_fibre.grid(row=7, column=2, pady=5)
    
    eta_lbl = tk.Label(frame, text='Fibre content (kg/cu.m)')
    eta_lbl.grid(row=8, column=1)
    eta_fibre = tk.Entry(frame)
    eta_fibre.grid(row=8, column=2, pady=5)
    
    phi_lbl = tk.Label(frame, text='Reduction factor (constant)')
    phi_lbl.grid(row=9, column=1)
    phi_const = tk.Entry(frame)
    phi_const.grid(row=9, column=2, pady=5)
    
    note1_lbl = tk.Label(frame, padx=20, width=600, anchor='w', text='Notes :')
    note1_lbl.grid(row=1, column=3)
    
    note2_lbl = tk.Label(frame, padx=20, width=600, anchor='w', justify='left', wraplength=500, text='This program calculates the limit state shear capacity of a rectangular \
    steel fibre reinforced concrete section using fracture mechanics. Output is saved to a pdf file.')
    note2_lbl.grid(row=2, column=3, rowspan=2)
        
    note3_lbl = tk.Label(frame, padx=20, width=600, anchor='w', justify='left', wraplength=500, text='Fibre pull-out is one of the most important parameters of this calculation. The type of fibre determines the post crack tensile and shear capacity. A spade-end fibre has an effective range of about 20mm, a hook-end is about 5mm. Shorter range is more conservative.')
    note3_lbl.grid(row=4, column=3, rowspan=2)
    
    note4_lbl = tk.Label(frame, padx=20, width=600, anchor='w', justify='left', wraplength=500, text='The reduction factor can be extrapolated from codes in your locality. For example, a "material factor" of 1.5 is the same as 0.67.')
    note4_lbl.grid(row=6, column=3, rowspan=2)
    
    note5_lbl = tk.Label(frame, padx=20, width=600, anchor='w', justify='left', wraplength=500, text='For a more detailed description of the theoretical background refer to this link.')
    note5_lbl.grid(row=8, column=3, rowspan=1)
    
    # enter a hyperlink to frconcrete website
    # define a callback function
    def callback(url):
        webbrowser.open_new_tab(url)

    #Create a Label to display the link
    link = tk.Label(frame, padx=20, width=600, anchor='w', text="Shear Capacity Using the Fracture Mechanics Method",font=('Helveticabold', 10), fg="blue", cursor="hand2")
    link.bind("<Button-1>", lambda e: callback("https://frconcrete.net/r-shear-fracmech.php"))
    link.grid(row=9, column=3)
    
    # frame for buttons below the input
    frame2 = tk.Frame(frame)
    frame2.grid(row=11, columnspan=2, pady=10)
    
    # Button to show charts on screen
    btn_PI = tk.Button(frame2, text="Show Chart", padx=10, pady=5, command=print_charts)
    btn_PI.pack(side=tk.LEFT)
    
    # Button to re-calculate
    btn_PI = tk.Button(frame2, text="Re-calculate", padx=10, pady=5, command=recalculate)
    btn_PI.pack(side=tk.LEFT)
    
    # Button to save charts to file and exit program
    btn_PI = tk.Button(frame2, text="Save", padx=10, pady=5, command=save)
    btn_PI.pack(side=tk.LEFT)
    
    # Quit button
    exit_btn = tk.Button(frame2, text='Quit', padx=10, pady=5, command=lambda:ws.destroy())
    exit_btn.pack(side=tk.LEFT)

# define and initiate global variables
variables()
# initiate main program
main()

ws.mainloop()

