""" readsas reads a sas data file into a pandas dataframe
    and converts the variable names to all lower case strings. 
    For columns imported as byte objects, it first attempts
    to convert byte objects into literal strings using 'utf-8'
    and if it fails it uses 'latin-1' encoding. It replaces byte 
    object columns with decoded literal strings.

    sas2stata performs the steps as described above and then 
    converts the pandas dataframe to Stata datafile (.dta) with
    the variable names converted to upper case strings as labels.
"""
import pandas as pd
import numpy as np
import pyreadstat
from pandas.api.types import is_datetime64_any_dtype as is_datetime, is_object_dtype as is_object
def sas2stata(sasfile,statafile):
    """ sas2sstata reads a sas data file into a pandas dataframe 
    and converts the variable names to all lower case strings. 
    For columns imported as byte objects, it first attempts to 
    convert byte objects into literal strings using 'utf-8' and 
    if it fails it uses 'latin-1' encoding. It replaces byte object
    columns with decoded literal strings and converts the pandas
    dataframe to Stata datafile (.dta) with the variable names
    converted to upper case strings as labels.
    """
    dat = pd.read_sas(sasfile)
    dat.columns = map(str.lower,dat.columns)
    str_df = dat.select_dtypes([object])
    if str_df.empty == False:
        try:
            str_df = str_df.stack().str.decode('utf-8').unstack()
        except:
            str_df = str_df.stack().str.decode('latin-1').unstack()
    for col in str_df:
        dat[col] = str_df[col]
    # Read in SAS dataset, make all variable names lower case and change decoding type if latin decoding doesn't work
    varlabels = sorted(set(map(str.upper,dat.columns)))
    varlist = sorted(set(dat.columns))
    labels = dict()
    for key in varlist:
        for value in varlabels:
            if key.lower() == value.lower():
                labels[key]=value
    # create uppercase labels for all variable names
    dat.to_stata(statafile, write_index=False, variable_labels = labels)
    # Transfer dataset to Stata
    
    
def readsas(sasfile):
    """ readsas reads a sas data file into a pandas dataframe and
    converts the variable names to all lower case strings. 
    For columns imported as byte objects, it first attempts to 
    convert byte objects into literal strings using 'utf-8' and if
    it fails it uses 'latin-1' encoding. It replaces byte object
    columns with decoded literal strings.
    """
    dat = pd.read_sas(sasfile)
    dat.columns = map(str.lower,dat.columns)
    str_df = dat.select_dtypes([object])
    if str_df.empty == False:
        try:
            str_df = str_df.stack().str.decode('utf-8').unstack()
            print("Used UTF-8 to decode {}".format(sasfile))
            enc = 'utf-8'
        except:
            str_df = str_df.stack().str.decode('latin-1').unstack()
            print("Used latin-1 to decode {}".format(sasfile))
            enc = 'latin-1'
    for col in str_df:
        dat[col] = str_df[col]
    return dat

def sas2csv(sasfile,csvfile,index_out=False):
    """ readsas reads a sas data file into a pandas dataframe and
    converts the variable names to all lower case strings. 
    For columns imported as byte objects, it first attempts to 
    convert byte objects into literal strings using 'utf-8' and if
    it fails it uses 'latin-1' encoding. It replaces byte object
    columns with decoded literal strings.
    """
    dat = pd.read_sas(sasfile)
    dat.columns = map(str.lower,dat.columns)
    str_df = dat.select_dtypes([object])
    if str_df.empty == False:
        try:
            str_df = str_df.stack().str.decode('utf-8').unstack()
            print("Used UTF-8 to decode {}".format(sasfile))
            enc = 'utf-8'
        except:
            str_df = str_df.stack().str.decode('latin-1').unstack()
            print("Used latin-1 to decode {}".format(sasfile))
            enc = 'latin-1'
    for col in str_df:
        dat[col] = str_df[col]
    dat.to_csv(csvfile,index=index_out,encoding = enc)

def sas_import_csv(df,sas_date_fmt='yymmddn8.',filePath='',outName = 'X'):
    '''Takes a dataframe and prepares a data step to import the csv file to SAS.
    Example: ```import sys, pandas as pd, numpy as np
sys.path.append(r'G:\\My Drive\\Jupyter Notebooks\\Modules\\DatafileConversions')
from conversions import sas_import_csv

dates = pd.date_range(start='1/1/2018', periods=6)
df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list("ABCD"))
df['E'] = "some string"
df = df.reset_index().rename(columns = {'index':'Date'})
f = r'C:\\Users\\flakej\\example.csv'
c = sas_import_csv(df,filePath=f)
df.to_csv(f,index=False)

f = r'C:\\Users\\flakej\\Dropbox\\Research\\BFP - Predict Rec Revisions\\Data\\Parsed_finbert_20210816.csv'
df2 = pd.read_csv(f,low_memory=False,parse_dates=['PubDate','Datetime'])
c2 = sas_import_csv(df2,filePath=f)```
    '''
    opening = f"%let infile = '{filePath}';\ndata {outName}; %let _EFIERR_ = 0; /* set the ERROR detection macro variable */ \ninfile &infile delimiter = ',' MISSOVER DSD TRUNCOVER lrecl=32767 firstobs=2 ;"
    inp = 'input '
    fmt = 'format '
    infmt = 'informat '
    closing = "if _ERROR_ then call symputx('_EFIERR_',1);  /* set ERROR detection macro variable */\nrun;"
    measurer = np.vectorize(len)
    dfLen = measurer(df.values.astype(str)).max(axis=0)
    for l,col in zip(dfLen,df.columns):
        if is_object(df[col]): inp = inp + f'{col} :${l}. '
        elif is_datetime(df[col]): 
            inp = inp + f'{col} '
            fmt = fmt + f'{col} {sas_date_fmt} '
            infmt = infmt + f'{col} yymmdd10. '
        else: inp = inp + f'{col} '
    return f'{opening} {inp} ;\n{fmt} ;\n{infmt} ;\n{closing}'