
/***************************************************************************
                          MetaPop.cpp  -  Librairie d'objets permettant de manipuler des donn�es
                          						sp�cifiques aux MetaPops
                             -------------------
    begin                : ven sep 01 10:25:55 CEST 2000
    copyright            : (C) 2000 by Olivier Langella CNRS UPR9034
    email                : Olivier.Langella@pge.cnrs-gif.fr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "metapop.h"
#include "jeupop.h"

//constructeur du niveau racine
MetaPop::MetaPop(Jeupop * pdonnees):StrucPop(pdonnees) {

//cerr << "MetaPop::MetaPop(Jeupop * pdonnees) constructeur debut" << endl;
	_Pmetapop = NULL;
	_niveau_struc = 0;
	_nom = "TOP_LEVEL";

	_tabPstrucpop.resize(0);
//cerr << "MetaPop::MetaPop(Jeupop * pdonnees) constructeur fin" << endl;
}

//constructeur de niveau interm�diaire
MetaPop::MetaPop(MetaPop * pmetapopsup):StrucPop(pmetapopsup->_Pjeu) {
	_Pmetapop = pmetapopsup;
	_niveau_struc = pmetapopsup->_niveau_struc + 1;
	
	_tabPstrucpop.resize(0);
}

//constructeur de copies
MetaPop::MetaPop (const MetaPop & popori, MetaPop * Pmetapop, Jeupop * Pjeu):StrucPop(popori, Pmetapop, Pjeu){
	unsigned long i, nbpop;
//cerr << "MetaPop::MetaPop construct copie d�but " << endl;

	//seulement pour l'objet MetaPop:
	_niveau_struc = popori.get_niveau();

	nbpop = popori.get_nbpop();

//	MetaPop * Pmetapopori(&popori);

	for (i=0; i < nbpop; i++) {
		if (popori._tabPstrucpop[i]->DuType("Population")) {
			AjouterPopulation(new Population(*(popori._tabPstrucpop[i]), this));
		}
		if (popori._tabPstrucpop[i]->DuType("MetaPop")) {
			AjouterPopulation(new MetaPop(*(popori.get_Ptabmetapop(popori._tabPstrucpop[i])), this, _Pjeu));
		}
	}
//cerr << "MetaPop::MetaPop construct copie fin " << endl;
}

//destructeur
MetaPop::~MetaPop () {
//cerr << "MetaPop::~MetaPop debut" << endl;

	unsigned long i;

	for (i=0;i < _tabPstrucpop.size();i++) delete _tabPstrucpop[i];
//cerr << "MetaPop::~MetaPop fin" << endl;
}

bool MetaPop::DuType(const string & nom) const {
	if (nom == "MetaPop") return(true);
	else return(false);
}

void MetaPop::ifAjouterIndividu(const Individu * Pind) {
}

void MetaPop::AjouterIndividu(Individu * Pind) {
	//ajout d'un individu dans une MetaPopulation

	_tabPind.push_back(Pind);

	if (_niveau_struc !=0) _Pmetapop->AjouterIndividu(Pind);
	
}

void MetaPop::AjouterPopulation(Population * Ppop) {

	_tabPstrucpop.push_back(Ppop);
	_Pjeu->AjouterTabPop(Ppop);
}

void MetaPop::AjouterPopulation(MetaPop * Pmetapop) {
	_tabPstrucpop.push_back(Pmetapop);
	_tabPmetapop.push_back(Pmetapop);
}

MetaPop * MetaPop::NewMetaPop(const biolib::vecteurs::ChaineCar & nom) {
	MetaPop * Pmetapop;
	unsigned long i;

	for (i=0; i < _tabPmetapop.size(); i++) {
		//si ma m�tapop existe d�j�:
		if (_tabPmetapop[i]->get_nom() == nom) return (_tabPmetapop[i]);
	}
	Pmetapop = new MetaPop(this);
	Pmetapop->set_nom(nom);
	AjouterPopulation(Pmetapop);
	return (Pmetapop);
}

void MetaPop::get_nomniveauxstruc(Titre & nom_niveaux) const{

	unsigned long i;

	if (nom_niveaux.size() < (_niveau_struc + 1)) nom_niveaux.push_back(_nom);
	else nom_niveaux[_niveau_struc] = nom_niveaux[_niveau_struc] + " " + _nom;

	for (i = 0; i < _tabPstrucpop.size(); i++) {
		_tabPstrucpop[i]->get_nomniveauxstruc(nom_niveaux);
	}
		
}

void MetaPop::set_nploidie() {
	_nploidie = _Pjeu->get_nploidie();
	_nbloc = _Pjeu->get_nbloc();

	unsigned long i;
//cerr << "MetaPop::set_nploidie() " << _nploidie  << endl;

	for (i=0; i < _tabPstrucpop.size(); i++) {
		_tabPstrucpop[i]->set_nploidie();
	}
//cerr << "MetaPop::set_nploidie() fin" << _nploidie  << endl;
}

void MetaPop::reset() {
	// remise � z�ro
	unsigned long i;

	StrucPop::reset();

	for (i=0 ; i < _tabPstrucpop.size(); i++) {
		delete _tabPstrucpop[i];
	}
	_tabPstrucpop.resize(0);
	_tabPmetapop.resize(0);
	
}

MetaPop * MetaPop::get_Ptabmetapop(const StrucPop * Pstrucpop) const {
	unsigned long i;
	
	for (i=0; i < _tabPmetapop.size(); i++) {
		if (_tabPmetapop[i] == Pstrucpop) return (_tabPmetapop[i]);
	}
//	pos = _tabPmetapop.Position(Pstrucpop);
	return (0);
}

void MetaPop::f_rempliTabStrucPop(Vecteur<StrucPop*> & tabStrucPop, unsigned int niveau) {
	unsigned long i;

//cerr << "MetaPop::f_rempliTabStrucPop(Vecteur<StrucPop*> & tabStrucPop, unsigned int niveau)" << endl;
//	if (get_niveau() > niveau) return;

  if (get_niveau() == niveau) {
//cerr <<  get_niveau() << " " << niveau  << endl;
		tabStrucPop.push_back(this);
		return;
	}
	for (i = 0; i < _tabPstrucpop.size(); i++) {
		_tabPstrucpop[i]->f_rempliTabStrucPop(tabStrucPop, niveau);
	}
}

long double MetaPop::f_Mheterozygotieatt(unsigned long locus) const {
//moyenne de l'h�t�rozygotie attendue sur les sous populations
//pour ce locus (Hs barre)
// ok verifie le 11/10/2000

	long unsigned nbpop(get_nbpop()),k, nbpopcalc;
	long double MoyHS(0);

	nbpopcalc = nbpop;
	for (k=0; k < nbpop; k++) {
		MoyHS += _tabPstrucpop[k]->f_heterozygotieatt(locus, &nbpopcalc);
	}
	if ((nbpopcalc == 0) || (nbpopcalc > 100000)) return(0);

	MoyHS /= (long double) nbpopcalc;

	return(MoyHS);

}

long double MetaPop::f_Mheterozygotieobs(unsigned long locus) const {
//moyenne de l'h�t�rozygotie observ�e sur les sous populations
//HI h�t�rozygotie individuelle pour ce locus
// ok verifie le 11/10/2000

	long unsigned nbpop(get_nbpop()),k, nbpopcalc;
	long double MoyHI(0);

	nbpopcalc =  nbpop;
	for (k=0; k < nbpop; k++) {
		MoyHI += _tabPstrucpop[k]->f_heterozygotieobs(locus, &nbpopcalc);
	}
	if ((nbpopcalc == 0) || (nbpopcalc > 100000)) return(0);
	
	MoyHI /= (long double) nbpopcalc;

	return(MoyHI);

}

long double MetaPop::f_Mheterozygotietotale(unsigned long locus) const {
// h�t�rozygotie totale (Ht) attendue, � partir des fr�quences all�liques
// moyennes sur la m�tapopulation pour ce locus
// ok verifie le 11/10/2000

	long unsigned i,nballloc,allnonnuls;
	long double sfreqcarre(0), freq;//, Hs;
	long unsigned nbpop(get_nbpop()),k;
	Allele * Pall;

	nballloc = _Pjeu->get_Plocus(locus)->get_nball();
	for (i=0; i < nballloc; i++) {
		for (freq=0,k=0; k < nbpop; k++) {
			allnonnuls = _tabPstrucpop[k]->r_nballnonnuls(locus);
			if (allnonnuls == 0) continue;

			Pall = _Pjeu->get_Pall(locus,i);
			if (Pall->r_estnul()) continue;
			freq += ((long double) _tabPstrucpop[k]->r_nbcopall(Pall)) / ((long double) allnonnuls);
		}
		freq /= (long double) nbpop; //fr�quence moyenne
		sfreqcarre += (freq * freq);			
	}		
	//Hs += (long double) 1 - sfreqcarre;
	return ((long double) 1 - sfreqcarre);	

	
}

long double MetaPop::f_M_Fis(unsigned long locus) const {
// indice de Wright Fis pour ce locus
// ok verifie le 11/10/2000
// Fis = (Hs barre - Hi) / Hs barre
	if (locus > (unsigned long)get_nbloc()) throw Anomalie(7);

//cerr << "MetaPop::f_M_Fis Hi:" << f_Mheterozygotieobs(locus) << endl;
	long double Hsbarre(f_Mheterozygotieatt(locus));
//cerr << "MetaPop::f_M_Fis Hsbarre:" << Hsbarre << endl;

	return ((Hsbarre - f_Mheterozygotieobs(locus))/ Hsbarre);
}

long double MetaPop::f_M_Fst(unsigned long locus) const {
// indice de Wright Fst pour ce locus
// ok verifie le 11/10/2000
// Fst = (Ht - Hs barre) / Ht
//cerr << "MetaPop::f_M_Fst" << endl;
//cerr << locus << " " << get_nbloc() << endl;
	if (locus > get_nbloc()) throw Anomalie(7);

	long double Ht(f_Mheterozygotietotale(locus));

//cerr << "MetaPop::f_M_Fst Ht " << Ht << endl;
//cerr << "MetaPop::f_M_Fst Hsbarre " << f_Mheterozygotieatt(locus) << endl;

	return ((Ht - f_Mheterozygotieatt(locus))/ Ht);
}


long double MetaPop::f_M_Fit(unsigned long locus) const {
// indice de Wright Fit pour ce locus
// ok verifie le 11/10/2000
// Fit = (Ht - Hi) / Ht
//cerr << "MetaPop::f_M_Fit" << endl;
//cerr << locus << " " << get_nbloc() << endl;

	if (locus > get_nbloc()) throw Anomalie(7);

	long double Ht(f_Mheterozygotietotale(locus));

	return ((Ht - f_Mheterozygotieobs(locus))/ Ht);
}

/** Suppression de la Population Ppop */
void MetaPop::SupprPop(StrucPop * Ppop){
//cerr << "MetaPop::SupprPop debut" << endl;
	unsigned long nbpop(_tabPstrucpop.size()),p;
	bool ok(false);

	for (p=0; p < nbpop; p++) {
		if (Ppop == _tabPstrucpop[p]) {
//cerr << "MetaPop::SupprPop efface" << endl;
			delete Ppop;
			_tabPstrucpop.erase(_tabPstrucpop.begin()+p);
			p--;
			nbpop--;
			ok = true;
		}
	}
//cerr << "MetaPop::SupprPop debut 2" << endl;

	if (ok) {
		nbpop = _tabPmetapop.size();
		for (p=0; p < nbpop; p++) {
			if (Ppop == _tabPmetapop[p]) {
				_tabPmetapop.erase(_tabPmetapop.begin()+p);
			}
		}
//cerr << "MetaPop::SupprPop debut 3 " << "ok" << endl;
	}
	else {
		for (p=0; p < nbpop; p++) _tabPstrucpop[p]->SupprPop(Ppop);
	}
//cerr << "MetaPop::SupprPop fin" << endl;
		
}
/** Retourne vrai, si l'allele n'est pas pr�sent dans MetaPop */
bool MetaPop::r_allelenonpresent(Allele * Pall) const{
	if (r_nbcopall(Pall) > 0) return (false);
	else return(true);
}
/** r�affectation du nombre de locus
 */
void MetaPop::set_nbloc(){
	unsigned int i;
	_nbloc = _Pjeu->get_nbloc();

	for (i = 0; i < _tabPstrucpop.size(); i ++) _tabPstrucpop[i]->set_nbloc();
}

void MetaPop::oPopulationsXML(unsigned int id, ostream & sortie, ostream & infos) {
  unsigned int i, nbpop(_tabPstrucpop.size());
  biolib::vecteurs::ChaineCar idXML;

  sortie << "<metapopulation";
  idXML = "mp";
  idXML.AjEntier(id);
  idXML += "le";
  idXML.AjEntier(get_niveau());
  sortie << " id=\"" << idXML << "\"";
  sortie << " name=\"" << get_nom() << "\"";
  sortie << ">" << endl;


  for (i=0; i < nbpop; i++) {
    _tabPstrucpop[i]->oPopulationsXML(i, sortie, infos);
  }
  
  sortie << "</metapopulation>" << endl;

}

Population * MetaPop::new_population(string & nom) {
  Population * Ppop(new Population(this));

  Ppop->set_nom(nom);
  AjouterPopulation(Ppop);
  return (Ppop);
}

