import cobra



def get_biomass_dict():
    
    
    # Note: universal and conditional precursors have been defined in Xavier2017 .
    # Here presented in the same order of Xavier2017 .
    # Xavier2017: 10.1016/j.ymben.2016.12.002.
    biomass_dict = {
        ##### UNIVERSAL #####
        'ribo_nucleotides': [
            'atp_c', 'ctp_c', 'gtp_c', 'utp_c'
        ],
        'deoxyribo_nucleotides': [
            'datp_c', 'dctp_c', 'dgtp_c', 'dttp_c'
        ],
        'amino_acids': [
            'ala__L_c', 'arg__L_c', 'asn__L_c', 'asp__L_c', 'cys__L_c', 
            'gln__L_c', 'glu__L_c', 'gly_c',    'his__L_c', 'ile__L_c', 
            'leu__L_c', 'lys__L_c', 'met__L_c', 'ser__L_c', 'pro__L_c', 
            'thr__L_c', 'trp__L_c', 'tyr__L_c', 'val__L_c', 'phe__L_c'
        ],
        'cofactors_uni': [
            'nad_c',    # B3: Nicotinamide -adenine dinucleotide phosphate
            'nadp_c',   # B3: Nicotinamide -adenine dinucleotide phosphate
            'coa_c',    # B5: Coenzyme A  (dependant on 'pnto__R_c')
            'fad_c',    # B2: Flavin adenine dinucleotide
            'fmn_c',    # B2: Flavin mononucleotide
            'ribflv_c', # B2: ribovlavin. Non-active form acording to Xavier2017 but anyway included.
            #'f4200_c', # B2: included by Xavier2017 in 'universal' but the description seems conditional.
            'thf_c',    # B9: tetrahydrofolate 
            '10fthf_c', # B9: 10-Formyltetrahydrofolate
            '5mthf_c',  # B9: 5-Methyltetrahydrofolate
            'thmpp_c',  # B1: Thiamine diphosphate
            'pydx5p_c', # B6: pyridoxal 5-phosphate
            'amet_c',   # SAM: S-adenosyl-methionine
        ],
        ##### CONDITIONAL #####
        'cofactors_con': [
            #'f4200_c', # coenzyme f420 (electron transfer in methanogens, actinobacteria , and others)
            'ptrc_c',   # Putrescine
            'spmd_c',   # Sperimidine
            'pheme_c',  # protoheme
            'mql8_c',   # menaquinol / manaquinone (mqn8_c)
            'q8h2_c',   # ubiquinol / ubiquinone (q8_c)
            # Methionaquinone
            'btn_c',    # B7: biotin
            'ACP_c',    # Acyl-carrier protein
            'adocbl_c', # B12: Adenosylcobalamin
            # Lipoate
            'uacgam_c'  # uridine diphosphate N-Acetylglucosamine (UDP-GlcNAc)
        ],
        ##### ADDED ##### (conditionals not included or lumped in Xavier2017)
        'cofactors_add': [
            'hemeO_c',  # heme-O
            'sheme_c',  # siroheme
            'moco_c',   # molybdenum cofactor
            'phllqol_c',# phylloquinol / phylloquinone (phllqne_c)
            'gthrd_c',  # glutathione (reduced)
            'br_c',     # bacterioruberin
        ],
        'lipids': [
            'pe120_c', # phosphatidyl-ethanolamine (12:0;12:0)
            'pg120_c', # phosphatidyl-glycerol (12:0;12:0)
            'clpn120_c', # cardiolipin (12:0;12:0;12:0;12:0)
            # 1-lysyl phosphatidylglycerol (plantarum)
        ],
        'membrane_wall': [
            'peptidoSTA_c', # peptidoglycan (dependant on 'udcpdp_c')
            'WTAgg40r_20n_20a_P_c', # teichoic acids
            'WTArg40r_20g_20a_P_c', # teichoic acids
            'WTAg40g_20g_20a_P_c', # teichoic acids
            'LTAgg40g_20n_20a_c', # lipoteichoic acids
            'LTAga40g_20t_20a_c', # lipoteichoic acids            
            # capsular polysaccharides
            # kdo_lipid_A
        ],
        'energy_stock': [
            # glycogen
            # starch
            'phb_c', # PHA / PHB
        ]
    }
    return biomass_dict



