/***************************************************************************
                          individu.cpp  -  description
                             -------------------
    begin                : Thu Sep 14 2000
    copyright            : (C) 2000 by Olivier Langella
    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 "individu.h"
#include "population.h"
#include "jeupop.h"


void Individu::creation (Population * Ppop) {
	_Ppop = Ppop;
	_nploidie = Ppop->get_nploidie();
	if (Ppop->get_nploidie() == 0) _nploidie = 0;
	else _nploidie = Ppop->get_nploidie();
	_tabPall.resize(Ppop->get_nbloc() * _nploidie);
}

//constructeur
Individu::Individu (Population * Ppop) {

	creation(Ppop);
	_nom.assign("");
}

Individu::Individu (Population * Ppop, const string& nom) {

	creation(Ppop);
	_nom.assign(nom);
}

//destructeur
Individu::~Individu () {
//cerr << "Individu::~Individu ()" << endl;
//	delete [] _tabPall;
}

void Individu::resize_alleles() {
	
	_nploidie = _Ppop->get_nploidie();
	_tabPall.resize(_Ppop->get_nbloc() * _nploidie);	
}

const Individu& Individu::operator= (const Individu & rval) {
	unsigned long i; //index
	unsigned long j; //locus
	string nomlocus;
	unsigned long nball(rval._tabPall.size());
	unsigned long nploidie(_Ppop->get_nploidie());
	unsigned long nbloc(_Ppop->get_nbloc());

	if (_tabPall.size() != nball) throw Anomalie (1);


	_nom = rval._nom;

//cerr << "operator= individu debut " << _nom << endl;
	for (j=0; j < nbloc; j ++) {
		nomlocus = rval._Ppop->_Pjeu->get_nomlocus(j);
		for (i=0; i < nploidie; i ++) {
//cerr << "operator= individu numall " << (j*nploidie) + i << endl;
			if (rval._tabPall[(j * nploidie) + i] == 0) {
				_tabPall[(j * nploidie) + i] = (_Ppop->_Pjeu->get_Plocus(nomlocus))->getPallNul();
			}
			else {
//cerr << "operator= individu nom de l'allele " << rval._tabPall[(j * nploidie) + i]->_nom << endl;
				_tabPall[(j * nploidie) + i] = _Ppop->get_Pall(nomlocus, rval._tabPall[(j * nploidie) + i]->_nom);
			}
		}
	}
//cerr << "operator= individu fin" << endl;

	return(*this);
}

bool Individu::r_estnul() const {
	long i; //index
	long nball(_tabPall.size());

	for (i=0; i < nball; i ++) if (_tabPall[i]->_miss == false ) return (false);
	return (true);
}

bool Individu::r_esthetero (unsigned long locus) const {
	//lancer une exception si l'individu n'est pas diploide !!!!

	unsigned long nploidie(_Ppop->get_nploidie());
	unsigned long nbloc(_Ppop->get_nbloc());

	if (locus >= nbloc) throw Anomalie(11);
	if (nploidie != 2) throw Anomalie(12);

	if ((_tabPall[(locus * 2)]->_miss)||(_tabPall[(locus * 2)+1]->_miss)) {
		throw Anomalie(13);
		return(false);
	}
	if (_tabPall[(locus * 2)] == _tabPall[(locus * 2) + 1]) return(false);
	else return (true);

}

bool Individu::operator== (const Individu & rval) const {

	//pas operationnel !!!!

	unsigned long i; //index
	unsigned long j; //locus
	string nomlocus;
	unsigned long nball(rval._tabPall.size());
	unsigned long nploidie(_Ppop->get_nploidie());
	unsigned long nbloc(_Ppop->get_nbloc());

//	cerr << "truc";
	if (rval == 0) {
//		cerr << "truc";
		nball = _tabPall.size();
		for (i=0; i < nball; i ++) if (_tabPall[i]->_miss == false ) return (false);
		return (true);
	}

	if (_tabPall.size() != nball) return (false);


//	_nom = rval._nom;

	for (j=0; j < nbloc; j ++) {
//		nomlocus = rval._Ppop->_Pjeu->get_nomlocus(j);
		for (i=0; i < nploidie; i ++) {
			if (_tabPall[(j*nploidie) + i] != rval._tabPall[(j*nploidie) + i]) return (false);
//			_tabPall[(j*nploidie) + i] = _Ppop->get_Pall(nomlocus, rval._tabPall[(j*nploidie) + i]->_nom);
		}
	}

	return(true);
}


Individu::Individu (const Individu & rval, Population * Ppop){
	unsigned long i; //index
	unsigned long j; //locus
	string nomlocus;
	unsigned long nball(rval._tabPall.size());
//cerr << "Individu::Individu construct copie début "<< nball << endl;
	unsigned long nploidie(Ppop->get_nploidie());
//cerr << "Individu::Individu construct copie début "<< nball << endl;
	unsigned long nbloc(Ppop->get_nbloc());

//cerr << "Individu::Individu construct copie début " << endl;
	_Ppop = Ppop;
	resize_alleles();
	if (_tabPall.size() != nball) throw Anomalie (1);

	_nom = rval._nom;

//cerr << "operator= individu debut " << _nom << endl;
	for (j=0; j < nbloc; j ++) {
		nomlocus = rval._Ppop->_Pjeu->get_nomlocus(j);
		for (i=0; i < nploidie; i ++) {
//cerr << "operator= individu numall " << (j*nploidie) + i << endl;
			if (rval._tabPall[(j * nploidie) + i] == 0) {
				_tabPall[(j * nploidie) + i] = (_Ppop->_Pjeu->get_Plocus(nomlocus))->getPallNul();
			}
			else {
//cerr << "operator= individu nom de l'allele " << rval._tabPall[(j * nploidie) + i]->_nom << endl;
				_tabPall[(j * nploidie) + i] = _Ppop->get_Pall(nomlocus, rval._tabPall[(j * nploidie) + i]->_nom);
			}
		}
	}
//cerr << "operator= individu fin" << endl;
//cerr << "Individu::Individu construct copie fin " << endl;

}


void Individu::ifPlacerAllele(Locus* Ploc, long position, const Allele * Pall) {
	// numall < nploidie
//	long position((Ploc->get_numloc()
//cerr << "coucou 3" << endl;
	if ((_tabPall[position] == 0) || (_tabPall[position]->_miss)) {
		//on remplace, normal
//cerr << "coucou 3 if" << endl;
		_tabPall[position] = Ploc->getPall(Pall->get_nom());
	}
	else {
		if (*_tabPall[position] == *Pall) {
			//c'est bon
			return;
		}
		else {
			// ca veut dire qu'il y a un allele, et qu'il est different
			//   => pb, mais on ignore pour l'instant
			return;
		}
	}
}

void Individu::ifFusionnerIndividu(const Individu& individu) {
	unsigned long nbloc(_Ppop->get_nbloc());
	unsigned long nploidie(_Ppop->get_nploidie());
	unsigned long taille,i,j;
	Locus * Ploc;
//	string nomloc;

	taille = nbloc * nploidie;

	while (_tabPall.size() < taille) {
		_tabPall.push_back(0);
	}
//cerr << "coucou 1" << endl;

	taille = individu._tabPall.size();
	for (i = 0; i < taille; i += nploidie) {
		Ploc = _Ppop->_Pjeu->get_Plocus(individu._tabPall[i]->get_NomLocus());
//cerr << "coucou 2" << Ploc->get_nom() << endl;
		for (j=0; j < nploidie; j++) {
			ifPlacerAllele(Ploc,((Ploc->get_numloc() * nploidie) + j),individu._tabPall[i+j]);
		}
	}
}


unsigned long Individu::r_nballnonnuls(unsigned long locus, unsigned int nploidie) const {
	// retourne le nb de copies d'allèles non nuls pour un locus
	unsigned long resultat(0);
	unsigned int j;
	
	for (j=0;j < nploidie;j++) {
		if (_tabPall[(locus * nploidie) + j]->_miss) continue;
		resultat++;
	}

	return(resultat);
}


const string& Individu::get_nom_all(unsigned long numlocus, unsigned long numall) const {
//	unsigned long nploidie(_Ppop->get_nploidie());
	unsigned long nbloc(_Ppop->get_nbloc());

	if (numlocus >= nbloc) throw Anomalie(11);
	if (numall >= _nploidie) throw Anomalie(12);

	return(_tabPall[(numlocus * _nploidie) + numall]->get_Nom());
}



void Individu::affiche (ostream& sortie, int choix) const {
	unsigned long i,j;
	unsigned int nploidie(_Ppop->get_nploidie());

	switch (choix) {
	case 1:
		//affichage d'un individu au format genepop
		sortie << _nom << " ,";

		for (i=0;i< _tabPall.size(); i+=2) {
			sortie << " " << _tabPall[i]->_nom << _tabPall[i+1]->_nom;
		}
		break;		

	case 2:
		//affichage d'un individu au format genepop en tenant compte des introgressions
		sortie << _nom << " ,";

		for (i=0;i< _tabPall.size(); i+=2) {
			sortie << " ";
			if (_Ppop->_tabAllIntrogressant.Existe(_tabPall[i])) {
				sortie << "00";
			}
			else {
				sortie << _tabPall[i]->_nom;
			}
			if (_Ppop->_tabAllIntrogressant.Existe(_tabPall[i+1])) {
				sortie << "00";
			}
			else {
				sortie << _tabPall[i+1]->_nom;
			}
		}
		break;

	case 3:
		//affichage d'un individu au format populations en tenant compte des introgressions
		sortie << _nom << " ,";

		for (i=0;i< _tabPall.size(); i+=nploidie) {
			for (j=0; j < nploidie; j++) {
				if (j==0) sortie << " ";
				else sortie << ":";
				if (_Ppop->_tabAllIntrogressant.Existe(_tabPall[i+j])) {
					sortie << "*";
				}
				if (_tabPall[i+j]->r_estnul()) {
					sortie << "00";
				}
				else {
					sortie << _tabPall[i+j]->_nom;
				}
	//			sortie << _tabPall[i+j]->_nom;
			}
		}
		break;
	case 4:
		//affichage d'un individu au format genetix
		sortie << _nom;
		for (i=0; i < (10 - _nom.size());i ++) sortie << " ";

		for (i=0;i< _tabPall.size(); i+=2) {
			if ((_tabPall[i]->r_estnul()) || (_tabPall[i+1]->r_estnul())) sortie << " 000000";
			else {
				sortie << " ";
				if (_tabPall[i]->_nom.size() == 2) sortie << "0";
				else if (_tabPall[i]->_nom.size() == 1) sortie << "00";
				sortie << _tabPall[i]->_nom;
				if (_tabPall[i+1]->_nom.size() == 2) sortie << "0";
				else if (_tabPall[i+1]->_nom.size() == 1) sortie << "00";
				sortie << _tabPall[i+1]->_nom;
				
			}
		}
		break;		
	case 5:
		//affichage d'un individu au format Fstat

		for (i=0;i< _tabPall.size(); i+=2) {
			if ((_tabPall[i]->r_estnul()) || (_tabPall[i+1]->r_estnul())) sortie << "\t0";
			else sortie << "\t" << _tabPall[i]->_nom << _tabPall[i+1]->_nom;
		}
		break;		
	default:
		break;
	}

}


unsigned long Individu::get_nballnuls (unsigned long locus) const {
	//retourne le nombre d'alleles nuls de l'individu pour le locus
//	long taille (_tabPall.size());
	unsigned long i;
//	unsigned long nploidie(_Ppop->get_nploidie());
	unsigned long resultat(0);

	for (i = 0; i < _nploidie; i++) {
		if (_tabPall[(locus * _nploidie) + i]->_miss) resultat++;
	}
	return(resultat);
}
/** Suppresssion d'un locus */
void Individu::supprLocus(unsigned int numlocus){
	unsigned int nbloc(_Ppop->get_nbloc());
	unsigned int i;

	if (numlocus >= nbloc) throw Anomalie(11);

	for (i = 0; i < _nploidie; i ++) {
		_tabPall.Suppr(numlocus * _nploidie);
	}		
}

