#import retrieve_articol
from compute_poisson import LoadPoisson
import time
import os
import sys
import datetime
from time import mktime
import marshal
import _mysql_exceptions
from sets import Set
from sets import Set
from vars import SYS_PATH
os.environ["DJANGO_SETTINGS_MODULE"] = "news.settings"
sys.path.append(SYS_PATH)

from django.conf import settings
from news.stiri.models import Articol, Cluster

from news.stiri.utils import get_cluster_keys, get_cluster_ids

class CreateCluster:
    """Clasa ce creaza clustere cu indecsii articolelor si cu cluster representatives corespunzatori,""" 
    
    def __init__(self, dict_poisson, cluster_limit, cluster_simil):
        self.dict_poisson = dict_poisson
        self.cluster_limit = cluster_limit
        self.cluster_simil = cluster_simil
        self.lista_clustere = self.genereaza_clustere(self.dict_poisson)
        
    def get_articole(self):
        #intoarce numarul total de articole folosit la calcularea Poisson
        today = datetime.date.today()
        start_date = today - datetime.timedelta(days=settings.NR_ZILE_ARTICOLE)
        return Articol.objects.filter(data__gte=start_date) 
    
    def genereaza_clustere(self, dict_poisson):
        lista_clustere = []
        articole = self.get_articole()
        dict_articole = self.get_cuvinte_for_articole(dict_poisson)
        articole = [articol for articol in articole if dict_articole.has_key(articol.id)]
        i = 0
        for articol in articole:
            cuvinte_articol = dict_articole[articol.id][:self.cluster_limit]
            contor = 0
            for cluster in lista_clustere:
                #daca sunt mai mult de cluster_simil comune intre cuvinte articol si
                #cuvintele reprezentative pentru cluster
                #articolele nou adaugate unui cluster trebuie nu trebuie sa fie mai vechi sai mai noi de 24 de ore
                dict_cuvinte_articol = self.get_dict_cuvinte(cuvinte_articol)
                dict_cuvinte_cluster = self.get_dict_cuvinte(cluster['lista_cuvinte'])
                distanta_cluster = self.compute_distanta_cluster(dict_cuvinte_cluster.keys(), dict_cuvinte_articol.keys())
                if distanta_cluster >= self.cluster_simil and abs(self.datetime_to_unixtime(articol.data) - cluster['data']) < 24*3600:
                    cuvinte_combinate = self.combina_lista_cuvinte(dict_cuvinte_cluster, dict_cuvinte_articol)
                    cluster['lista_cuvinte'] = cuvinte_combinate
                    cluster['articole_ids'].append(articol.id)
                    cluster['lungime'] += 1
                    contor += 1
                    break
            if contor == 0:
                #daca articolul nu s-a adaugat la nici un cluster, cream un nou cluster
                new_cluster = self.get_new_cluster(articol, cuvinte_articol)
                lista_clustere.append(new_cluster)
        return lista_clustere

    def get_dict_cuvinte(self, cuvinte):
        #"cuvinte" e o lista de forma [['3.434', 'iliescu'], ['2.12', 'nastase']]
        #intoarcem un dict de forma dict['iliescu'] = 3.234
        dict_return = {}
        for item in cuvinte:
            dict_return[item[1]] = item[0]
        return dict_return

    def combina_lista_cuvinte(self, dict_cuvinte_cluster, dict_cuvinte_articol):
        #combina doua liste de cuvinte
        cuvinte_combinate = []
        #obtinem mai intai lista cuvintelor comune
        set_cluster = Set(dict_cuvinte_cluster.keys())
        set_articol = Set(dict_cuvinte_articol.keys())
        combined_set = set_cluster & set_articol
        cuvinte_comune = [item for item in combined_set]
        set_cuvinte_separate = (set_cluster - set_articol) | (set_articol - set_cluster)
        cuvinte_separate = []
        for cuvant in set_cuvinte_separate:
            if dict_cuvinte_cluster.has_key(cuvant):
                cuvinte_separate.append([dict_cuvinte_cluster[cuvant], cuvant])
            else:
                cuvinte_separate.append([dict_cuvinte_articol[cuvant], cuvant])

        cuvinte_separate.sort()
        cuvinte_separate.reverse()
        cuvinte_combinate = [[dict_cuvinte_cluster[cuvant], cuvant] for cuvant in cuvinte_comune]
        cuvinte_combinate.extend(cuvinte_separate[:self.cluster_limit - len(cuvinte_combinate)])
        return cuvinte_combinate

    def get_new_cluster(self, articol, lista_cuvinte):
        #genereaza un nou cluster, pe care il adauga la lista_cluster
        #pentru moment nu salvam si overest pentru fiecare cuvant
        #important din cluster
        #data este salvata in formata Unix Time, deoarece 
        #am intampinat erori la marshalizare incercand sa salvez ca datetime
        data_unix = self.datetime_to_unixtime(articol.data)
        cluster = {'lungime': 1, 'lista_cuvinte': lista_cuvinte,
                   'articole_ids': [articol.id], 'data': data_unix,
                   'categorie': 0}
        return cluster

    def datetime_to_unixtime(self, data):
        #tranforma o data din format datetime in format unixtime
        return mktime(data.timetuple())+1e-6*data.microsecond
    
    def unixtime_to_datetime(self, data_unix):
        #transforma o data din format unixtime in format datetime
        return datetime.fromtimestamp(data_unix)

    def compute_distanta_cluster(self, cuvinte_cluster, cuvinte_articol):
        #calculeaza distanta dintre doua lista de cuvinte
        return len(Set(cuvinte_cluster) & Set(cuvinte_articol))

    def get_cuvinte_for_articole(self, dict_poisson):
        #se intoarce un dictionar de forma
        #dict[articol_id] = [[3.2323, 'iliescu'], [2.34, 'nastase']]
        return_dict = {}
        for key, values in dict_poisson.iteritems():
            lista_indecsi = values['lista_indecsi']
            for index in lista_indecsi:
                if return_dict.has_key(index):
                    return_dict[index].append([values['overest'], key])
                else:
                    return_dict[index] = [[values['overest'], key]]
        for key, value in return_dict.iteritems():
            value.sort()
            value.reverse()
        return return_dict 

