import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import torch
import torchvision
#from skleran.neural_network import MLPRegressor


torch.manual_seed(0)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)

# Leemos los datos
D = np.genfromtxt( "creditcard.csv", delimiter=',', skip_header=1, usecols=(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30), dtype=np.float32)
print( D.shape )

# De 0 a 27 son las características


#print(A.shape)
# Como el autoencoder es una red neuronal, se tienen
# que normalizar los datos

rows=np.where( D[:,29]==0)
normales = D[rows]
A = StandardScaler().fit_transform( normales[:,28].reshape(-1,1) )
print(A.shape)
normales[:,28] = A[:,0]
print( normales.shape )

# 80% de los datos para entrenamiento
# 20% para prueba
X_entrena, X_prueba = train_test_split(normales[:,0:29], test_size=0.2, random_state=42)
#print(X_entrena.shape)

# El autoencoder se está entrenando con los
# datos de las transacciones normales
Datos_tensor = torch.from_numpy(X_entrena)
Datos_tensor = Datos_tensor.to(device)

Datos_tensor_prueba = torch.from_numpy(X_prueba)
Datos_tensor_prueba = Datos_tensor_prueba.to(device)

# Capa de entrada: 29 
# Capa oculta:     20 
# Capa salida:     14

class AE(torch.nn.Module):
    def __init__(self):
        super().__init__()
         
       
        self.encoder = torch.nn.Sequential(
            torch.nn.Linear(29, 20),
            torch.nn.Tanh(),
            torch.nn.Linear(20, 14),
            torch.nn.ReLU(),
        )
         
        self.decoder = torch.nn.Sequential(
            torch.nn.Linear(14, 20),
            torch.nn.Tanh(),
            torch.nn.Linear(20, 29),
            torch.nn.Tanh()
        )
 
    def forward(self, x):
        encoded = self.encoder(x)
        decoded = self.decoder(encoded)
        return decoded


model = AE()
model.to(device)
#print("Prueba")
#print(Datos_tensor[0].shape)

#loss_function = torch.nn.MSELoss()
loss_function = torch.nn.CrossEntropyLoss()
#optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
optimizer = torch.optim.Adam(model.parameters())
#optimizer = torch.optim.RMSprop(model.parameters())
epochs = 100
batch_size = 32
losses = []
print("Inicio del entrenamiento")

for epoch in range(epochs):
    print("Epoca: ", epoch)
    for i in range(0, Datos_tensor.shape[0], batch_size):
        def closure():
            global loss
            #print("Hola")
            optimizer.zero_grad()

            output = model(Datos_tensor[i:i+batch_size])
            loss = loss_function(output, Datos_tensor[i:i+batch_size])
            loss.backward()
            return loss
        optimizer.step(closure)
        #optimizer.step()
        
        # Storing the losses in a list for plotting
        e = loss.to('cpu').detach().numpy()
    #print(e)
        losses.append(e)
    print(e)
    #print(loss)
print("Fin del entrenamiento")

torch.save(model.state_dict(), "adam00.pth")

#plt.style.use('fivethirtyeight')
plt.xlabel('Iterations')
plt.ylabel('Loss')
# Plotting the last 100 values
#print(type (losses[0]) )
plt.plot(losses)
plt.show()
Datos_tensor.shape[0]
model.eval()
'''
for i in range(5):
    output = model(Datos_tensor_prueba[i])
    print(output)
    print(Datos_tensor_prueba[i])
'''
