#ifndef CLUSTERINGPSO_H_INCLUDED
#define CLUSTERINGPSO_H_INCLUDED

/*************************************************************************
* Project: Library of Evolutionary Algoriths
*************************************************************************
* Author: Changhe Li & Ming Yang
* Email: changhe.lw@google.com Or yangming0702@gmail.com
* Language: C++
*************************************************************************
*  This file is part of EAlib. This library 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, or (at your option) any later version.
*************************************************************************/
// Created: 21 September 2011
// Last modified:

/*
S. Yang and C. Li, “A clustering particle swarm optimizer for locating
and tracking multiple optima in dynamic environments,” IEEE Trans.
Evol. Comput., vol. 14, no. 6, pp. 959–974, 2010.
*/

#include "../../Clustering/ClusterPopulation.h"
#include "../../../PerformanceMeasure/PopulationInforDOP.h"
template<typename T, typename U>
class ClusteringMethod:public ClusterPopulation<T,U>{

public:
    ClusteringMethod():ClusterPopulation<T,U>(){
        strcpy(this->ma_name,"CPSO");
        this->m_algPar<<"Global population size: "<<Global::g_gPopsize<<"; sub-population size: "<<Global::g_subSize<<"; Overlap degree:"<<Global::g_overlapDegree<<";";
    }

    ~ClusteringMethod(){

    }


static void clearMemory(){

	delete ClusterPopulation<T,U>::msp_converge;
	ClusterPopulation<T,U>::msp_converge=0;
	delete ClusterPopulation<T,U>::msp_nonConverge;
	ClusterPopulation<T,U>::msp_nonConverge=0;
}



static void clearPreEnvironment(ClusterPopulation<T,U> &cradle){
    clearMemory();

	for(int j=0;j<ClusterPopulation<T,U>::ms_popNum;j++){
			delete ClusterPopulation<T,U>::msp_subPop[j];
			ClusterPopulation<T,U>::msp_subPop[j]=0;
	}
	ClusterPopulation<T,U>::ms_popNum=0;
	ClusterPopulation<T,U>::msp_subPop.clear();
	delete  &cradle;
	Population<U>::ms_gNumIndis=0;

}


 ClusterPopulation<T,U>  *initializePopulation(){


	ClusterPopulation<T,U> *cradle=0;


    if(dynamic_cast<DynamicProblem*> (Global::gp_problem)->getFlagDimensionChange()){
        cradle = new ClusterPopulation<T,U>(Global::g_gPopsize);

	}else{
        cradle = new ClusterPopulation<T,U>(Global::g_gPopsize-ClusterPopulation<T,U>::msp_preOptima->m_number);
        cradle->addIndividual(*ClusterPopulation<T,U>::msp_preOptima);
    }


	ClusterPopulation<T,U>::msp_converge=new Optima();
	ClusterPopulation<T,U>::msp_nonConverge= new Optima();

	createSubswarms(*cradle);
	return cradle;
}


 void createSubswarms( ClusterPopulation<T,U> &cradle){
	cradle.clst.initialize(cradle.mp_pop,cradle.m_popsize);
	cradle.clst.roughClustering();

	for(int k=0;k< cradle.clst.m_number;k++){
		//cout<<"pop "<<k<<" size "<<cradle.clst.mp_group[k].m_number<<endl;
		ClusterPopulation<T,U> *s=new ClusterPopulation<T,U>( cradle.clst.mp_group[k]);

		ClusterPopulation<T,U>::addPopulation(*s);

		int *id=new int[ cradle.clst.mp_group[k].m_number];
		for(int j=0;j< cradle.clst.mp_group[k].m_number;j++) id[j]= cradle.clst.mp_group[k].mp_member[j].m_id;
		cradle.deleteIndividual(id, cradle.clst.mp_group[k].m_number);
		Optimum lo;

		lo.m_location= cradle.clst.mp_group[k].m_best;
		lo.m_radius= cradle.clst.mp_group[k].m_radius;

		SmartPtr <Optima> tlo(*ClusterPopulation<T,U>::msp_nonConverge+lo);
		*ClusterPopulation<T,U>::msp_nonConverge=*tlo;

		delete [] id;
		id=0;
	}
	cradle.clst.freeMemory();
    #ifdef EALIB
	PopulationInforDOP::getPopInfor()->input(Global::gp_problem->getEvaluations(),ClusterPopulation<T,U>::ms_gNumIndis,ClusterPopulation<T,U>::msp_nonConverge->m_number,ClusterPopulation<T,U>::computePeaksFound()
                                             , dynamic_cast<DynamicContinuous*>(Global::gp_problem)->getNumofVisablePeaks(),this->getAvgInitialRadius(),this->getAvgCurRadius());
#endif
}


