# chromosome.py
# This file handles the chromosome construct for use in training binary word embeddings


# Internal Imports
from src.genetic_embedding.core.interface import fitness

# External Imports
import os
import random
import numpy

# Globals
chunk_size = 16


class Chromosome:
    # Constructor
    def __init__(self, vocab = {}, embed = "", dim = 32, fit = 0.0, max_embed_len = 0):
        if max_embed_len:
            self.max_embed_length = max_embed_len
        else:
            self.max_embed_length = int(len(vocab) * dim)
        if embed:
            self.embedding = str(embed)
        else:
            self.embedding = ""
            for i in range(int(self.max_embed_length / chunk_size)):
                temp = bin(random.getrandbits(chunk_size))[2:].rjust(chunk_size, "0")# This line doesn't randomize the numbers for some reason. Fix this
                self.embedding += temp
                #self.embedding += bin(random.getrandbits((2^chunk_size-1)-1))[2:].rjust(chunk_size, "0")
            '''print("Vocab Length:", len(vocab))
            print("Dimension:", dim)
            print("Embed Length:", len(self.embedding))'''
            #self.embedding = random.getrandbits((2^(dim * len(vocab))-1))
            #self.embedding = numpy.random.randint((2^dim)-1, size=len(vocab)).tolist()
            #self.embedding = numpy.random.randint(2, size=(len(vocab) * dim)).tolist()
        if fit:
            self.fitness = fit
        else:
            self.fitness = 0.0
        self.dimension = dim
        #print(len(self.convert_to_binary()))
        #temp = self.decode(vocab)
        #for key in temp:
        #    print(key, len(temp[key]))

    def __eq__(self, rh):
        return self.fitness == rh.fitness

    def __lt__(self, rh):
        return self.fitness < rh.fitness

    def __gt__(self, rh):
        return self.fitness > rh.fitness

    def __str__(self):
        #return str(self.chromosome) + " - " + str(self.fitness)
        return str(self.fitness)

    def __getitem__(self, key):
        return self.embedding[key]

    # Chromosome Decoder
    def decode(self, vocab, binary = True):
        embed = {}
        index = 0

        # Export the k-bit number associated with each word in the vocabulary, and return it as a dictionary
        '''for word in sorted(vocab):
            if binary:
                embed[word] = (self.convert_to_binary())[index * self.dimension : (index+1) * self.dimension]
            else:
                embed[word] = int((self.convert_to_binary())[index * self.dimension : (index+1) * self.dimension], 2)
            index += 1'''
        for word in sorted(vocab):# This decode loop kind of slows it down
            embed[word] = self.embedding[index * self.dimension : (index+1) * self.dimension]
            index+=1

        #del vocab

        return embed

    # Fitness Tracker
    def compute_fitness(self, vocab, cooccurrence={}, fitness_scheme = "analogy_bitcount_based", filepath = ""):
        self.fitness = fitness.compute_fitness(self, vocab, cooccurrence, fitness_scheme, filepath)
        #print(self.fitness, "-", len(self.convert_to_binary())/self.dimension)

    # Binary Converter
    def convert_to_binary(self):
        binary = bin(self.embedding)[2:]
        return binary.rjust(self.max_embed_length, "0")

    # Fragment Binary Converter
    '''def convert_fragment_to_binary(self, number, padding):
        binary = bin(number)[2:]
        return binary.rjust(self.dimension, str(padding))'''