import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from sklearn import metrics 
from sklearn.metrics import *
from sklearn.neural_network import MLPClassifier
from sklearn import svm
from sklearn.neighbors import KNeighborsClassifier
from time import time
import sys
from ctgan import CTGAN
######################Gererate Data######################
ModelName = "HHo"
CFName = "KNN"
# parameter
N    = 10   # number of solutions / population size ( for all methods )
T    = 100   # maximum number of generations
UserID = 49
fac = 2

fName = 'output/'+ModelName+' '+CFName+'U'+str(UserID)+'F'+str(fac)+'.txt'
sys.stdout = open(fName, 'w')
#######################################################
dbName = str(UserID)+'.csv'
ds = pd.read_csv(dbName)         
#print(ds.head())
ds=  np.array(ds)
########################################
synthetic_data = pd.read_csv("GDuser"+str(UserID)+".csv")    
synthetic_data =  np.array(synthetic_data)
#######################################################
dbName = 'keystroke_51_DataSet_Processd_28-5-2023A.csv'

#print(dbName)
###############################
###Read data and preprocessing
################################
ds1=pd.read_csv(dbName)         
#print(ds.head())
print(np.shape(ds1))

ds1 = ds1.dropna(how='any')    #to drop if any value in the row has a nan

XX= ds1.iloc[0:,:-1].values
print ('X shape is ' , XX.shape)
feat = 0
feat = np.array(XX)


label =ds1.iloc[0:,-1].values
print ('Y shape is ' , label.shape)
label = np.array(label[:], dtype=int)
########################################
l1 = 18
l2 = l1*20
l = l1 + l2
n = int(20 * l / 100)
m =  l - n
print(m,"   ",n)


ytrain =  np.zeros(m*fac)
xtrain = np.zeros((m*fac,53))

ytest =  np.zeros(n*fac)
xtest = np.zeros((n*fac,53))
c = 0 
k = 0

#######################Put User Data 
############1. orginal 
n1 = int(20 * l1 / 100)
m1 =  l1 - n1

for i in range(l1) : 
    if c < m1 :
             ytrain[c] = 1
             xtrain[c]  =  ds[i]
             c = c + 1 
    if c >= m1 and k < n1:
              ytest[k] = 1
              xtest[k]  =  ds[i]
              k = k + 1    

############2. generated
n2 = int(20 * l2 / 100)
m2 =  l2 - n2

for i in range(l2) : 
    if c < m2 :
             ytrain[c] = 1
             xtrain[c]  =  synthetic_data[i]
             c = c + 1 
    if c >= m2 and k < n2:
              ytest[k] = 1
              xtest[k]  =  synthetic_data[i]
              k = k + 1 

########################################
for i in range(len(label)) :
    if c < fac*m :
        if label[i] != UserID :
             xtrain[c]  =  feat[i]
             c = c + 1 
    if c >= fac*m and k < fac*n:
         if label[i] != UserID :
              xtest[k]  =  feat[i]
              k = k + 1         
    i = i + 10

print(c,"  ",k)    
print(ytrain,"  ",xtrain)   
print(ytest,"  ",xtest)  
   
c = 0 
k = 0 

label =  np.zeros(l*fac)
feat = np.zeros((l*fac,53))

for i in range(fac*l) :
    if i < m*fac :
        feat[i] =  xtrain[c]
        label[i] = ytrain[c]
        c = c + 1
    else : 
        feat[i] = xtest[k]  
        label[i] = ytest[k]
        k = k +1 
        
print(feat,"  ",label,"  ",len(label))        
# newLabel = np.asarray(newLabel)     
# #print(newLabel,"  ",len(newLabel))

print(l,"  ",l*fac,"  ",len(label))

#########################################
#############################################
# split data into train & validation (70 -- 30)
# xtrain, xtest, ytrain, ytest = train_test_split(feat, newLabel, test_size=0.3, stratify=newLabel)
fold = {'xt':xtrain, 'yt':ytrain, 'xv':xtest, 'yv':ytest}

k    = 5     # k-value in KNN
####################################################
if ModelName == "HHo" :
    opts = {'k':k, 'fold':fold, 'N':N, 'T':T , 'CFName':CFName}
    from FS.hho import jfs   # change this to switch algorithm 
elif  ModelName == "GWO" :
    opts = {'k':k, 'fold':fold, 'N':N, 'T':T,'CFName':CFName}
    from FS.gwo import jfs 
elif  ModelName == "GA" :
    CR   = 0.8
    MR   = 0.01
    opts = {'k':k, 'fold':fold, 'N':N, 'T':T, 'CR':CR, 'MR':MR,'CFName':CFName}
    from FS.ga import jfs 