void Individu::oPopulationsXML(unsigned int id, ostream & sortie, ostream & infos) const {
  unsigned int i, j, nploidie(_Ppop->get_nploidie()), nbloc(_Ppop->get_nbloc());
  biolib::vecteurs::ChaineCar idXML;

  sortie << "<individual";
  idXML = _Ppop->get_idXML();
  idXML += "i";
  idXML.AjEntier(id);
  //set_idXML(idXML);
  sortie << " id=\"" << idXML << "\"";
  sortie << " nploidy=\"" << nploidie << "\"";
  sortie << " name=\"" << get_nom() << "\"";
  sortie << ">" << endl;

  for (i=0; i < nbloc; i++) {
    sortie << "<locus";
    sortie << " allid=\"";
    for (j=0; j < nploidie; j++) {
      sortie << _tabPall[(i*nploidie)+j]->get_idXML();
      if (j < (nploidie-1)) sortie << " ";
    }
    sortie << "\">";
    sortie << "</locus>" << endl;
  }

  sortie << "</individual>" << endl;
  


}

void Individu::set_Pallele (Allele * Pall, unsigned int pos) {
  unsigned int nploidie(get_nploidie());
  
  if (pos >= nploidie) throw Anomalie(12);

  pos = (Pall->get_Ploc()->get_numloc() * nploidie) + pos;

  _tabPall[pos] = Pall;
  
}
