import time
from basic_ml_functions import *
from ml_training_functions import *
import matplotlib.pyplot as plt


# Create Train and Test features using file data
inputDir='.'
inputFileName = 'Cori2020_VASP_OneWeek_JobsData.csv' 
print('Input File Name:',inputFileName)
features, featuresAll = get_features_df(os.path.realpath(os.path.join(os.getcwd(), inputDir, inputFileName)), 'hardware')
labels, feature_list, features = convert_feature_df_to_vars(features)
train_features, test_features, train_labels, test_labels = create_train_test_sets(features, labels)

# Multi-variate Linear Regression
print('\n*** Multi-variate Linear Regression ***')
timeStartLR = time.monotonic()
lr = train_using_LinearRegression(train_features, train_labels)
timeStepLR = time.monotonic()
predictionsLR, errorsLR, errorsAbsLR, accuracyLR  = make_predictions(lr, test_features, test_labels)
timeEndLR = time.monotonic()
print('Time taken for training: '+str(round(timeStepLR - timeStartLR, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndLR - timeStepLR, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndLR - timeStartLR, 2))+'s')

# LASSO Regression
print('\n*** LASSO Regression ***')
timeStartLasso = time.monotonic()
lasso = train_using_Lasso(train_features, train_labels)
timeStepLasso = time.monotonic()
predictionsLasso, errorsLasso, errorsAbsLasso, accuracyLasso = make_predictions(lasso, test_features, test_labels)
timeEndLasso = time.monotonic()
print('Time taken for training: '+str(round(timeStepLasso - timeStartLasso, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndLasso - timeStepLasso, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndLasso - timeStartLasso, 2))+'s')

# ElasticNet Regression
print('\n*** ElasticNet Regression ***')
timeStartENet = time.monotonic()
enet = train_using_ElasticNet(train_features, train_labels)
timeStepENet = time.monotonic()
predictionsENet, errorsENet, errorsAbsENet, accuracyENet = make_predictions(enet, test_features, test_labels)
timeEndENet = time.monotonic()
print('Time taken for training: '+str(round(timeStepENet - timeStartENet, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndENet - timeStepENet, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndENet - timeStartENet, 2))+'s')

# K-Nearest Neighbors Regression
print('\n*** K-Nearest Neighbors Regression ***')
timeStartKNN = time.monotonic()
knn = train_using_KNeighborsRegressor(train_features, train_labels)
timeStepKNN = time.monotonic()
predictionsKNN, errorsKNN, errorsAbsKNN, accuracyKNN = make_predictions(knn, test_features, test_labels)
timeEndKNN = time.monotonic()
print('Time taken for training: '+str(round(timeStepKNN - timeStartKNN, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndKNN - timeStepKNN, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndKNN - timeStartKNN, 2))+'s')

# Support Vector Regression
print('\n*** Support Vector Regression ***')
timeStartSVR = time.monotonic()
svr = train_using_SVR(train_features, train_labels)
timeStepSVR = time.monotonic()
predictionsSVR, errorsSVR, errorsAbsSVR, accuracySVR = make_predictions(svr, test_features, test_labels)
timeEndSVR = time.monotonic()
print('Time taken for training: '+str(round(timeStepSVR - timeStartSVR, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndSVR - timeStepSVR, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndSVR - timeStartSVR, 2))+'s')

# Decision Tree Regression
print('\n*** Decision Tree Regression ***')
timeStartDT = time.monotonic()
dt = train_using_DecisionTreeRegressor(train_features, train_labels)
timeStepDT = time.monotonic()
predictionsDT, errorsDT, errorsAbsDT, accuracyDT = make_predictions(dt, test_features, test_labels)
timeEndDT = time.monotonic()
print('Time taken for training: '+str(round(timeStepDT - timeStartDT, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndDT - timeStepDT, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndDT - timeStartDT, 2))+'s')

# Ada Boost Regression
print('\n*** Ada Boost Regression ***')
timeStartAB = time.monotonic()
ab = train_using_AdaBoostRegressor(train_features, train_labels)
timeStepAB = time.monotonic()
predictionsAB, errorsAB, errorsAbsAB, accuracyAB = make_predictions(ab, test_features, test_labels)
timeEndAB = time.monotonic()
print('Time taken for training: '+str(round(timeStepAB - timeStartAB, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndAB - timeStepAB, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndAB - timeStartAB, 2))+'s')