 int evolve(ClusterPopulation<T,U> & cradle){

    if(cradle.m_popsize>0){
        Chromosome x;
        x=cradle.m_best;
        int flag;
        flag=cradle.evolve();
        if(flag==2) return 2; //inform algorithm if dimensional change happens, otherwise algorithm needs to detect the change
        if(gIsTerminate()) return 0;
		double xfit=x.getObj();
        if(!dynamic_cast<DynamicProblem * >(Global::gp_problem)->predictChange(1)){
			//the detection will cost one evalution, which may trigger the enviromental change. In such case, change detection does not work
			x.evaluate();
		}
        if(flag==1&&xfit==x.getObj()){
            cout<<"detection failed"<<endl;
        }
        if(xfit!=x.getObj()){
            return 1;
        }

    }

	#ifdef DEMON_EALIB
	if(ClusterPopulation<T,U>::ms_popNum>0) Buffer<U>::updateBuffer(ClusterPopulation<T,U>::msp_subPop);
	else  Buffer<U>::updateBuffer(&cradle);
#endif

    for(int k=0;k<ClusterPopulation<T,U>::ms_popNum;k++){

        Chromosome x;
        x=dynamic_cast<T*>(ClusterPopulation<T,U>::msp_subPop[k])->m_best;
		//x.printToScreen();
		double xfit=x.getObj();
		int flag;
		flag=dynamic_cast<T*>(ClusterPopulation<T,U>::msp_subPop[k])->evolve();
        if(flag==2) return 2;//inform algorithm if dimensional change happens, otherwise algorithm needs to detect the change
		if(gIsTerminate()) return 0;
		//x.printToScreen();

		if(!dynamic_cast<DynamicProblem * >(Global::gp_problem)->predictChange(1)){
			//the detection will cost one evalution, which may trigger the enviromental change. In such case, change detection does not work
			x.evaluate();
		}


        if(flag==1&&xfit==x.getObj()){
            cout<<"detection failed"<<endl;
        }
        if(xfit!=x.getObj()){
            return 1;
        }

        if(dynamic_cast<T*>(ClusterPopulation<T,U>::msp_subPop[k])->m_best>(ClusterPopulation<T,U>::msp_nonConverge->mp_optima[k].m_location))
        ClusterPopulation<T,U>::msp_nonConverge->mp_optima[k].m_location=dynamic_cast<T*>(ClusterPopulation<T,U>::msp_subPop[k])->m_best;

    }

    if(Population<U>::ms_gNumIndis>=Global::g_subSize){
        while(ClusterPopulation<T,U>::checkOverlapping());

        for(int k=0;k<ClusterPopulation<T,U>::ms_popNum;k++)dynamic_cast<ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[k])->checkOverCrowd();

        //convergence check
        for(int k=0;k<ClusterPopulation<T,U>::ms_popNum;k++){
            if(dynamic_cast<ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[k])->isConverged()){
                Optimum lo;
                lo.m_location=dynamic_cast<T *>(ClusterPopulation<T,U>::msp_subPop[k])->m_best;
                lo.m_radius=dynamic_cast<T *>(ClusterPopulation<T,U>::msp_subPop[k])->m_initialRadius;

                SmartPtr <Optima> tlo(*ClusterPopulation<T,U>::msp_converge+lo);
                *ClusterPopulation<T,U>::msp_converge=*tlo;

                ClusterPopulation<T,U>::msp_nonConverge->deleteOptimum(k);
                ClusterPopulation<T,U>::deletePopulation(k);
                k--;
            }
        }
    }
    #ifdef EALIB
	PopulationInforDOP::getPopInfor()->input(Global::gp_problem->getEvaluations(),Population<U>::ms_gNumIndis,ClusterPopulation<T,U>::msp_nonConverge->m_number+ClusterPopulation<T,U>::msp_converge->m_number,ClusterPopulation<T,U>::computePeaksFound(), 
		dynamic_cast<DynamicContinuous*>(Global::gp_problem)->getNumofVisablePeaks(),this->getAvgInitialRadius(),this->getAvgCurRadius());
#endif
    if(Population<U>::ms_gNumIndis<Global::g_subSize){

        if(dynamic_cast<DynamicProblem * >(Global::gp_problem)->predictChange(Global::g_subSize-cradle.m_popsize)){
             cradle.addIndividual(Global::g_subSize-cradle.m_popsize,false);
        }else{
             cradle.addIndividual(Global::g_subSize-cradle.m_popsize);
        }


    }

	return 0;

}

int run(){

    ClusterPopulation<T,U>::msp_preOptima=new Optima();
    ClusterPopulation<T,U> *cradle=initializePopulation();

     while(!gIsTerminate()){
         //cout<<Global::g_runIdx+1<<" "<<Global::gp_problem->getEvaluations()<<endl;
         if(evolve(*cradle)!=0){
             //cout<<"Change happend"<<endl;
            clearPreEnvironment(*cradle);
            cradle=initializePopulation();
         }

     }
	
	#ifdef DEMON_EALIB
	 if(ClusterPopulation<T,U>::ms_popNum>0) Buffer<U>::updateBuffer(ClusterPopulation<T,U>::msp_subPop);
	else  Buffer<U>::updateBuffer(cradle);
#endif

	clearPreEnvironment(*cradle);
    delete ClusterPopulation<T,U>::msp_preOptima;

    return 0;

}


};






#endif // CLUSTERINGPSO_H_INCLUDED
