from skga.hbrkga.brkga_mp_ipr.exploitation_method import ExploitationMethod
from skga.hbrkga.nn_decoder_PT import NNDecoder
import random

class RandomWalk(ExploitationMethod):
    def __init__(self, decoder: NNDecoder, e = 0.3, steps = 3, percentage = 1.0):

        self._decoder = decoder
        self._e = e
        self._steps = steps
        self._solutions = []
        self._percentage = percentage

    def initialize(self, initial_populations):
        # Add the individuals to the list of explored solutions
        for population in initial_populations:
            for individual_idx in range(len(population.chromosomes)):
                individual = population.chromosomes[individual_idx]
                value = [x for (x,y) in population.fitness if y == individual_idx][0]
                self._solutions.append((individual, value))
                #print(f"Appended {self._solutions[-1]}\n")
        return initial_populations

    def new_chromosomes(self, new_chromosomes: BaseChromosome):
        # Replace clones with suggestions from the optimizer
        for chromosome_idx in range(len(new_chromosomes)):
            if new_chromosomes[chromosome_idx] in [x for (x, value) in self._solutions]:
                #print(f"Repeated chromosome -> {new_chromosomes[chromosome_idx]}")
                new_chromosomes[chromosome_idx] = self.suggest_random_chromosome()
                #print(f"Replaced by -> {new_chromosomes[chromosome_idx]}\n")
        return new_chromosomes

    def suggest_random_chromosome(self):
        # Suggest new random chromosome for new_chromosomes()
        new_chromosome = []
        for _ in range(5):
            new_chromosome.append(random.uniform(0, 1))

        if new_chromosome in [x for (x, value) in self._solutions]:
            new_chromosome = self.suggest_random_chromosome()

        return new_chromosome

    def new_population(self, new_population: Population):
        for individual_idx in range(len(new_population.chromosomes)):
            if not new_population.chromosomes[individual_idx] in [x for (x, value) in self._solutions]:
                self._solutions.append((new_population.chromosomes[individual_idx], [x for (x,y) in new_population.fitness if y == individual_idx][0]))
                #print(f"Appended {self._solutions[-1]}\n")
            for _ in range(self._steps):
                new_individual = []
                for chromosome_idx in range(len(new_population.chromosomes[individual_idx])):
                    new_chromosome = new_population.chromosomes[individual_idx][chromosome_idx] + random.uniform(-self._e, self._e)
                    if new_chromosome < 0:
                        new_chromosome = 0
                    elif new_chromosome > 1:
                        new_chromosome = 1
                    new_individual.append(new_chromosome)
                new_value = self._decoder.decode(chromosome=new_individual, rewrite=False, percentage=self._percentage)
                self._solutions.append((new_individual, new_value))
                #print(f"Appended {self._solutions[-1]}\n")
                #print(f"Old chromosome -> {new_population.chromosomes[individual_idx]}")
                #print(f"New chromosome -> {new_individual}")
                for fitness_idx in range(len(new_population.fitness)):
                    if new_population.fitness[fitness_idx][1] != individual_idx:
                        continue
                    #print(f"Old value = {new_population.fitness[fitness_idx][0]}")
                    #print(f"New value = {new_value}\n")
                    if new_population.fitness[fitness_idx][0] < new_value:
                        new_population.chromosomes[individual_idx] = new_individual
                        new_population.fitness[fitness_idx] = (new_value, individual_idx)
                    break
                
        return new_population