/***************************************************************************
                          arbre.h  -  description
                             -------------------
    begin                : Mon Dec 18 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.                                   *
 *                                                                         *
 ***************************************************************************/


//modifications le 2/1/2001
//modifications le 4/6/2001

#ifndef ARBRE_H
#define ARBRE_H

#include <string.h>
#include <stdlib.h>
#include <fstream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <functional>

#include "internat.h"
#include "matrices.h"
//#define _(String) gettext (String)
#   ifndef _
#    define _(String) gettext (String)
#   endif

namespace biolib {
namespace arbres {


using namespace std;
using namespace biolib::vecteurs;

template <class _Tp>
struct moins
{
  bool operator()(const _Tp& x, const _Tp& y) const { return *x < *y; }
};

class ArbreVectUINT:public vector<unsigned int>  {
public:
	void f_tri();

	bool Existe (unsigned int element) const {
//cerr << "ArbreVectUINT::Existe "  << endl;
//affiche();
			unsigned int i;
			for (i= 0 ; i < size(); i++) {
//cerr << "ArbreVectUINT::Existe i " << i << endl;
				if (operator[](i) == element) return(true);
			}

			return (false);
		};

	void affiche() const;

	bool operator== (const ArbreVectUINT &rval);

private:
	void f_trishell(unsigned int n,unsigned int j);
	void f_tribulle();
};

class Arbre;
class ArbreNdNoeud;
class ArbreNdOTU;

class ArbreNdBase {
public:
	ArbreNdBase(Arbre * Parbre);
	ArbreNdBase(const ArbreNdBase &) {};
	virtual ~ArbreNdBase();

	virtual void set_longueur(unsigned int pos, float taille);
//	virtual void set_longueur(float longueur) {};

	virtual void set_longueur(ArbreNdBase * Pnoeudbase, float taille);
	virtual void oPhylipRec(ostream & fichier) const;//écriture d'arbres au format Phylip
	virtual void oXML(ostream & fichier) const {};//écriture d'arbres au format XML
	virtual ArbreNdBase * get_Pracine() const;
	virtual ArbreNdBase * get_Pnoeud(unsigned int i) const {return(_tabPNoeuds[i]);};
	virtual unsigned int get_nbbranches() const;
	virtual double get_longueur_branche() const;
	virtual double get_tabLgBranches(unsigned int i) const;
	virtual bool est_racine() const ;
	virtual void set_nom(const string & nom);
	virtual void f_chgt_sens(ArbreNdNoeud * Pancracine, ArbreNdNoeud * Pnvsens);
	virtual int get_posnoeuds(ArbreNdNoeud * Pnoeud) const;

	void set_Pnoeud(unsigned int pos, ArbreNdBase * Pnoeud);

	virtual void fCalcValBootstrap(unsigned int total_bootstrap) {return;};
	virtual double get_longueur_max()const {return(0);} ;
	virtual unsigned int get_nbnoeuds_max()const {return(0);} ;

	virtual ArbreVectUINT* f_load_id_ind() {return (0);};

//interface pour PointPlotArbreBase
protected:

	Arbre * _Parbre;
	vector<ArbreNdBase *> _tabPNoeuds;
	vector<float> _tabLgBranches;
};


class ArbreNdOTU: public ArbreNdBase {
public:
	ArbreNdOTU(Arbre * Parbre, ArbreNdNoeud * Pnoeud);
	ArbreNdOTU(Arbre * Parbre, const string & nom);
	ArbreNdOTU(const ArbreNdOTU & original):ArbreNdBase(original) {};
	virtual ~ArbreNdOTU();

	void set_nom(const string & nom);
//	void set_nom(const char * nom){_nom.assign(nom);};

//	void set_longueur(float longueur){ArbreNdBase::set_longueur((unsigned int) 0,longueur);};

	void set_reference(const string & ref) {strcpy(_reference, ref.c_str());};
	void set_reference(const char * ref){strcpy(_reference, ref);};
	const char * get_reference() const {return (_reference);};

	void set_id(unsigned int id) { _id = id;};
	const string & get_nom() const {return(_nom);};

