from matplotlib import pyplot as plt
from matplotlib import cm
from matplotlib.colors import Normalize, TwoSlopeNorm
import numpy as np

def plotDeformedStructure2d(nDofPerNode,coordinates,connectivity,disp,scaleFactor,theTitle):
    # Create a figure and axis
    fig, ax = plt.subplots()

    # Plot undeformed structure
    for i in range(connectivity.shape[0]):
        x=[coordinates[connectivity[i,0],0],coordinates[connectivity[i,1],0]]
        y=[coordinates[connectivity[i,0],1],coordinates[connectivity[i,1],1]]
        # ax.plot(x,y,'k--o')
        ax.plot(x, y, 'k-', color='grey')  # Change color to grey
    
        # Plot deformed structure
    for i in range(connectivity.shape[0]):
        x=[coordinates[connectivity[i,0],0]+disp[nDofPerNode*connectivity[i,0]]*scaleFactor,coordinates[connectivity[i,1],0]+disp[nDofPerNode*connectivity[i,1]]*scaleFactor]
        y=[coordinates[connectivity[i,0],1]+disp[nDofPerNode*connectivity[i,0]+1]*scaleFactor,coordinates[connectivity[i,1],1]+disp[nDofPerNode*connectivity[i,1]+1]*scaleFactor]
        # ax.plot(x,y,'k-o')
        ax.plot(x,y,'k-')
        


    # Add node numbers
    # for i in range(coordinates.shape[0]):
    #     ax.text(coordinates[i,0], coordinates[i,1], str(i+1), fontsize=12, ha='right')

    # Remove the box (spines) around the plot
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.spines['bottom'].set_visible(False)

        # Plot properties
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_aspect('equal')  # Ensure the x and y scales are the same

    # Save the figure 
    #plt.savefig(f'{theTitle}.pdf', format='pdf')
    #plt.savefig(f'{theTitle}.png', format='PNG')
    
    # Show the figure
    plt.show()
    
    return

def plotInternalForces2dTruss(coordinates,connectivity, E, areas, disp, theTitle):
    # Create a figure and axis
    fig, ax = plt.subplots()

    # Plot undeformed structure
    for i in range(connectivity.shape[0]):
        x=[coordinates[connectivity[i,0],0],coordinates[connectivity[i,1],0]]
        y=[coordinates[connectivity[i,0],1],coordinates[connectivity[i,1],1]]
        ax.plot(x,y,'k--o')

    # Compute axial load
    N=np.zeros(connectivity.shape[0])
    for i in range(connectivity.shape[0]):
        l_undeformed=np.linalg.norm(coordinates[connectivity[i,1]]-coordinates[connectivity[i,0]])
        theta_undeformed=np.arctan2(coordinates[connectivity[i,1],1]-coordinates[connectivity[i,0],1],coordinates[connectivity[i,1],0]-coordinates[connectivity[i,0],0])    

        # Global reference coordinates
        u_X1=disp[2*connectivity[i,0]]
        u_Y1=disp[2*connectivity[i,0]+1]
        u_X2=disp[2*connectivity[i,1]]
        u_Y2=disp[2*connectivity[i,1]+1]
        # Local reference coordinates
        u_x1=u_X1*np.cos(theta_undeformed)+u_Y1*np.sin(theta_undeformed)
        u_x2=u_X2*np.cos(theta_undeformed)+u_Y2*np.sin(theta_undeformed)

        deltaL=u_x2-u_x1
        N[i]=E*areas[i]/l_undeformed*deltaL/1000
    
    # Determine the colormap and normalization
    max_abs_N = np.max(np.abs(N))
    norm = TwoSlopeNorm(vmin=-max_abs_N, vcenter=0, vmax=max_abs_N)
    cmap = cm.get_cmap('coolwarm')

    # Plot internal forces with heat map effect
    for i in range(connectivity.shape[0]):
        x = [coordinates[connectivity[i,0],0], coordinates[connectivity[i,1],0]]
        y = [coordinates[connectivity[i,0],1], coordinates[connectivity[i,1],1]]
        color = cmap(norm(N[i]))
        ax.plot(x, y, color=color, linewidth=2)

        # Add the value of N on each bar
        mid_x = (x[0] + x[1]) / 2
        mid_y = (y[0] + y[1]) / 2
        ax.text(mid_x, mid_y, f'{N[i]:.2f}', color='black', fontsize=12, ha='center', va='center')
    
    # Plot properties
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_aspect('equal')  # Ensure the x and y scales are the same

    # Add a single colorbar
    sm = cm.ScalarMappable(cmap=cmap, norm=norm)
    sm.set_array([])
    fig.colorbar(sm, ax=ax, label='Axial Force (kN)')

    # Remove the box (frame) around the plot
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.spines['bottom'].set_visible(False)


    # Save the figure 
    #plt.savefig(f'{theTitle}.pdf', format='pdf')
    #plt.savefig(f'{theTitle}.png', format='PNG')
    
    # Show the figure
    plt.show()
    
    return