def introduce_biomass(logger, db, model): 
    
    
    biomass_dict = get_biomass_dict()
    
    
    rstring =           f'0.01 {" + 0.01 ".join(biomass_dict["ribo_nucleotides"])}'
    rstring = rstring + f' + 0.01 {" + 0.01 ".join(biomass_dict["deoxyribo_nucleotides"])}'
    rstring = rstring + f' + 0.01 {" + 0.01 ".join(biomass_dict["amino_acids"])}'
    rstring = rstring + f' + 0.01 {" + 0.01 ".join(biomass_dict["cofactors_uni"])}'
    rstring = rstring + f' + 0.01 {" + 0.01 ".join(biomass_dict["cofactors_con"])}'
    rstring = rstring + f' + 0.01 {" + 0.01 ".join(biomass_dict["cofactors_add"])}'
    rstring = rstring + f' + 0.01 {" + 0.01 ".join(biomass_dict["lipids"])}'
    rstring = rstring + f' + 0.01 {" + 0.01 ".join(biomass_dict["membrane_wall"])}'
    rstring = rstring + f' + 0.01 {" + 0.01 ".join(biomass_dict["energy_stock"])}'
    rstring = rstring + f' + 0.01 atp_c + 0.01 h2o_c --> 0.01 adp_c + 0.01 h_c + 0.01 pi_c'
    
    
    r = cobra.Reaction('Biomass')
    model.add_reactions([r])
    r = model.reactions.get_by_id('Biomass')
    r.name = 'Biomass assembly reaction'
    r.build_reaction_from_string(rstring)
    
    
    # set as objective:
    model.objective = 'Biomass'
    
    
    return model



def adjust_biomass_equation(logger, model, universe, conditional_threshold):
    
    
    # Note: universal and conditional precursors have been defined in 10.1016/j.ymben.2016.12.002 .
    precursor_to_pathway = {  # listing alternative biosynthetic pathways
        
        ##### cofactors_con #####
        #'f4200_c', # coenzyme f420 (electron transfer in methanogens, actinobacteria , and others)
        #'ptrc_c',   # Putrescine
        #'spmd_c',   # Sperimidine
        'pheme_c': ['M00868', 'M00121', 'M00926'],   # protoheme (heme)
        'mql8_c': ['M00116', 'M00930', 'M00931'],  # menaquinol
        'q8h2_c': ['M00117', 'M00989', 'M00128'],  # ubiquinol
        # Methionaquinone
        #'btn_c',    # B7: biotin
        #'ACP_c',    # Acyl-carrier protein
        'adocbl_c': ['M00122'],   # vitamin B12 (cobolamin)
        # Lipoate
        #'uacgam_c'  # uridine diphosphate N-Acetylglucosamine (UDP-GlcNAc)
        
        ##### cofactors_add #####
        'hemeO_c': ['gr_HemeO'],  # heme-O
        'sheme_c': ['M00846'],   # siroheme
        'moco_c': ['M00880'],  # molybdenum cofactor
        'phllqol_c': ['M00932'],  # phylloquinol
        'gthrd_c': ['M00118'],  # Reduced glutathione
        'br_c': ['gr_br'],  # bacterioruberin
        
        ##### lipids #####
        #'pe120_c', # phosphatidyl-ethanolamine (12:0;12:0)
        #'pg120_c', # phosphatidyl-glycerol (12:0;12:0)
        #'clpn120_c', # cardiolipin (12:0;12:0;12:0;12:0)
        
        ##### membrane_wall #####
        # 1-lysyl phosphatidylglycerol (plantarum)
        'peptidoSTA_c': ['gr_ptdSTA'], # peptidoglycan (dependant on 'udcpdp_c')
        'WTAgg40r_20n_20a_P_c': ['gr_WTA3'], # teichoic acids
        'WTArg40r_20g_20a_P_c': ['gr_WTA2'], # teichoic acids
        'WTAg40g_20g_20a_P_c':  ['gr_WTA1'], # teichoic acids
        'LTAgg40g_20n_20a_c':  ['gr_LTA1'], # lipoteichoic acids
        'LTAga40g_20t_20a_c':  ['gr_LTA2'], # lipoteichoic acids            
        # capsular polysaccharides
        # kdo_lipid_A
        
        ##### energy_stock #####
        'phb_c': ['gr_PHA1'],  # PHA / PHB
        
    }
    modeled_rids = [r.id for r in model.reactions]
    
    
    cnt_removed = 0
    varprec = {}  # dictionary of variable biomass precursors
    for precursor, pathways in precursor_to_pathway.items(): 
        
        pathway_to_absence = {}
        pathway_to_compstring = {}   # completeness string
        for pathway in pathways:   # more pathways might lead to the same precursor
            # initialize counters:
            cnt_members_tot = 0
            cnt_members_present = 0

            
            if pathway not in [gr.id for gr in universe.groups]:
                continue   # still missing from the universe
                
            for member in universe.groups.get_by_id(pathway).members:
                cnt_members_tot += 1    
                if member.id in modeled_rids:
                    cnt_members_present += 1
            # populate dicts:
            pathway_to_absence[pathway] = (cnt_members_present / cnt_members_tot) < conditional_threshold
            pathway_to_compstring[pathway] = f'{pathway}: {cnt_members_present}/{cnt_members_tot}'
            

        varprec[precursor] = '; '.join(list(pathway_to_compstring.values()))
        if all(list(pathway_to_absence.values())):
            cnt_removed += 1
            logger.debug(f"Biomass precursor '{precursor}' seems not required ({varprec[precursor]}).")
            # add metabolites to the right side (they will disappear if the balance if 0)
            model.reactions.Biomass.add_metabolites({precursor: 0.01})

       
    if model.id != universe.id:   # avoid printing when testing blocked biomass precusors of the universe.
        logger.info(f"Removed {cnt_removed} biomass precursors.")
        
        
    return varprec