#%% LOAD REQUIRED LIBRARIES

from pathlib import Path

import tensorflow
import keras

from keras.preprocessing import image
from keras.utils import np_utils

import numpy as np
import joblib
from PIL import Image

#%% LOAD KERAS MOBILENET LIBRARY

from keras.applications import MobileNet
from keras.applications.mobilenet import decode_predictions
from keras.applications.mobilenet import preprocess_input

#%% #%% LOAD INPUT IMAGES

fs180_path = Path("DATA-RAW")/"fs180"
kf_path = Path ("DATA-RAW")/"kf"
nfs_path = Path("DATA-RAW")/"nfs"
ollie_path = Path("DATA-RAW")/"ollie"
ps_path = Path("DATA-RAW")/"ps"

targetimage = "*.jpg"

images = []
labels = []

new_width  = 224
new_height = 224

    #Load all the fs180 trick images
for img in fs180_path.glob(targetimage):
    # load image from disk
    img = image.load_img(img)
    
    img = img.resize((new_width, new_height), Image.ANTIALIAS)
    #convert intu numoy array
    image_array = image.img_to_array(img)
    
    #add the image to the list of images
    images.append(image_array)
    
    #for each fs180 image, the expected value shoud be 0
    labels.append(0)
    
    #Load all the kickflip trick images
for img in kf_path.glob(targetimage):
    # load image from disk
    img = image.load_img(img)
    
    img = img.resize((new_width, new_height), Image.ANTIALIAS)
    
    #convert intu numoy array
    image_array = image.img_to_array(img)
    
    #add the image to the list of images
    images.append(image_array)
    
    #for each kf image, the expected value shoud be 1
    labels.append(1)
    
    #Load all the nfs trick images
for img in nfs_path.glob(targetimage):
    # load image from disk
    img = image.load_img(img)
    
    img = img.resize((new_width, new_height), Image.ANTIALIAS)
    
    #convert intu numoy array
    image_array = image.img_to_array(img)
    
    #add the image to the list of images
    images.append(image_array)
    
    #for each nfs image, the expected value shoud be 2
    labels.append(2)

    #Load all the ollie trick images
for img in ollie_path.glob(targetimage):
    # load image from disk
    img = image.load_img(img)
    
    img = img.resize((new_width, new_height), Image.ANTIALIAS)
    #convert intu numoy array
    image_array = image.img_to_array(img)
    
    #add the image to the list of images
    images.append(image_array)
    
    #for each ollie image, the expected value shoud be 3
    labels.append(3)

    #Load all the popshuvit trick images
for img in ps_path.glob(targetimage):
    # load image from disk
    img = image.load_img(img)
    
    img = img.resize((new_width, new_height), Image.ANTIALIAS)
    #convert intu numoy array
    image_array = image.img_to_array(img)
    
    #add the image to the list of images
    images.append(image_array)
    
    #for each popshuvit image, the expected value shoud be 4
    labels.append(4)

#Create a single numpy array with all the images loaded
x_val = np.array(images)

#convert all the labels to a numpy array
y_val = np.array(labels)
num_classes = 5
y_val = np_utils.to_categorical(y_val, num_classes)

#normalize data 0 to 1 range
x_val = preprocess_input(x_val)

#%% TRANSFER LEARNING

#load pretrained NN as feature extractor
pretrained_nn = MobileNet(weights="imagenet", include_top=False, input_shape=(new_width,new_height,3))
print('MobileNet')

#Extract features for each omage
features_x_val = pretrained_nn.predict(x_val)

features_flatten = features_x_val.reshape((features_x_val.shape[0],7*7*1024))

#save the array of extracted features into a file
joblib.dump(features_x_val, "RAW-MobileNet-x_val.dat")

#save the matching array of expected values to a file
joblib.dump(y_val, "RAW-MobileNet-y_val.dat")

#%% DATA SPLIITING FOR TRAIN-VALIDATION-TEST

features = features_flatten
lables = labels

from sklearn.model_selection import train_test_split

#First Split, splitting the data to 3:1 ratio.
X_train,X_test,y_train,y_test = train_test_split(features, labels, test_size=1/3,random_state=42,stratify=lables)

#Second split, splitting the test data into half, i.e., validation and test.
X_val,X_test,y_val,y_test = train_test_split(X_test, y_test, test_size=0.5,random_state=42,stratify=y_test)

#%% SVM HYPERPARAMETER TUNING USING GRIDSEARCH

from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC

def print_results(results):
    print('Best Param: {}\n'.format(results.best_params_))
    means = results.cv_results_['mean_test_score']
    stds = results.cv_results_['std_test_score']
    for mean, std, params in zip(means,stds, results.cv_results_['params']):
        print('{} (+/-{}) for {}'.format(round(mean,3),round(std*2,3),params))

svc=SVC()
parameters={
        'kernel':['linear', 'rbf', 'poly'],
        'gamma' : [0.1, 1, 10, 100],
        'C':[0.01, 0.1, 1, 10, 100],
        'degree' : [0, 1, 2, 3, 4, 5, 6]
        }

cv = GridSearchCV(svc,parameters,cv=3)

cv.fit(X_train,y_train)

print_results(cv)

cv.best_estimator_

joblib.dump(cv.best_estimator_,'RAW-MobileNet-SVM.pkl')

#%% TRAIN-VALIDATION-TEST OPTIMIZED HYPERPARAMETERS

from sklearn.metrics import classification_report
from sklearn.metrics import accuracy_score

from sklearn.svm import SVC
from sklearn.model_selection import cross_val_score
from sklearn.metrics import plot_confusion_matrix
import matplotlib.pyplot as plt

class_names = ['PS180','KF','NFS','OLLIE','PS']

# MANUAL INSERT OPTIMIZED HYPERPARAMETER
SVM1 = SVC(kernel='linear', C=0.01, gamma=0.1, degree=0)

scores = cross_val_score(SVM1, X_train, y_train, cv=3)
AC=round(scores.mean(),3)
stds=round(scores.std(),3)
print(AC,stds)

classifier = SVM1.fit(X_train, y_train)

print('           #############Train#############')
y_pred_tr = SVM1.predict(X_train)
tr_acc = round(accuracy_score(y_train,y_pred_tr),5)
print(classification_report(y_train,y_pred_tr))

print('           #############Validate#############')
y_pred_v = SVM1.predict(X_val)
v_acc = round(accuracy_score(y_val,y_pred_v),5)
print(classification_report(y_val,y_pred_v))

print('           #############Test#############')
y_pred_ts = SVM1.predict(X_test)
ts_acc = round(accuracy_score(y_test,y_pred_ts),5)
print(classification_report(y_test,y_pred_ts))

np.set_printoptions(precision=2)

# PLOT CONFUSION MATRIX
titles_options = [("MobileNet-RAW-SVM - Confusion Matrix", None),
                  ("MobileNet-RAW-SVM - Normalized Confusion Matrix", 'true')]

for title, normalize in titles_options:
    disp = plot_confusion_matrix(classifier, X_test, y_test,
                                 display_labels=class_names,
                                 cmap=plt.cm.Blues,
                                 normalize=normalize)
    disp.ax_.set_title(title)

    print(title)
    print(disp.confusion_matrix)
    plt.show()