from agentta import Agent_TA
from agentev import Agent_EV
from matrice import Matrice
from parametres import Parametres
import numpy as np
import random
import os


class Marche_biens :
    
    def __init__(self,parametres) :
        self.parametres = parametres
    
    def creer_matrice_AV(self,liste_agents_EV,liste_agents_TA) :
        #Initialisation de la matrice :
        m = len(liste_agents_TA) 
        n = len(liste_agents_EV) 
        matrice_AV = Matrice(m,n) 
        #Remplissage de la matrice :
        for i in range (matrice_AV.m) :
            for j in range (matrice_AV.n) :
                if self.parametres.proba_matrice_AV == 0.50 :
                    matrice_AV.data[i][j] = random.randint(0,1)
                else :
                    r = random.random()
                    matrice_AV.data[i][j] = int(r + float(self.parametres.proba_matrice_AV))
        return matrice_AV
        
    def creer_liste_prix_indicatifs(self,liste_agents_EV,liste_benefices_entreprises,liste_production_entreprises,liste_prix_indicatifs_precedente,parametres) :
        n = len(liste_agents_EV)
        f = lambda j : liste_agents_EV[j].proposer_prix(liste_benefices_entreprises[j],liste_production_entreprises[j],liste_prix_indicatifs_precedente[j],parametres) #Attention : ici les benefices sont ceux de l'iteration n-1 et la production est celle de l'iteration n (en cours)
        #Certains prix indicatifs de la liste peuvent etre None si la production de l'entreprise est nulle
        #Certains prix indicatifs de la liste peuvent etre nuls si les benefices de l'entreprise a l'iteration precedente sont nuls
        return map( f, range(n) )
        
    #Methode pour creer une liste qui contient pour chaque acheteur la somme des prix indicatifs qui lui sont proposes :
    def creer_liste_somme_prix_acheteurs(self,matrice_AV,liste_prix_indicatifs) :
        liste_somme_prix_indic_acheteurs = []
        #Pour chaque acheteur :
        for i in range (matrice_AV.m) :
            somme_prix = 0
            for j in range (len(liste_prix_indicatifs)) :
                if liste_prix_indicatifs[j] != None :
                    somme_prix += liste_prix_indicatifs[j]*matrice_AV.data[i][j]
            liste_somme_prix_indic_acheteurs.append(somme_prix)
        return liste_somme_prix_indic_acheteurs
        
    #Obtenir couples prix - vendeur dans l'ordre croissant :
    #Les prix indic None (cas ou la production de l'entreprise est nulle) sont classes en debut de liste
    #Methode utilisee dans la methode couper_lien_acheteur_vendeur 
    def obtenir_couples_prix_vendeur(self,liste_prix_indicatifs) :
        I = len(liste_prix_indicatifs)
        #Liste des paires de prix - indice vendeur :
        liste_prix_indice = zip(liste_prix_indicatifs,range(I)) 
        #Le premier element de la paire x est compare a celui de la paire y (prix) :
        couples_prix_vendeur = sorted(liste_prix_indice,cmp = lambda x,y : cmp(x[0],y[0])) 
        return couples_prix_vendeur
    
    def couper_lien_acheteur_vendeur(self,matrice_AV,liste_prix_indicatifs) :
        couples_prix_vendeur = self.obtenir_couples_prix_vendeur(liste_prix_indicatifs)
        L = len(couples_prix_vendeur)
        #Coupure si le prix indic de l'entreprise est None :
        couples_a_supprimer = []
        for i in range (matrice_AV.m) :
            for j in range (L) :
                if couples_prix_vendeur[j][0] == None or couples_prix_vendeur[j][0] == 0 :
                    matrice_AV.data[i][couples_prix_vendeur[j][1]] = 0 #Un agent TA peut se retrouver sans vendeur si les entreprises qu'il connait ne produisent plus
                    if i == 0 :
                        couples_a_supprimer.append(couples_prix_vendeur[j])               
        for c in range (len(couples_a_supprimer)) :       
            couples_prix_vendeur.remove(couples_a_supprimer[c]) 
            #print "Prix None ou nul donc coupure"
        L = len(couples_prix_vendeur) 
        #Coupure avec l'entreprise qui propose le prix le plus eleve si ce dernier est plus de 5x superieur au prix indic le plus bas : 
        for i in range (matrice_AV.m) :
            #On cree la liste des couples salaire - entreprise connectee pour chaque employe :
            couples_prix_vendeur_connecte = []
            for j in range (L) :
                if matrice_AV.data[i][j] == 1 :
                    couples_prix_vendeur_connecte.append(couples_prix_vendeur[j])
            #On verifie que l'acheteur est connecte a plus d'un vendeur :
            l = len(couples_prix_vendeur_connecte) 
            if l > 1 :
                if (couples_prix_vendeur[0][0] * self.parametres.param_coupure) < couples_prix_vendeur[l-1][0] :
                    matrice_AV.data[i][couples_prix_vendeur[l-1][1]] = 0
                    #print "Une coupure a eu lieu entre l'acheteur " + str(i) + " et le vendeur " + str(j)
        return matrice_AV 
    
    #Obtenir matrice de repartition du cash des acheteurs chez les vendeurs :
    def repartir_cash(self,iteration,matrice_AV,liste_agents_TA,liste_prix_indicatifs,liste_somme_prix_indic_acheteurs,liste_payes_totales_employes) : #liste_payes_totales_employes = paye totale de chaque employe recue a l'iteration n-1
        repartition_cash_acheteurs = []
        somme_repartition_cash_acheteurs = 0
        #Pour chaque acheteur :
        for i in range (len(liste_agents_TA)) :
            somme_inverses = 0
            liste_repartition_cash = []
            #On calcule la somme des inverses des prix indicatifs des entreprises connectes (utilise pour la repartition inversement proportionnelle) :
            for j in range (len(liste_prix_indicatifs)) :
                if matrice_AV.data[i][j] == 1 :
                    if liste_prix_indicatifs[j] != None and liste_prix_indicatifs[j] != 0 : 
                        somme_inverses += 1/liste_prix_indicatifs[j]
            #Repartition du cash inversement proportionnelle aux prix propose par les vendeurs :
            for j in range (len(liste_prix_indicatifs)) :
                if matrice_AV.data[i][j] == 0 :
                    cash_investi = 0
                else :
                    if liste_prix_indicatifs[j] == None or liste_prix_indicatifs[j] == 0 :
                        cash_investi = 0
                    else : 
                        if iteration == 0 :
                            cash_investi = ((1/liste_prix_indicatifs[j])/somme_inverses)*liste_agents_TA[i].cash*self.parametres.prct_invest_achat
                        else :
                            cash_investi = ((1/liste_prix_indicatifs[j])/somme_inverses)*liste_payes_totales_employes[i] #Payes recues a l'iteration n-1
                liste_repartition_cash.append(cash_investi)
                somme_repartition_cash_acheteurs += cash_investi
            repartition_cash_acheteurs.append(liste_repartition_cash)
        return repartition_cash_acheteurs,somme_repartition_cash_acheteurs
    
    def echanger(self,liste_agents_EV,liste_agents_TA,repartition_cash_acheteurs,liste_prix_indicatifs,liste_production_entreprises) :
        liste_benefices_entreprises = []
        #Pour chaque entreprise :
        for j in range (len(liste_agents_EV)) :
            total_cash = 0
            #Les acheteurs donnent le cash :
            for i in range (len(liste_agents_TA)) :
                montant = repartition_cash_acheteurs[i][j]
                total_cash += montant
                liste_agents_TA[i].cash -= montant
                liste_agents_EV[j].cash += montant 
            liste_benefices_entreprises.append(total_cash)
            #Les entreprises donnent les biens :
            for i in range (len(liste_agents_TA)) :    
                montant = repartition_cash_acheteurs[i][j]
                if montant != 0 :
                    quantite = (montant/liste_benefices_entreprises[j])*liste_production_entreprises[j]
                    liste_agents_EV[j].biens -= quantite
        return liste_benefices_entreprises 

    
    ###METHODES GLOBALES POUR FAIRE TOURNER LE MARCHE DES BIENS :                 
                      
    #Methode a lancer a l'iteration 0 :
    def initialiser_marche_biens(self,iteration,liste_agents_EV,liste_agents_TA,matrice_AV,liste_production_entreprises) :
        #Initialisation de la liste des prix indicatifs :
        m = len(liste_agents_EV)
        liste_prix_indicatifs = [self.parametres.prix_initial] * m
        #Les acheteurs repartissent leur cash chez les differents vendeurs avec lesquels ils sont connectes :
        liste_somme_prix_indic_acheteurs = self.creer_liste_somme_prix_acheteurs(matrice_AV,liste_prix_indicatifs)
        liste_payes_totales_employes = []
        repartition_cash_acheteurs,somme_repartition_cash_acheteurs = self.repartir_cash(iteration,matrice_AV,liste_agents_TA,liste_prix_indicatifs,liste_somme_prix_indic_acheteurs,liste_payes_totales_employes)
        liste_benefices_entreprises = self.echanger(liste_agents_EV,liste_agents_TA,repartition_cash_acheteurs,liste_prix_indicatifs,liste_production_entreprises)
        return matrice_AV,liste_prix_indicatifs,repartition_cash_acheteurs,somme_repartition_cash_acheteurs,liste_benefices_entreprises

    #Methode centrale :
    def faire_tourner_marches_biens(self,iteration,matrice_AV,liste_agents_EV,liste_agents_TA,liste_benefices_entreprises,liste_production_entreprises,liste_payes_totales_employes,liste_prix_indicatifs_precedente,parametres) :
        #Chaque vendeur propose un prix unitaire indicatif :
        liste_prix_indicatifs = self.creer_liste_prix_indicatifs(liste_agents_EV,liste_benefices_entreprises,liste_production_entreprises,liste_prix_indicatifs_precedente,parametres) #Attention : ici liste_benefices_entreprises contient les benefices de chaque entreprises a l'iteration n-1
        #Coupures eventuelles avec MAJ de la matrice TE :
        matrice_AV = self.couper_lien_acheteur_vendeur(matrice_AV,liste_prix_indicatifs)
        #Les acheteurs repartissent leur cash chez les differents vendeurs avec lesquels ils sont connectes :
        liste_somme_prix_indic_acheteurs = self.creer_liste_somme_prix_acheteurs(matrice_AV,liste_prix_indicatifs)
        repartition_cash_acheteurs,somme_repartition_cash_acheteurs = self.repartir_cash(iteration,matrice_AV,liste_agents_TA,liste_prix_indicatifs,liste_somme_prix_indic_acheteurs,liste_payes_totales_employes)
        liste_benefices_entreprises = self.echanger(liste_agents_EV,liste_agents_TA,repartition_cash_acheteurs,liste_prix_indicatifs,liste_production_entreprises)
        return matrice_AV,liste_prix_indicatifs,repartition_cash_acheteurs,somme_repartition_cash_acheteurs,liste_benefices_entreprises
                
  
                
                
                
                
                
    