from random import uniform
from random import randint
from random import sample

def generateARandomPermutation(n):
    perm = [i for i in range(n)]
    pos1 = randint(0, n - 1)
    pos2 = randint(0, n - 1)
    perm[pos1], perm[pos2] = perm[pos2], perm[pos1]
    return perm

'''
Class for the chromosome representation
'''
class Chromosome:

    def __init__(self, problParam = None):
        self.__problParam = problParam
        # self.__repres = sample(range(-1, problParam['noNodes']-1), problParam['noNodes'])
        self.__repres = generateARandomPermutation(problParam['noNodes'])
        self.__fitness = 0.0
    
    @property
    def repres(self):
        return self.__repres
    
    @property
    def fitness(self):
        return self.__fitness 
    
    @repres.setter
    def repres(self, l = []):
        self.__repres = l 
    
    @fitness.setter 
    def fitness(self, fit = 0.0):
        self.__fitness = fit           
    
    '''
    The crossover operation between two chromosomes

    In: c - other chromosome

    Out: offspring - the newborn chromosome
    '''
    def crossover(self, c):
        pos1 = randint(0, self.__problParam['noNodes'] - 2)
        pos2 = randint(0, self.__problParam['noNodes'] - 2)
        if (pos2 < pos1):
            pos1, pos2 = pos2, pos1 

        newrepres = [-1] * len(self.__repres)
        for i in range(pos1, pos2 + 1):
            newrepres[i] = self.__repres[i]
        
        pos = pos2+1
        posc = pos2+1
        if(pos == len(self.__repres)):
            pos = 0
        if (posc == len(self.__repres)):
            posc = 0

        while(newrepres[pos] == -1):
            while(c.repres[posc] in newrepres):
                posc+=1
                if(posc == len(self.__repres)):
                    posc = 0
            newrepres[pos] = c.repres[posc]
            pos += 1
            if(pos == len(self.__repres)):
                pos = 0


        # k = 0
        # newrepres = self.__repres[pos1 : pos2]
        # for el in c.__repres[pos2:] +c.__repres[:pos2]:
        #     if (el not in newrepres):
        #         if (len(newrepres) < self.__problParam['noNodes'] - pos1):
        #             newrepres.append(el)
        #         else:
        #             newrepres.insert(k, el)
        #             k += 1

        offspring = Chromosome(self.__problParam)
        offspring.repres = newrepres
        return offspring
    
    '''
    Description: mutates the chromosome
    '''
    def mutation(self):
        # insert mutation
        pos1 = randint(0, self.__problParam['noNodes'] - 1)
        pos2 = randint(0, self.__problParam['noNodes'] - 1)
        if (pos2 < pos1):
            pos1, pos2 = pos2, pos1
        el = self.__repres[pos2]
        del self.__repres[pos2]
        self.__repres.insert(pos1 + 1, el)
        
    def __str__(self):
        return '\nChromosome: ' + str(self.__repres) + ' has fit: ' + str(self.__fitness)
    
    def __repr__(self):
        return self.__str__()
    
    def __eq__(self, c):
        return self.__repres == c.__repres and self.__fitness == c.__fitness