class SaveClusters:
    """Salveaza clusterele calculate.""" 
    
    def __init__(self, lista_clustere):
        self.root = settings.ENGINE_ROOT
        self.lista_clustere = lista_clustere
        try:
            self.file = open(self.root + 'files/lista_clustere.txt', 'w+')
        except IOError:
            print 'Nu am putut deschide fisierul %sfiles/lista_clustere.txt' %(self.root)
        else:
            self.salveaza_clustere(self.lista_clustere)
            self.file.close()
            #salvam si cheile clusterelor precum si ID-urile asociate lor
            self.save_keys(self.lista_clustere)
            
    def salveaza_clustere(self, lista_clustere):
        self.file.truncate(0)
        marshal.dump(lista_clustere, self.file)

    def save_keys(self, lista_clustere):
        for cluster in lista_clustere:
            try:
                keys = get_cluster_keys(cluster).encode('utf-8')
            except UnicodeDecodeError:
                pass
            else:
                ids = get_cluster_ids(cluster)
                try:
                    cluster_keys_obj = Cluster.objects.get(keys__exact=keys)
                except Cluster.DoesNotExist:
                    cluster_keys_obj = None
                if cluster_keys_obj is None:
                    #clusterul nu exista, cream unul nou
                    cluster_keys_obj = Cluster(keys=keys, ids=ids)
                else:
                    #clusterul exista deja, updatam lista de ID-uri
                    cluster_keys_obj.ids = ids
                try:
                    cluster_keys_obj.save()
                except _mysql_exceptions.Warning, e:
                    print 'MySQL Warning: %s (ids = %s)' % (e, ids)
                except _mysql_exceptions.IntegrityError, e:
                    print 'IntegrityError: %s (ids = %s)' % (e, ids)

class LoadClusters:
    """Clasa ce incarca lista clusterelor"""
    
    def __init__(self):
        self.root = settings.ENGINE_ROOT
        try:
            self.file = open(self.root + 'files/lista_clustere.txt', 'r+')
        except IOError:
            print 'Nu am putut deschide fisierul %sfiles/lista_clustere.txt' %(self.root)
        else:
            self.lista_clustere = self.get_lista_clustere()
            self.file.close()

    def get_lista_clustere(self):
        return marshal.load(self.file)

if __name__ == "__main__":
    inceput = time.time()
    """cluster_limit = 10
    cluster_simil = 5
    poisson_loader = LoadPoisson()
    dict_poisson = poisson_loader.dict_poisson
    create_cl_obj = CreateCluster(dict_poisson, cluster_limit, cluster_simil)    
    save_obj = SaveClusters(create_cl_obj.lista_clustere)"""
    loader = LoadClusters()
    lista_clustere = loader.lista_clustere
    #print len(lista_clustere)
    save_obj = SaveClusters(lista_clustere)
    #print lista_clustere
    print 'Programul a fost executat in %s secunde' %(str(time.time() - inceput))