def plotForceDisplacement(disp,force, theTitle):
    # Create a figure and axis
    fig, ax = plt.subplots()

    # Plot force-displacement curve
    ax.plot(disp,force,'k-')

    # Plot properties
    ax.set_xlabel('Displacement (mm)')
    ax.set_ylabel('Force (kN)')
    ax.grid(True)

    # Save the figure 
    #plt.savefig(f'{theTitle}.pdf', format='pdf')
    #plt.savefig(f'{theTitle}.png', format='PNG')
    
    # Show the figure
    plt.show()
    
    return


def plotInternalForces2dBeam(AllElement_data,QLocal_elem_list,theTitle):
    ## Bending moment diagram
    fig, ax = plt.subplots()

    M=np.zeros([len(QLocal_elem_list),2])
    for elemIdx, elem in enumerate(AllElement_data):
        connectivity=elem[1]
        coordinatesI=elem[2]
        coordinatesJ=elem[3]
        # Plot undeformed structure
        x=[coordinatesI[0],coordinatesJ[0]]
        y=[coordinatesI[1],coordinatesJ[1]]
        ax.plot(x,y,'k-o',linewidth=2)

        # Fill the arrays with the bending moment values
        M[elemIdx,0]=QLocal_elem_list[elemIdx,2]/1e6
        M[elemIdx,1]=QLocal_elem_list[elemIdx,5]/1e6

    # Determine the scale factor
    max_abs_M = np.max(np.abs(M))
    scaleFactor = 2000 / max_abs_M

    # Plot moments 
    for elemIdx, elem in enumerate(AllElement_data):
        coordinatesI=elem[2]
        coordinatesJ=elem[3]
        x=[coordinatesI[0],coordinatesJ[0]]
        y=[coordinatesI[1],coordinatesJ[1]]
        dx=x[1]-x[0]
        dy=y[1]-y[0]

        moments = M[elemIdx, :]
        x4M_I_1=x[0]
        y4M_I_1=y[0]
        x4M_I_2=x4M_I_1+scaleFactor*(dy/(dy+1e-6))*moments[0]
        y4M_I_2=y4M_I_1+scaleFactor*(dx/(dx+1e-6))*moments[0]
        x4M_J_1=x[1]
        y4M_J_1=y[1]
        x4M_J_2=x4M_J_1+scaleFactor*(dy/(dy+1e-6))*-1*moments[1]
        y4M_J_2=y4M_J_1+scaleFactor*(dx/(dx+1e-6))*-1*moments[1]

        ax.plot([x4M_I_1,x4M_I_2],[y4M_I_1,y4M_I_2],'k-')
        ax.plot([x4M_I_2,x4M_J_2],[y4M_I_2,y4M_J_2],'k-')
        ax.plot([x4M_J_1,x4M_J_2],[y4M_J_1,y4M_J_2],'k-')

        # Add moment values near the points
        # dx=x[1]-x[0]
        # dy=y[1]-y[0]
        # offset_x = 0.4*6000*(dx)/(dx+1e-6)  # Offset for text placement
        # offset_y = 0.05*7000*(dy)/(dy+1e-6)  # Offset for text placement
        ax.text(x4M_I_2, y4M_I_2, f'{moments[0]:.2f}', fontsize=8, ha='right', color='black')
        ax.text(x4M_J_2, y4M_J_2, f'{moments[1]:.2f}', fontsize=8, ha='left', color='black')


    ax.axis('off')
    ax.set_aspect('equal')

    # Save the figure 
    #plt.savefig(f'{theTitle}.pdf', format='pdf')
    #plt.savefig(f'{theTitle}.png', format='PNG')

    plt.show()
    