#include "Header.h"

std::random_device rd;
std::mt19937 engine(rd());

std::uniform_int_distribution<int> randna(0, 1), randomonkey(0, POPULATION), randomutation(0, 100);

void initialize(std::vector<Primate>& monkeys, Text& book, Population& troop)
{
	// Text of reference
	std::ifstream file("book.txt");
	std::string str;

	while (std::getline(file, str))
	{
		while (str.length() < LENGTH)
			str += " ";

		book.lines.emplace_back(str);
	}
	book.editions = 0;

	// Reads the number of cycles
	std::ifstream read_log("log.txt");
	if (read_log.good())
		read_log >> book.editions;

	// Initialization of DNA
	std::string blank(LENGTH * 12, '0');
	for (int i = 0; i < POPULATION * 2; ++i)
		monkeys[i].dna = blank;
}

void restart(Text& book, Population& troop)
{
	book.current_line = 0;
	troop.better = false;

	remove("progress.txt");
	remove("progress_tmp.txt");
	remove("progress_light.txt");

	for (int i = 0; i < book.lines.size(); ++i)
	{
		remove(("stats/snapshot" + std::to_string(i) + ".txt").c_str());
		remove(("stats/basics" + std::to_string(i) + ".txt").c_str());
		remove(("stats/DNA" + std::to_string(i) + ".txt").c_str());
	}
}

/* Before searching for the next sentence, the DNA is randomized */
void reset(std::vector<Primate>& monkeys, Population& troop)
{
	troop.generations = 0;
	troop.fitness_reference = 0.0;

	for (int i = 0; i < POPULATION; ++i)
		for (int j = 0; j < LENGTH * 12; ++j)
			monkeys[i].dna[j] = randna(engine) + '0';
	
	remove("progress_line.txt");
}

void evaluation(std::vector<Primate>& monkeys, Text& book, Population& troop, bool& next)
{
	int i;

	for (i = 0; i < POPULATION; ++i)
	{
		monkeys[i].line.clear();
		monkeys[i].intelligence = 0.0;
		bool shift = false; char c;

		for (int j = 0; j < LENGTH; ++j)
		{
			auto expression = decode(monkeys[i].dna, j);
			c = keyboard(std::get<0>(expression), std::get<1>(expression), std::get<2>(expression));
			monkeys[i].line += c;
			
			// Comparison char by char between the sentence generated by the monkey and the expected output
			monkeys[i].intelligence += abs(monkeys[i].line[j] - book.lines[book.current_line][j]);
		}
		// The only information given to the monkeys: this score
		monkeys[i].intelligence = 1 / pow(monkeys[i].intelligence + 1, 2);
	}
	
	for (i = 0; i < POPULATION; ++i)
	{
		if (monkeys[i].intelligence == 1)
		{
			troop.better = true;
			troop.best = i;
			next = true;
			break;
		}
	}
}

void selection(std::vector<Primate>& monkeys, Text& book, Population& troop)
{
	int i;

	for (i = 0; i < POPULATION * 2; ++i)
		monkeys[i].best = false;

	std::vector<double> fitness;

	for (i = 0; i < POPULATION; ++i)
		fitness.emplace_back(monkeys[i].intelligence);

	sort(std::begin(fitness), std::end(fitness));

	for (i = 0; i < POPULATION; ++i)
		if (monkeys[i].intelligence == fitness.back())
		{
			monkeys[i].best = true;
			break;
		}

	troop.better = false;

	if (fitness.back() > troop.fitness_reference)
	{
		troop.fitness_reference = fitness.back();

		for (i = 0; i < POPULATION; ++i)
			if (monkeys[i].best)
			{
				troop.better = true;
				troop.best = i;
				log(monkeys, book, troop, 1);
				break;
			}
	}
}

void reproduction(std::vector<Primate>& monkeys)
{
	int i, j, k;
	std::vector<int> breeders;

	// ELITE: best monkey is cloned twice
	for (i = 0; i < POPULATION; ++i)
		if (monkeys[i].best == true)
		{
			breeders.emplace_back(i);
			monkeys[POPULATION].dna = monkeys[i].dna;
			monkeys[POPULATION + 1].dna = monkeys[i].dna;
			break;
		}

	// ROULETTE
	double total_fitness = 0.0;

	for (i = 0; i < POPULATION; ++i)
		total_fitness += monkeys[i].intelligence;

	std::uniform_real_distribution<double> roulette(0.0, total_fitness);

	double r;

	do
	{
		i = 0;
		r = roulette(engine);

		do
		{
			r -= monkeys[i].intelligence;
			++i;
		} while (r > 0);

		breeders.emplace_back(i - 1);

	} 
	while (breeders.size() < POPULATION);

	// 50/50 DNA CROSSOVER: 2 babies per couple
	for (i = 2; i < POPULATION; i+=2)
	{
		k = randomonkey(engine);

		for (j = 0; j < (LENGTH * 12) / 2; ++j)
		{
			monkeys[i + POPULATION].dna[j] = monkeys[breeders[i]].dna[j];
			monkeys[i + POPULATION + 1].dna[j] = monkeys[k].dna[j];
		}
		
		for (; j < LENGTH * 12; ++j)
		{
			monkeys[i + POPULATION].dna[j] = monkeys[k].dna[j];
			monkeys[i + POPULATION + 1].dna[j] = monkeys[breeders[i]].dna[j];
		}			
	}
	breeders.clear();
}

void mutation(std::vector<Primate>& monkeys)
{
	int i, j, k;

	for (i = 1; i < POPULATION; ++i)
		for (j = 0; j < LENGTH; ++j)
			if (randomutation(engine) < MUTATION)
				for (k = j * 12; k < j * 12 + 12; ++k)
					monkeys[i + POPULATION].dna[k] = randna(engine) + '0';

	// Baby monkeys become adults of next generation
	for (i = 0; i < POPULATION; ++i)
		monkeys[i].dna = monkeys[i + POPULATION].dna;
}