import random

import numpy as np
from keras.datasets import imdb
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import Conv1D
from keras.layers.embeddings import Embedding
import keras.backend as K


# fix random seed for reproducibility
np.random.seed(7)




# input shape for LSTM: (batch_size, sequence_length, output_dim = 1), values are float
def generate_random_dataset(dataset_size, sequence_length):
    input = []
    output = []
    for i in range(dataset_size):
        multiplicator_coeff = random.uniform(0.8, 1.0)
        values = [[k * multiplicator_coeff / float(sequence_length)] for k in range(sequence_length + 1)]
        input_row = values[:-1]
        output_row = values[1:]
        input.append(input_row)
        output.append(output_row)
    return np.array(input, dtype="float32"), np.array(output, dtype="float32")



batch_size = 64
input_dataset_size = 100 * batch_size
output_dataset_size = 40 * batch_size
sequence_length = 20
X_train, y_train = generate_random_dataset(input_dataset_size, sequence_length)
X_test, y_test = generate_random_dataset(output_dataset_size, sequence_length)
print("X_train.shape = {}".format(X_train.shape))
print("y_train.shape = {}".format(y_train.shape))

# if False:
#     # load the dataset but only keep the top n words, zero the rest
#     top_words = 500
#     (X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=top_words)
#
#     max_review_length = 500
#     X_train = sequence.pad_sequences(X_train, maxlen=max_review_length)
#     X_test = sequence.pad_sequences(X_test, maxlen=max_review_length)
#
#     print(X_train.shape)
#     print(X_test.shape)
#     print(y_train.shape)
#     print(y_test.shape)

def get_accuracy_pred_func(accuracy_limit):
    def accuracy_pred(y_true, y_pred): return K.mean(K.clip(K.sign(1 - (K.abs(y_pred - y_true) / accuracy_limit)), 0.0, 1.0)) # ERROR lambda function mais qui a besoin d'etre nomme par keras/tensorflow
    return accuracy_pred




accuracy_limit = 0.05

model = Sequential()
# ERROR first layer of sequenetial must have batch_input_shape or input_shape set (this ont dont include the batch_size)
#model.add(Conv1D(1, 1, batch_input_shape = (batch_size, X_train.shape[1], X_train.shape[2])))
model.add(Dense(2, batch_input_shape = (batch_size, X_train.shape[1], X_train.shape[2]))) # @@@@@ ERROR dense is by default a linear activation function
#model.add(LSTM(2, return_sequences=True))
model.add(LSTM(2, return_sequences=True)) # batch_input_shape = (batch_size, X_train.shape[1], X_train.shape[2]), return_sequences=True)) # lstm_1, then lstm_2 etc..
# ERROR forgot return_sequences
# when 5 neurons, got: expected lstm_1 to have shape (20, 5) but got array with shape (20, 1)
model.add(LSTM(1, return_sequences = True))


#model.add(Dense(1, activation='sigmoid'))
model.compile(loss='mse', optimizer='adam', metrics=['accuracy', get_accuracy_pred_func(accuracy_limit)])
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=64)
print(model.summary())

scores = model.evaluate(X_test, y_test, verbose=0, batch_size=64) # ERROR evalutes set a batch_size default to 32, in my case I need 64
print(model.metrics_names)
print("Accuracy: %.2f%%" % (scores[1]*100))
print("Loss: %.2f" % (scores[0]*100))
print("Accuracy prediction: %.2f" % (scores[2]))