#  Gradient Boosting Regression
print('\n*** Gradient Boosting Regression ***')
timeStartGB = time.monotonic()
gb = train_using_GradientBoostingRegressor(train_features, train_labels)
timeStepGB = time.monotonic()
predictionsGB, errorsGB, errorsAbsGB, accuracyGB = make_predictions(gb, test_features, test_labels)
timeEndGB = time.monotonic()
print('Time taken for training: '+str(round(timeStepGB - timeStartGB, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndGB - timeStepGB, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndGB - timeStartGB, 2))+'s')

#  Random Forests Regression
print('\n*** Random Forests Regression ***')
timeStartRF = time.monotonic()
rf = train_using_RandomForestRegressor(train_features, train_labels)
timeStepRF = time.monotonic()
predictionsRF, errorsRF, errorsAbsRF, accuracyRF = make_predictions(rf, test_features, test_labels)
timeEndRF = time.monotonic()
print('Time taken for training: '+str(round(timeStepRF - timeStartRF, 2))+'s')
print('Time taken for prediction: '+str(round(timeEndRF - timeStepRF, 2))+'s')
print('Time taken for training and prediction: '+str(round(timeEndRF - timeStartRF, 2))+'s')

lst_modelNames = ['Muti-variate Linear', 'Lasso', 'ElasticNet', 'KNN', 'Support Vector', 'Decision Tree', 'Ada Boost', 'Gradient Boosting', 'Random Forest']

lstOFLists_Errors = [errorsLR, errorsLasso, errorsENet, errorsKNN, errorsSVR, errorsDT, errorsAB, errorsGB, errorsRF]
df_Errors = pd.DataFrame.from_dict(dict(zip(lst_modelNames, lstOFLists_Errors)))

lst_Accuracy = [accuracyLR, accuracyLasso, accuracyENet, accuracyKNN, accuracySVR, accuracyDT, accuracyAB, accuracyGB, accuracyRF]
dict_Accuracy = dict(zip(lst_modelNames, lst_Accuracy))

lst_timeLabels = ['Start', 'Step', 'End']
lst_timeStart = [timeStartLR, timeStartLasso, timeStartENet, timeStartKNN, timeStartSVR, timeStartDT, timeStartAB, timeStartGB, timeStartRF]
lst_timeStep = [timeStepLR, timeStepLasso, timeStepENet, timeStepKNN, timeStepSVR, timeStepDT, timeStepAB, timeStepGB, timeStepRF]
lst_timeEnd = [timeEndLR, timeEndLasso, timeEndENet, timeEndKNN, timeEndSVR, timeEndDT, timeEndAB, timeEndGB, timeEndRF]

df_Time = pd.DataFrame.from_dict(dict(zip(lst_timeLabels, [lst_timeStart, lst_timeStep, lst_timeEnd])))
df_Time['Model'] = lst_modelNames
df_Time['Training'] = df_Time['Step'] - df_Time['Start']
df_Time['Prediction'] = df_Time['End'] - df_Time['Step']
df_Time['Total'] = df_Time['End'] - df_Time['Start']

plt.style.use('default')
plt.rc('font', family='serif')
fig, axis = plt.subplots()

dict_Accuracy = dict(sorted(dict_Accuracy.items(), key=lambda item: item[1]))
plt.bar(dict_Accuracy.keys(), dict_Accuracy.values(), edgecolor='black', color='coral')
plt.ylabel('Accuracy (%)', fontweight='bold')
plt.xticks(rotation=90)
plt.ylim(85,100)

axis.set_axisbelow(True)
plt.grid(linestyle='--')

outputDir = '.'
outputFileName = 'Figure4_MLModel_Accuracy_Comparison_VASP_OneWeek_HardwareVars.png'
print('Output File Name:', outputFileName)
plt.savefig(os.path.realpath(os.path.join(os.getcwd(), outputDir, outputFileName)), bbox_inches="tight", dpi=300)
plt.close()