	inline bool operator== (const ArbreNdOTU &rval){return (_nom == rval._nom);};
	virtual void oPhylipRec(ostream & fichier) const;//écriture d'arbres au format Phylip
	virtual void oXML(ostream & fichier) const;//écriture d'arbres au format XML
	virtual double get_longueur_max()const;
	virtual unsigned int get_nbnoeuds_max()const;
	unsigned int get_id() const {return(_id);};

	virtual ArbreVectUINT* f_load_id_ind();

	inline bool operator< (const ArbreNdOTU &rval){return (strcmp(_reference, rval._reference) < 0);};

private:

//	char _nomchar[50];
	char _reference[50];
	string _nom;

	unsigned int _id;
};


class ArbreNdNoeud: public ArbreNdBase {
public:
	ArbreNdNoeud(Arbre * Parbre, ArbreNdNoeud * Pnoeud=0);
	ArbreNdNoeud(const ArbreNdNoeud & original):ArbreNdBase(original) {};
	ArbreNdNoeud(Arbre * Parbre, unsigned int nbbranches);
	virtual ~ArbreNdNoeud();
	inline bool operator== (const ArbreNdNoeud &rval);

	void AjouterFils(ArbreNdBase * Pnoeud);
	void AjBootstrap();

	double get_val_bootstrap() const;
	void set_force(float force);
	void set_longueur(unsigned int pos, float taille);
	void set_racine(bool oui) {_racine = oui;};
	virtual void oPhylipRec(ostream & fichier) const;//écriture d'arbres au format Phylip
  virtual void oXML(ostream & fichier) const;
	virtual ArbreNdBase * get_Pracine() const;
	virtual unsigned int get_nbbranches() const;
	virtual bool est_racine() const;
	virtual bool ExisteBranche(const ArbreVectUINT & tabInd) const;

	void f_chgt_racine(ArbreNdNoeud * Pracine);
	void f_load_vectind();

	virtual ArbreVectUINT* f_load_id_ind();

	void fCalcValBootstrap(unsigned int total_bootstrap);
	virtual double get_longueur_max() const;
	virtual unsigned int get_nbnoeuds_max()const;

protected:
	void f_chgt_sens(ArbreNdNoeud * Pancracine, ArbreNdNoeud * Pnvsens);

	bool _racine;
	int _force;
//	vector<VectArbreNdOTU *> _tabPPind;
	vector<ArbreVectUINT *> _tabP_tab_id_ind;
	unsigned int _accuvalboot;
};

class Arbre {
public:
	Arbre();
//	Arbre(unsigned int nbessais , unsigned int nbind, unsigned int * Ptemp, char * * PPotu);
	Arbre(const Arbre &) {};
	
	virtual ~Arbre();

	virtual ArbreNdOTU * new_ArbreNdOTU(const string & nom);
	virtual ArbreNdOTU * new_ArbreNdOTU(ArbreNdNoeud * PArbreNdNoeudere);
	virtual ArbreNdNoeud * new_ArbreNdNoeud(unsigned int nbbranches);
	virtual ArbreNdNoeud * new_ArbreNdNoeud(ArbreNdNoeud * PArbreNdNoeudere=0);

	void AjBootstrap(Arbre &);

	virtual void iFichier(istream &);

	void iPhylip(istream &);//lecture d'arbres au format Phylip
	void iNimbus(unsigned int nbessais , unsigned int nbind, unsigned int * Ptemp, char * * PPotu);

	void iDistances(MatriceLD &, int);
	void iDistances(MatriceLD & matrice);
	void f_forceiDistances(MatriceLD & distancesRef,int methodeArbre);

	void oFichier(ostream & fichier, unsigned int format=0) const;
//	void iMega(istream &);//lecture d'arbres au format Mega
	void set_Pracine(ArbreNdNoeud * Pracine);

	void f_test();
	void SquizNoeud(ArbreNdNoeud * Pndaeffacer);

	int PositionInd(const char *) const;

	virtual void f_load_vect_id_ind();