elif  ModelName == "PSO" :
    w    = 0.9
    c1   = 2
    c2   = 2
    opts = {'k':k, 'fold':fold, 'N':N, 'T':T, 'w':w, 'c1':c1, 'c2':c2,'CFName':CFName}
    from FS.pso import jfs 
elif  ModelName == "WOA" :
    b  = 1    # constant
    opts = {'k':k, 'fold':fold, 'N':N, 'T':T, 'b':b,'CFName':CFName}  
    from FS.woa import jfs 

#####################################################    
for i in range(5):
    
        #print("\n iteration ",i)  
        # perform feature selection
        t0 = time()
        fmdl = jfs(feat, label, opts)
        sf   = fmdl['sf']
        
        # model with selected features
        num_train = np.size(xtrain, 0)
        num_valid = np.size(xtest, 0)
        x_train   = xtrain[:, sf]
        y_train   = ytrain.reshape(num_train)  # Solve bug
        x_valid   = xtest[:, sf]
        y_valid   = ytest.reshape(num_valid)  # Solve bug
        
        ##########################Model
        if CFName == "KNN" :
                  mdl       = KNeighborsClassifier(n_neighbors = k) 
        elif CFName == "SVM" :
                  mdl = svm.SVC() 
        elif CFName == "RandomForest" :
                  from sklearn.ensemble import RandomForestClassifier
                  mdl = RandomForestClassifier() 
        ######################################################## 
        mdl.fit(x_train, y_train)
        fit_time = time()- t0
        #print("time %.3f s" %fit_time)
        # accuracy
        y_pred    = mdl.predict(x_valid)
        Acc       = np.sum(y_valid == y_pred)  / num_valid
        #print("Accuracy:", 100 * Acc)
        
        # # # # # ####### metrics to evaluate classifier
        # # ############ confusion matrix
        
        cm = confusion_matrix(y_valid, y_pred) 
        #print('Confusion Matrix : \n', cm)
               
        ################ Accuracy
        accuracy_score=metrics.accuracy_score(y_valid, y_pred)
        #print("accuracy_score=",accuracy_score)
              
        ################################# f1_score
        from sklearn.metrics import f1_score
        f1_sco=f1_score(y_valid, y_pred)
        #print('f1_score=',f1_sco)        
         
        ########################### roc_auc_score  
        roc_auc = metrics.roc_auc_score(y_valid,y_pred)
        #print("roc_auc=",roc_auc)
                    
        ################ precision
        PrecisionScore = precision_score(y_valid, y_pred)
        #print(" Precision:",PrecisionScore)
              
        ################Recall       
        Rec=recall_score(y_valid, y_pred)
        #print("recall:", Rec)
               
        ###################specificity
        spec = cm[1,1]/(cm[1,1]+cm[1,0])
        #print('Specificity : ', spec)
                
        ############# kappa
        kappa=cohen_kappa_score(y_valid, y_pred)
        #print('kappa:', kappa)
                
        
       #######################################
       # import numpy as np
        from sklearn.metrics import roc_curve

        fpr, tpr, threshold = roc_curve(y_valid, y_pred, pos_label=1)
        fnr = 1 - tpr
        eer_threshold = threshold[np.nanargmin(np.absolute((fnr - fpr)))]
       
        # Note that this gets you the threshold at which the EER occurs not, the EER. The EER is defined as FPR = 1 - PTR = FNR. Thus to get the EER (the actual error rate) you could use the following:
        EER1 = fpr[np.nanargmin(np.absolute((fnr - fpr)))]
       # as a sanity check the value should be close to

        EER2 = fnr[np.nanargmin(np.absolute((fnr - fpr)))]
       
       
        # number of selected features
        num_feat = fmdl['nf']
        #print("Feature Size:", num_feat)
        
        print(fit_time,"  ",num_feat,"  ",100 * Acc,"  ",f1_sco,"  ",
              roc_auc,"   ",PrecisionScore,"   ",Rec,"  ",spec
              ,"   ",kappa,"  ",fpr,"  ",fnr,"  ",EER1,"  ",EER2)
        # plot convergence
        curve   = fmdl['c']
        curve   = curve.reshape(np.size(curve,1))
        x       = np.arange(0, opts['T'], 1.0) + 1.0
        
        fig, ax = plt.subplots(figsize=(20,20))
        ax.plot(x, curve, 'o-')
        ax.set_xlabel('Number of Generations',fontsize=20)
        ax.set_ylabel('Fitness',fontsize=20)
        ax.set_title(ModelName + '  ' + CFName,fontsize=20)
        ax.grid()
        plt.show()
        fig.savefig('image/'+ModelName+CFName+'U'+str(UserID)+'F'+str(fac)+"i"+str(i)+'.png',bbox_inches='tight', 
                    dpi=250)




