# RCAIDE/Library/Methods/Geometry/Airfoil/import_airfoil_geometry.py

# ----------------------------------------------------------------------
#  Imports
# ---------------------------------------------------------------------- 
from RCAIDE.Framework.Core import  Data 
import numpy as np
from scipy import interpolate

# ----------------------------------------------------------------------------------------------------------------------
# import_airfoil_geometry
# ----------------------------------------------------------------------------------------------------------------------   
def import_airfoil_geometry(airfoil_geometry_file, npoints = 201,surface_interpolation = 'cubic'):
    """This imports an airfoil geometry from a text file  and store
    the coordinates of upper and lower surfaces as well as the mean
    camberline
    
    Assumptions:
    Works for Selig and Lednicer airfoil formats. Automatically detects which format based off first line of data. Assumes it is one of those two.
    Source:
    airfoiltools.com/airfoil/index - method for determining format and basic error checking
    Inputs:
    airfoil_geometry_files   <list of strings>
    surface_interpolation   - type of interpolation used in the SciPy function. Preferable options are linear, quardratic and cubic. 
    Full list of options can be found here : 
    https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy.interpolate.interp1d
    Outputs:
    airfoil_data.
        thickness_to_chord 
        x_coordinates 
        y_coordinates
        x_upper_surface
        x_lower_surface
        y_upper_surface
        y_lower_surface
        camber_coordinates  
    Properties Used:
    N/A
    """

    if npoints%2 != 1:
        npoints+= 1
        print('Number of points must be odd, changing to ' + str(npoints) + ' points')      
    
    geometry     = Data()
    half_npoints = npoints//2         
 
    # Open file and read column names and data block
    f = open(airfoil_geometry_file) 

    # Extract data
    data_block = f.readlines()
    try:
        # Check for header block
        first_element = float(data_block[0][0])
        if first_element == 1.:
            lednicer_format = False
    except:
        # Check for format line and remove header block
        format_line = data_block[1]

        # Check if it's a Selig or Lednicer file
        try:
            format_flag = float(format_line.strip().split()[0])
        except:
            format_flag = float(format_line.strip().split(',')[0])
            
        if format_flag > 1.01: # Amount of wiggle room per airfoil tools
            lednicer_format = True
            # Remove header block
            data_block      = data_block[3:]
        else:
            lednicer_format = False   
            # Remove header block
            data_block = data_block[1:]
            
    # Close the file
    f.close() 

    if lednicer_format:
        x_up_surf = []
        y_up_surf = []
        x_lo_surf = []
        y_lo_surf = []
        
        # Loop through each value: append to each column
        upper_surface_flag = True
        for line_count , line in enumerate(data_block): 
            #check for blank line which signifies the upper/lower surface division 
            line_check = data_block[line_count].strip()
            if line_check == '':
                upper_surface_flag = False
                continue
            if upper_surface_flag:
                x_up_surf.append(float(data_block[line_count].strip().split()[0])) 
                y_up_surf.append(float(data_block[line_count].strip().split()[1])) 
            else:                              
                x_lo_surf.append(float(data_block[line_count].strip().split()[0])) 
                y_lo_surf.append(float(data_block[line_count].strip().split()[1]))   

    else:
        x_up_surf_rev  = []
        y_up_surf_rev  = []
        x_lo_surf      = []
        y_lo_surf      = []
        
        # Loop through each value: append to each column
        upper_surface_flag = True
        for line_count , line in enumerate(data_block): 
            #check for line which starts with 0., which should be the split between upper and lower in selig
            line_check = data_block[line_count].strip()
            
            # Remove any commas
            line_check = line_check.replace(',','')
            
            if float(line_check.split()[0]) == 0.:
                x_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[0])) 
                y_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[1]))

                x_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[0])) 
                y_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[1])) 

                upper_surface_flag = False
                continue

            if upper_surface_flag:
                x_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[0])) 
                y_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[1])) 
            else:                              
                x_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[0])) 
                y_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[1]))
            
            
            if upper_surface_flag ==True:
                # check if next line flips without x-coordinate going to 0
                next_line  = data_block[line_count+1].strip()
                next_line  = next_line.replace(',','')
            
                if next_line.split()[0]>line_check.split()[0] and float(next_line.split()[0]) >0.:
                    upper_surface_flag = False
                
        # Upper surface values in Selig format are reversed from Lednicer format, so fix that
        x_up_surf_rev.reverse()
        y_up_surf_rev.reverse()

        x_up_surf = x_up_surf_rev
        y_up_surf = y_up_surf_rev 

    x_up_surf = np.array(x_up_surf)
    x_lo_surf = np.array(x_lo_surf)
    y_up_surf = np.array(y_up_surf)
    y_lo_surf = np.array(y_lo_surf)  
    
    # Check for extra zeros (OpenVSP exports extra zeros)
    if len(np.unique(x_up_surf))!=len(x_up_surf):
        x_up_surf = x_up_surf[1:]
        x_lo_surf = x_lo_surf[1:]
        y_up_surf = y_up_surf[1:]
        y_lo_surf = y_lo_surf[1:]

    # create custom spacing for more points and leading and trailing edge
    t            = np.linspace(0,4,npoints-1)
    delta        = 0.25 
    A            = 5
    f            = 0.25
    smoothsq     = 5 + (2*A/np.pi) *np.arctan(np.sin(2*np.pi*t*f + np.pi/2)/delta) 
    dim_spacing  = np.append(0,np.cumsum(smoothsq)/sum(smoothsq))
    
    # compute thickness, camber and concatenate coodinates 
    x_data        = np.hstack((x_lo_surf[::-1], x_up_surf[1:])) 
    y_data        = np.hstack((y_lo_surf[::-1], y_up_surf[1:]))   
    tck,u         = interpolate.splprep([x_data,y_data],k=3,s=0) 
    out           = interpolate.splev(dim_spacing,tck) 
    x_data        = out[0]   
    y_data        = out[1]  
    
    # shift points to leading edge (x = 0, y = 0)
    x_delta  = min(x_data)
    x_data   = x_data - x_delta 
    
    arg_min  = np.argmin(x_data) 
    y_delta  = y_data[arg_min]
    y_data   = y_data - y_delta
    
    if (x_data[arg_min] == 0) and (y_data[arg_min]  == 0): 
        x_data[arg_min]  = 0  
        y_data[arg_min]  = 0 
    
    # make sure points start and end at x = 1.0
    x_data[0]  = 1.0
    x_data[-1] = 1.0
    
    # make sure a small gap at trailing edge
    if (y_data[0] == y_data[-1]): 
        y_data[0]          = y_data[0]  - 1E-4
        y_data[-1]         = y_data[-1] + 1E-4
        
    # thicknes and camber distributions require equal points     
    x_up_surf_old  = np.array(x_up_surf)   
    arrx_up_interp = interpolate.interp1d(np.arange(x_up_surf_old.size),x_up_surf_old, kind=surface_interpolation)
    x_up_surf_new  = arrx_up_interp(np.linspace(0,x_up_surf_old.size-1,half_npoints))    
 
    x_lo_surf_old  = np.array(x_lo_surf) 
    arrx_lo_interp = interpolate.interp1d(np.arange(x_lo_surf_old.size),x_lo_surf_old, kind=surface_interpolation )
    x_lo_surf_new  = arrx_lo_interp(np.linspace(0,x_lo_surf_old.size-1,half_npoints)) 
 
    # y coordinate s 
    y_up_surf_old  = np.array(y_up_surf)   
    arry_up_interp = interpolate.interp1d(np.arange(y_up_surf_old.size),y_up_surf_old, kind=surface_interpolation)
    y_up_surf_new  = arry_up_interp(np.linspace(0,y_up_surf_old.size-1,half_npoints))    
 
    y_lo_surf_old  = np.array(y_lo_surf) 
    arry_lo_interp = interpolate.interp1d(np.arange(y_lo_surf_old.size),y_lo_surf_old, kind=surface_interpolation)
    y_lo_surf_new  = arry_lo_interp(np.linspace(0,y_lo_surf_old.size-1,half_npoints)) 

    # compute thickness, camber and concatenate coodinates 
    thickness      = y_up_surf_new - y_lo_surf_new
    camber         = y_lo_surf_new + thickness/2  
    max_t          = np.max(thickness)
    max_c          = max(x_data) - min(x_data)
    t_c            = max_t/max_c 
    
    geometry.thickness_to_chord = t_c
    geometry.max_thickness      = max_t     
    geometry.x_coordinates      = x_data   
    geometry.y_coordinates      = y_data      
    geometry.x_upper_surface    = x_up_surf_new 
    geometry.x_lower_surface    = x_lo_surf_new 
    geometry.y_upper_surface    = y_up_surf_new 
    geometry.y_lower_surface    = y_lo_surf_new              
    geometry.camber_coordinates = camber
         
    return geometry