//-------------------------------------------------------------------------
// Get a tab with indexes of speakers with maximum likelihood (mdtm and etf only)
void getTarClientIdx(Config & config, XList & inputList, unsigned long nbLoc, unsigned long * tarTab)
{
	XLine *linep;
	unsigned long cpt=0;
	unsigned long cpttab=0;
	double minLLK=config.getParam("minLLK").toDouble();
	double maxScore=minLLK;
	unsigned long idxTar=0;
    bool verbose=config.existsParam("verbose");
    
	while((linep=inputList.getLine())!=NULL)
	{
		double score=linep->getElement(6).toDouble();
		if (score>=maxScore)
		{
			maxScore=score;		// this is the maximum score
			idxTar=cpt;			// index is just the line
			if (verbose) {cout << "giving highest score to " << linep->getElement(1) << " "<<maxScore << endl;} 
		}                         
		if (cpt%nbLoc==(nbLoc-1))	// when changing segment
		{
			tarTab[cpttab]=idxTar;   // idx of the target goes in the tab
			if (verbose) {cout << linep->getElement(1) << " max score: "<<maxScore <<"idx: "<<idxTar<<"cpt: "<<cpt<< endl;}
			cpttab++;	
			maxScore=minLLK; 	//reset maxScore
		}
		cpt++;
	}
}
//-------------------------------------------------------------------------
// 
unsigned long getIndexOfMaxScore(XList & inputList,unsigned long scoreField, unsigned long segField, unsigned long & i, unsigned long nbListLines)
{
	String seg=inputList.getLine(i).getElement(segField);	//this is a new segment
	long max_score=-200;
	long score;
	unsigned long maxIndex=0; 

	while (inputList.getLine(i).getElement(segField)==seg)	// while same segment test
	{
		score= inputList.getLine(i).getElement(scoreField).toLong();
		if (score >= max_score) {max_score=score; maxIndex=i;}	// store max score and its index
		i++;
		if (i >= nbListLines) break; // break the loop if end of file
	}
return maxIndex;
}
//-------------------------------------------------------------------------
// Produce a tab with mean and cov by segment without the maximum score(mdtm and etf only)
void getSegmentalMeanCovWithoutMax(Config & config, XList & inputList, unsigned long nbLoc, unsigned long * tarTab, double* meanTab, double * covTab)
{
	XLine *linep;
	unsigned long cpt=0;
	unsigned long cpttab=0;
	double minLLK=config.getParam("minLLK").toDouble();
	double maxScore=minLLK;
	double meanAcc=0.0;
	double covAcc=0.0;
	unsigned long idxTar=0;
	bool verbose=config.existsParam("verbose");

	while((linep=inputList.getLine())!=NULL)
	{
		double score=linep->getElement(6).toDouble();
		if (score>=maxScore)
		{
			maxScore=score;		// this is the maximum score
			idxTar=cpt;			// index is just the line
			if (verbose) {cout << "giving highest score to " << linep->getElement(1) << " "<<maxScore << endl;} 
		}
		meanAcc+=score;			// Accumulate mean and cov
		covAcc+=(score*score);
          
		if (cpt%nbLoc==(nbLoc-1))	// when changing segment
		{	
			tarTab[cpttab]=idxTar;
			meanAcc-=maxScore;	//remove max from Stats
			covAcc-=(maxScore*maxScore);
			meanTab[cpttab]=meanAcc;
			covTab[cpttab]=covAcc;
			if (verbose) {cout << linep->getElement(1) << " max score: "<<maxScore <<"idx: "<<idxTar<<"cpt: "<<cpt<< " meanA: "<<meanAcc<<" covA: "<<covAcc<<endl;}
			cpttab++;	
			maxScore=minLLK;
			meanAcc=0.0;
			covAcc=0.0;
		}
		cpt++;
	}
}  
//-------------------------------------------------------------------------
// Produce a tab with mean and cov by segment (mdtm and etf only)
void getSegmentalMeanCov(XList & inputList, unsigned long nbLoc, double* meanTab, double * covTab)
{
	XLine *linep;
	unsigned long cpt=0;
	unsigned long cpttab=0;
	double meanAcc=0.0;
	double covAcc=0.0;
     
	while((linep=inputList.getLine())!=NULL)
	{
		double score=linep->getElement(6).toDouble();
		meanAcc+=score;			// Accumulate mean and cov
		covAcc+=(score*score);
		if (cpt%nbLoc==(nbLoc-1))	// when changing segment
		{
			meanTab[cpttab]=meanAcc;
			covTab[cpttab]=covAcc;
			cpttab++;	
			meanAcc=0.0;
			covAcc=0.0;
		}
		cpt++;
	}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
int IvExtractorEigenDecomposition(Config& config)
{
	String inputWorldFilename = config.getParam("inputWorldFilename");

	// label for selected frames - Only the frames associated with this label, in the label files, will be used
	bool fixedLabelSelectedFrame=true;
	String labelSelectedFrames;
	if (config.existsParam("useIdForSelectedFrame"))    // the ID of each speaker is used as labelSelectedFrame ?
		fixedLabelSelectedFrame=(config.getParam("useIdForSelectedFrame").toBool()==false);  
	if (fixedLabelSelectedFrame)                        // the label is decided by the command line and is unique for the run
		labelSelectedFrames=config.getParam("labelSelectedFrames");
 
try{
	MixtureServer ms(config);
	if (verbose) cout << "(IvExtractor) Approximate i-vector by using Eigen Decomposition"<<endl;
	if (verbose) cout << "(IvExtractor) TotalVariability - Load world model [" << inputWorldFilename<<"]"<<endl;
	MixtureGD& world = ms.loadMixtureGD(inputWorldFilename);      

	//Read the NDX file
	String ndxFilename = config.getParam("targetIdList");

	//Remove the first element of each line which is the model name
	XList tmpFileList(ndxFilename);
	XList fileList;
	for(unsigned long ll=0;ll<tmpFileList.getLineCount();ll++){
		fileList.addLine();
		for(unsigned long i=1;i<tmpFileList.getLine()->getElementCount();i++){
			fileList.getLine(fileList.getLineCount()-1).addElement(tmpFileList.getLine(ll).getElement(i));
		}
	}
	
	//Create and initialise the accumulator
	TVAcc tvAcc(fileList, config);
	Matrix<double> Q(tvAcc.getRankT(),tvAcc.getRankT());
	Matrix<double> D(tvAcc.getNDistrib(),tvAcc.getRankT());

	if(config.existsParam("loadEigenDecompositionParam") && config.getParam("loadEigenDecompositionParam").toBool()){	// Load normalized T matrix and weighted Covariance matrix if pre-computed

		//Load TotalVariability matrix
		String normTFilename = config.getParam("totalVariabilityMatrix") + "_norm";
		tvAcc.loadT(normTFilename, config);

		//Load D and Q matrices
		String dFilename = config.getParam("matrixFilesPath") + config.getParam("totalVariabilityMatrix") + "_EigDec_D" + config.getParam("loadMatrixFilesExtension");
		D.load(dFilename,config);

		String qFilename = config.getParam("matrixFilesPath") + config.getParam("totalVariabilityMatrix") + "_EigDec_Q" + config.getParam("loadMatrixFilesExtension");
		Q.load(qFilename,config);

	}
	else{
		//Load TotalVariability matrix
		tvAcc.loadT(config.getParam("totalVariabilityMatrix"), config);

		// Normalize matrix T
		tvAcc.normTMatrix();

		// Compute weighted co-variance matrix by using UBM weight coefficients
		DoubleSquareMatrix W(tvAcc.getRankT());
		W.setAllValues(0.0);
		tvAcc.getWeightedCov(W,world.getTabWeight(),config);

		// Eigen Decomposition of W to get Q
		Matrix<double> tmpW(W);
		Q.setAllValues(0.0);
		tvAcc.computeEigenProblem(tmpW,Q,tvAcc.getRankT(),config);

		// Compute D matrices (approximation of Tc'Tc matrices)
		D.setAllValues(0.0);
		tvAcc.approximateTcTc(D,Q,config);
	}

	//Load the statistics from files or compute statistics for all segments at once
	if((config.existsParam("loadAccs")) && config.getParam("loadAccs").toBool()){	//load pre-computed statistics
		cout<<"	(IvExtractor) Load Accumulators"<<endl;
		tvAcc.loadN(config);
		tvAcc.loadF_X(config);
	}
	else{															//Compute statistics if they don't exists
		tvAcc.computeAndAccumulateTVStat(config);
		tvAcc.saveAccs(config);
	}

	// Then load the meanEstimate computed by minDiv if required
	DoubleVector meanEstimate = tvAcc.getUbmMeans();
	if(config.existsParam("minDivergence")&& config.getParam("minDivergence").toBool()){
		String minDivName = config.getParam("matrixFilesPath") + config.getParam("meanEstimate") + config.getParam("loadMatrixFilesExtension");
		Matrix<double> tmpMean(minDivName,config);
		for(unsigned long i=0;i<meanEstimate.size();i++){
			meanEstimate[i] = tmpMean(0,i);
		}
	}
	//Update the mean Estimate
	cout<<"	(IvExtractor) Load Mean Estimate"<<endl;
	tvAcc.loadMeanEstimate(meanEstimate);

	//Substract mean from the statistics and normalize co-variance
	tvAcc.normStatistics(config);

	// Estimate I-Vectors
	tvAcc.estimateWEigenDecomposition(D,Q,config);

	cout<<"--------- save IV by File --------"<<endl;
	tvAcc.saveWbyFile(config);
	cout<<"--------- end of process --------"<<endl;


} // fin try
catch (Exception& e) {cout << e.toString().c_str() << endl;}
return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
int IvExtractor(Config& config)
{
	String inputWorldFilename = config.getParam("inputWorldFilename");

	// label for selected frames - Only the frames associated with this label, in the label files, will be used
	bool fixedLabelSelectedFrame=true;
	String labelSelectedFrames;
	if (config.existsParam("useIdForSelectedFrame"))    // the ID of each speaker is used as labelSelectedFrame ?
		fixedLabelSelectedFrame=(config.getParam("useIdForSelectedFrame").toBool()==false);  
	if (fixedLabelSelectedFrame)                        // the label is decided by the command line and is unique for the run
		labelSelectedFrames=config.getParam("labelSelectedFrames");
 
try{
	MixtureServer ms(config);
	if (verbose) cout << "(IvExtractor) TotalVariability - Load world model [" << inputWorldFilename<<"]"<<endl;
	MixtureGD& world = ms.loadMixtureGD(inputWorldFilename);      
	
	//Load the statistics from files or compute statistics for all segments at once
	//Read the NDX file
	String ndxFilename = config.getParam("targetIdList");

	//Remove the first element of each line which is the model name
	XList tmpFileList(ndxFilename);
	XList fileList;
	for(unsigned long ll=0;ll<tmpFileList.getLineCount();ll++){
		fileList.addLine();
		for(unsigned long i=1;i<tmpFileList.getLine()->getElementCount();i++){
			fileList.getLine(fileList.getLineCount()-1).addElement(tmpFileList.getLine(ll).getElement(i));
		}
	}

	//Create and initialise the accumulator
	TVAcc tvAcc(fileList, config);

	//Load TotalVariability matrix
	tvAcc.loadT(config.getParam("totalVariabilityMatrix"), config);

	//Statistics
	if((config.existsParam("loadAccs")) && config.getParam("loadAccs").toBool()){	//load pre-computed statistics
		cout<<"	(IvExtractor) Load Accumulators"<<endl;
		tvAcc.loadN(config);
		tvAcc.loadF_X(config);
	}
	else{															//Compute statistics if they don't exists
		tvAcc.computeAndAccumulateTVStat(config);
		tvAcc.saveAccs(config);
	}

	// Then load the meanEstimate computed by minDiv if required
	DoubleVector meanEstimate = tvAcc.getUbmMeans();
	if(config.existsParam("minDivergence")&& config.getParam("minDivergence").toBool()){
		String minDivName = config.getParam("matrixFilesPath") + config.getParam("meanEstimate") + config.getParam("loadMatrixFilesExtension");
		Matrix<double> tmpMean(minDivName,config);
		for(unsigned long i=0;i<meanEstimate.size();i++){
			meanEstimate[i] = tmpMean(0,i);
		}
	}
	//Update the mean Estimate
	cout<<"	(IvExtractor) Load Mean Estimate"<<endl;
	tvAcc.loadMeanEstimate(meanEstimate);

	//Substract mean from the statistics
	tvAcc.substractM(config);

	//Compute vEvT for each session
	tvAcc.estimateTETt(config);

	// Estimate I-Vectors
	tvAcc.estimateW(config);

	cout<<"--------- save IV by File --------"<<endl;
	tvAcc.saveWbyFile(config);
	cout<<"--------- end of process --------"<<endl;

} // fin try
catch (Exception& e) {cout << e.toString().c_str() << endl;}
return 0;
}
//-------------------------------------------------------------------------
// Retrieve info in a nist file providing the good fields
void retrieveNISTSegmentInfo(XList & inputList, String & gender, String & clientName, String & seg, unsigned long genderField, unsigned long nameField, unsigned long segField, unsigned long & i) {
	if (inputList.getLine(i).getElement(genderField)=="F") {gender="f";} else {gender="m";}
	clientName=inputList.getLine(i).getElement(nameField);
	seg=inputList.getLine(i).getElement(segField);
}
void launchTurnDetectionProcess(Config & config){

	String outputFilesPath=config.getParam("outputFilesPath");

	String inputListFileName = config.getParam("listFileToSegment");	//file including the list of files to segment
	XLine classToAnalyse;	//Array of labels to analyze
	classToAnalyse.reset();

	if(verbose){
		cout << "*********** Current Configuration ***************" << endl;
		for(unsigned long i=0; i<config.getParamCount(); i++){
			cout << config.getParamName(i) << " => " <<  config.getParamContent(i) << endl;
		}
		cout << "*************************************************" << endl;
	}

	try{
		XList listLabel;
		XList listFileName;
		try{
			listFileName.load(inputListFileName,config);
		}
		catch(FileNotFoundException& e){
			cout<<"There is no files to segment !"<<endl;
		      	exit(-1);
		}
		listFileName.rewind();
		XLine *filep;
		while ((filep=listFileName.getLine()) != NULL){							// For each stream of audio data (in several files in the same line)
			const XLine & listFile=filep->getElements();						// One or several files, as several part of the same stream
		      	MixtureServer ms(config);
	      		StatServer ss(config, ms);
		      	SegServer Resultat;
	      		FeatureServer fs(config,listFile);							// Reading the features (one or more files) 
		      	SegServer segmentsServer;								// Create the segment server for managing the segments/clusters
	      		LabelServer labelServer;								// Create the lable server, for indexing the segments/clusters
		      	initializeClusters(listFile,segmentsServer,labelServer,config);				// Reading the segmentation files for each feature input file
	      		verifyClusterFile(segmentsServer,fs,config);						// Verify if the segments ending before the end of the feature files
			String fileInit=listFile.getElement(0);
			config.setParam("fileSize", String::valueOf(fs.getFeatureCountOfASource(fileInit)));	

			if(config.existsParam("fileRefPath")){
				// assumption: all the segments in the segment server come from the same source file !!!
				displayAllSegmentsFromRef(config, fileInit, fs.getFeatureCountOfASource(fileInit));
			}

			for(unsigned long icluster=0;icluster<segmentsServer.getClusterCount();icluster++){	// for each cluster
				SegCluster& cluster=segmentsServer.getCluster(icluster);
  				SegServer segOutputServer;
  				TurnDetection(config,cluster,segOutputServer,ss,fs,ms,labelServer);
				displayAllSegments(config,segOutputServer); 
  				for(unsigned long i=0;i<segOutputServer.getSegCount();i++){
    					Seg& segment=segOutputServer.getSeg(i);
	    				Resultat.createSeg(segment.begin(),segment.length(),segment.labelCode(),segment.string(),segment.sourceName());
	  			}
			}//for icluster
			saveSegmentation(config,Resultat,fs,outputFilesPath,1);
		}// while
	} // end try
	catch (Exception& e){ 
		cout << e.toString().c_str() << endl;
	}
}//launchTurnDetectionProcess