	void reset();
	void fCalcValBootstrap();
	bool get_oui_bootstrap() const {return(_oui_bootstrap);};
	bool get_oui_taille_branches() const {return(_oui_taille_branches);};
	void set_oformat(unsigned int nbformat=1) {_oFormat = nbformat;};
	unsigned int get_nbind() const {return(_tabPind.size());};
	void f_tri_ind_alpha();

	void operator>>(ostream& sortie);

	Titre _titre;

protected:
	ArbreNdNoeud * RechercheNoeud(const ArbreVectUINT &) const;
	const ArbreNdOTU * RecherchePOTU(unsigned int id) const;

	void iNeighborJoining(Matrice & distances);
	void iNeighborJoiningTopo(Matrice & distances);
	void iUPGMA(Matrice & distances);

	float iPhylipRecGetTaille(istream & fichier);
	float iPhylipRecGetForce(istream & fichier);
	void i_PhylipRecGetNom(istream & fichier, char *);
	void iPhylipRec(istream & fichier, ArbreNdNoeud * Pnoeud=0 );//lecture d'arbres au format Phylip
	virtual void iPhylipDefGroupe(const string &) {}; //définition d'un groupe
	void oPhylip(ostream &) const;//écriture d'arbres au format Phylip
  void oXML(ostream & fichier) const;
	virtual void oPhylipEcritGroupes(ostream &) const {};

	void iNimbusRec(ArbreNdNoeud * Pnoeud, MatriceLD & tab_nimbus, unsigned int numessai, unsigned int deb, unsigned int fin, unsigned int longueur=0);
//	void iPhylipRecPasser(istream & fichier);
	void f_trishell_ind_alpha(unsigned int lb, unsigned int ub);
	void f_tribulle_ind_alpha();//	void f_tri_ind_alpha();

	ArbreNdNoeud * _Pracine;
	vector<ArbreNdNoeud *> _tabPnoeuds;
	vector<ArbreNdOTU *> _tabPind;

	bool _oui_taille_branches;
	bool _oui_bootstrap;
	bool _ind_charge;
	unsigned int _cumulbootstrap;
	unsigned int _oFormat; //format de sortie (1 = phylip/newick)

public:
	struct Anomalie{
		Anomalie (int i):le_pb(i){};

		// 1-> Echec de RechercheNoeud
		int le_pb;
		string _message;

		string& fmessage(int num){
			switch (num) {
			case 1:
				_message = _("Error n°1 in Arbre: leaf not found");
				break;
			case 100:
				_message = _("Error reading treefile");
				break;
				
			default:
				_message = _("Error in Arbre");
				break;
			}
			return(_message);
		}
	};

};


inline bool ArbreNdNoeud::operator== (const ArbreNdNoeud &rval) {
	//=>remplir les vecteurs avant
//cerr << "ArbreNdNoeud::operator==" << endl;		
	unsigned int taille(_tabP_tab_id_ind.size());
	if (taille != rval._tabP_tab_id_ind.size()) return (false);
	if (taille == 0) cerr << "ArbreNdNoeud::operator== vide" << endl;
	if (taille == 1) return (*_tabP_tab_id_ind[0] == *rval._tabP_tab_id_ind[0]);

	unsigned int i,j;
	
	bool ok;

//	bool * tab_dejatrouve;
//	tab_dejatrouve = new bool[taille];
 // for (i = 0; i < taille; i++) tab_dejatrouve[i] = false;

	for (i=0; i < (taille - 1); i++) {
		ok = false;
		for (j=0; ((ok == false) && (j < taille)); j++)
//			if (!tab_dejatrouve[j])
			ok = (*_tabP_tab_id_ind[i] == *rval._tabP_tab_id_ind[j]);
//				if (ok = (*_tabPPind[i] == *rval._tabPPind[j])) tab_dejatrouve[j] = true;
//if (ok) cerr << "ArbreNdNoeud::operator== ok " << this << endl;		
		
		if (!ok) return (false);
	}
//	delete tab_dejatrouve;

	return(true);	
}

}//namespace arbres
}//namespace biolib
#endif


