import matplotlib.pyplot as plt
import numpy as np
import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=False, transform=transform) # descargar el dataset de entrenamiento
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2) # carga el dataset de entrenamiento

testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=False, transform=transform) # descargar el dataset de prueba

testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2) # carga el dataset de prueba


classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')


def imgshow(img):
	img = img / 2 + 0.5 # unnormalize
	npimg = img.numpy()
	plt.imshow(np.transpose(npimg, (1, 2, 0)))
	plt.show()

class Net(nn.Module):
	def __init__(self):
		super(Net, self).__init__()
		self.conv1 = nn.Conv2d(3, 6, 5)
		self.pool = nn.MaxPool2d(2, 2)
		self.conv2 = nn.Conv2d(6, 16, 5)
		self.fc1 = nn.Linear(16 * 5 * 5, 120)
		self.fc2 = nn.Linear(120, 84)
		self.fc3 = nn.Linear(84, 10)

	def forward(self, x):
		x = self.pool(F.relu(self.conv1(x)))
		x = self.pool(F.relu(self.conv2(x)))
		x = x.view(-1, 16 * 5 * 5)
		x = F.relu(self.fc1(x))
		x = F.relu(self.fc2(x))
		x = self.fc3(x)
		return x
		
if __name__ == '__main__':
	net = Net()
	criterion = nn.CrossEntropyLoss()
	optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

	print("Starting classifier CNN Training")
	for epoch in range(3):  # loop over the dataset multiple times
		running_loss = 0.0
		for i, data in enumerate(trainloader, 0):
			# get the inputs; data is a list of [inputs, labels]
			inputs, labels = data


			# zero the parameter gradients
			optimizer.zero_grad()

			# forward + backward + optimize
			outputs = net(inputs)
			loss = criterion(outputs, labels)
			loss.backward()
			optimizer.step()

			# print statistics
			running_loss += loss.item()
			if i % 2000 == 1999:    # print every 2000 mini-batches
				print('[%d, %5d] loss: %.3f' %
					  (epoch + 1, i + 1, running_loss / 2000))
				running_loss = 0.0

	print('Finished Training')

	correct = 0
	total = 0
	with torch.no_grad():
		for data in testloader:
			images, labels = data
			outputs = net(images)
			_, predicted = torch.max(outputs.data, 1)
			total += labels.size(0)
			correct += (predicted == labels).sum().item()

	print('Accuracy of the network on the 10000 test images to assign labels: %d %%' % (100 * correct / total))


	########
	model=torch.load('imagenes_test6.pt') # this file include all the data generated by the GAN.py
	
	
	fakeLabels = []  
	fakeList = model[-1] #this is the last set of picture generate by the GAN
	outputs = net(fakeList) 
	_, predicted = torch.max(outputs, 1)

	for elem in predicted:
		fakeLabels.append(elem)

	cnn2 = Net()
	criterion = nn.CrossEntropyLoss()
	optimizer = optim.SGD(cnn2.parameters(), lr=0.001, momentum=0.9)

	print("Starting CNN2 Training with fakes images")
	for epoch in range(3):  # loop over the dataset multiple times
		running_loss = 0.0
		for i, data in enumerate(trainloader, 0):
			# get the inputs; data is a list of [inputs, labels]
			inputs, labels = data

			# zero the parameter gradients
			optimizer.zero_grad()

			# forward + backward + optimize
			outputs = cnn2(inputs)
			loss = criterion(outputs, labels)
			loss.backward()
			optimizer.step()

			# print statistics
			running_loss += loss.item()
			if i % 2000 == 1999:    # print every 2000 mini-batches
				print('[%d, %5d] loss: %.3f' %
						(epoch + 1, i + 1, running_loss / 2000))
				running_loss = 0.0

		
		inputs = fakeList
		labels = fakeLabels

		# zero the parameter gradients
		optimizer.zero_grad()

		# forward + backward + optimize
		outputs = cnn2(inputs)
		loss = criterion(outputs,torch.tensor(labels))
		loss.backward()
		optimizer.step()
		running_loss += loss.item()
		if i % 2000 == 1999:    # print every 2000 mini-batches
			print('[%d, %5d] lossFake: %.3f' %
					(epoch + 1, i + 1, running_loss / 2000))
			running_loss = 0.0
	print('Finished Training')

	correct = 0
	total = 0
	with torch.no_grad():
		for data in testloader:
			images, labels = data
			outputs = net(images)
			_, predicted = torch.max(outputs.data, 1)
			total += labels.size(0)
			correct += (predicted == labels).sum().item()

	print('Accuracy of the network after train with fake and original data: %d %%' % (100 * correct / total))


			
