/*************************************************************************
* 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:
#ifndef CLUSTERPOPULATION_H
#define CLUSTERPOPULATION_H

#include "Clustering.h"
#include "../../ToolnDef/SmartPointer.h"
template <class T,class U>
class ClusterPopulation: public T{
public:

	Cluster <U> clst;
	//static int m_numPeaksFound;
public:

	static int ms_popNum;
    static Optima *msp_preOptima,*msp_converge, *msp_nonConverge;

	static bool checkOverlapping();
	static void addPopulation(ClusterPopulation &w);
	static void deletePopulation(int index);
	//static int computePeaksFound();

public:
	ClusterPopulation(void);
	ClusterPopulation(const int pop_size,bool mode=true);
	ClusterPopulation(Group<U> &g);
	ClusterPopulation(ClusterPopulation &s);
	virtual ~ClusterPopulation(void){};
	ClusterPopulation &operator= ( ClusterPopulation &s);
	bool isConverged();
	void checkOverCrowd();

	int evolve();

};


template <class T,class U>
int ClusterPopulation<T,U>::ms_popNum=0;
template <class T,class U>
Optima *ClusterPopulation<T,U>::msp_preOptima=0;
template <class T,class U>
Optima *ClusterPopulation<T,U>::msp_nonConverge=0;
template <class T,class U>
Optima *ClusterPopulation<T,U>::msp_converge=0;
//template <class T,class U>
//int ClusterPopulation<T,U>::m_numPeaksFound=0;


template <class T,class U>
ClusterPopulation<T,U>::ClusterPopulation(void):T(){
}

template <class T,class U>
ClusterPopulation<T,U>::ClusterPopulation(const int pop_size,bool mode):T(pop_size,mode){

}

template <class T,class U>
ClusterPopulation<T,U>::ClusterPopulation( Group<U> &g):T(g){

}

template <class T,class U>
ClusterPopulation<T,U>::ClusterPopulation( ClusterPopulation &s):T(s){
	clst=s.clst;
}

template <class T,class U>
bool ClusterPopulation<T,U>::isConverged(){
	if(this->m_curRadius<=Global::g_sigma)
		return true;
	else return false;
}
template <class T,class U>
ClusterPopulation<T,U> &ClusterPopulation<T,U>::operator= ( ClusterPopulation &s){

		if(this==&s) return *this;

		if(this->m_popsize!=s.m_popsize){
			Throw(Logic_error("the size of two populations must be the same in assignment operator"));
			return *this;
		}

		T::operator=(s);
		clst=s.clst;
		return *this;
}


template <class T,class U>
void ClusterPopulation<T,U>::checkOverCrowd(){
	if(this->m_popsize<= Global::g_subSize) return;
	for(int i=0;i<this->m_popsize;i++) this->mp_pop[i].m_flag=0;
	int num=this->m_popsize-Global::g_subSize;
	int *id=new int[num];
	for(int i=0;i<num;i++){
		int index,j=0;
		while(this->mp_pop[j].m_flag!=0&& j<this->m_popsize)j++;
		index=j;

		for(int j=index+1;j<this->m_popsize;j++){
			if(this->mp_pop[j].m_flag==0&&!(this->mp_pop[j].getRepresentative()>(this->mp_pop[index].getRepresentative()))){
				index=j;
			}
		}
		id[i]=this->mp_pop[index].m_id;
		this->mp_pop[index].m_flag=1;
	}
	this->deleteIndividual(id,num);
	delete [] id;
}

template <class T,class U>
void ClusterPopulation<T,U>::addPopulation(ClusterPopulation<T,U>  & w){

	ClusterPopulation<T,U> ::msp_subPop.push_back(&w);
	ms_popNum++;

}

template <class T,class U>
void ClusterPopulation<T,U>::deletePopulation(int index){

	if(index<0||index>=ms_popNum) Throw(Logic_error("no subswarm of index"));
	delete  ClusterPopulation<T,U>::msp_subPop[index];
	 ClusterPopulation<T,U>::msp_subPop[index]=0;
	 ClusterPopulation<T,U>::msp_subPop.erase(T::msp_subPop.begin()+index);
	ms_popNum--;
}

template <class T,class U>
bool ClusterPopulation<T,U>::checkOverlapping(){
	for(int i=0;i<ms_popNum;i++){
		for(int j=i+1;j<ms_popNum;j++){
			double dist=dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[i])->m_center.getDistance(dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[j])->m_center);

			if(dist<dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[i])->m_initialRadius||dist<dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[j])->m_initialRadius){

				int c1=0,c2=0;
				for(int k=0;k<ClusterPopulation<T,U>::msp_subPop[j]->m_popsize;k++){
					dist=dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[i])->m_center.getDistance(ClusterPopulation<T,U>::msp_subPop[j]->mp_pop[k].getRepresentative());
					if(dist<dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[i])->m_initialRadius) c1++;
				}

				for(int k=0;k<ClusterPopulation<T,U>::msp_subPop[i]->m_popsize;k++){
					dist=dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[j])->m_center.getDistance(ClusterPopulation<T,U>::msp_subPop[i]->mp_pop[k].getRepresentative());
					if(dist<dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[i])->m_initialRadius) c2++;
				}
				if(c1>ClusterPopulation<T,U>::msp_subPop[j]->m_popsize*Global::g_overlapDegree&&c2>ClusterPopulation<T,U>::msp_subPop[i]->m_popsize*Global::g_overlapDegree){
					if(dynamic_cast<T*>(ClusterPopulation<T,U>::msp_subPop[i])->m_best>(dynamic_cast<T*>(ClusterPopulation<T,U>::msp_subPop[j])->m_best)){	
						dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[i])->addIndividual(*dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[j]));
					
						ClusterPopulation<T,U>::deletePopulation(j);
						if(Global::gp_algorithm->getID()==ALG_CPSO)
						msp_nonConverge->deleteOptimum(j);
					}else{
						dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[j])->addIndividual(*dynamic_cast <ClusterPopulation<T,U> *>(ClusterPopulation<T,U>::msp_subPop[i]));
						ClusterPopulation<T,U>::deletePopulation(i);
						if(Global::gp_algorithm->getID()==ALG_CPSO)
						msp_nonConverge->deleteOptimum(i);
					}
					return true;
				}
			}
		}
	}
	return false;
}

/*
template <class T,class U>
int ClusterPopulation<T,U>::computePeaksFound(){

    for(int k=0;k<dynamic_cast<DynamicProblem*>(Global::gp_problem)->getNumberofPeak();k++ ){
		for(int j=0;j<ms_popNum;j++){
		    double *peak=const_cast <double *> (dynamic_cast<DynamicContinuous*>(Global::gp_problem)->getPeak(k));

            if(dynamic_cast<T*>(ClusterPopulation<T,U>::msp_subPop[j])->m_best.getDistance(peak)<Global::g_sigma){
                if(! ClusterPopulation<T,U> ::msp_subPop[j]->m_flag&& dynamic_cast<DynamicContinuous*>(Global::gp_problem)->isVisable(k)){
                    ClusterPopulation<T,U> ::msp_subPop[j]->m_flag=true;
                    m_numPeaksFound++;
                    break;
                }
            }
        }

    }

	return m_numPeaksFound;

}
*/

template <class T,class U>
int ClusterPopulation<T,U>::evolve(){
	int flag=T::evolve();
	if(flag==2){
		// dimensional change
        if(dynamic_cast<DynamicProblem*> (Global::gp_problem)->getDirDimensionChange()==true){
            for(int i=0;i<ms_popNum;i++)  ClusterPopulation<T,U> ::msp_subPop[i]->increaseDimension();
            if(msp_preOptima)
            for(int i=0;i<msp_preOptima->m_number;i++)          msp_preOptima->mp_optima[i].getLocation().increaseDimension();
            if(msp_converge)
            for(int i=0;i<msp_converge->m_number;i++)          msp_converge->mp_optima[i].getLocation().increaseDimension();
            if(msp_nonConverge)
            for(int i=0;i<msp_nonConverge->m_number;i++)          msp_nonConverge->mp_optima[i].getLocation().increaseDimension();
        }else{
            for(int i=0;i<ms_popNum;i++)  ClusterPopulation<T,U> ::msp_subPop[i]->decreaseDimension();
            if(msp_preOptima)
            for(int i=0;i<msp_preOptima->m_number;i++)          msp_preOptima->mp_optima[i].getLocation().decreaseDimension();
            if(msp_converge)
            for(int i=0;i<msp_converge->m_number;i++)          msp_converge->mp_optima[i].getLocation().decreaseDimension();
            if(msp_nonConverge)
            for(int i=0;i<msp_nonConverge->m_number;i++)          msp_nonConverge->mp_optima[i].getLocation().decreaseDimension();

        }
        for(int i=0;i<ms_popNum;i++)  ClusterPopulation<T,U> ::msp_subPop[i]->m_flag=false;
       // m_numPeaksFound=0;
        return 2;
	}else if(flag==1){
        if(msp_nonConverge&&msp_converge&&msp_preOptima){
        SmartPtr <Optima> tlo(*msp_nonConverge+*msp_converge);

        *msp_preOptima=*tlo;
        for(int i=0;i<msp_preOptima->m_number;i++) msp_preOptima->mp_optima[i].m_location.evaluate(false);

        }

        for(int i=0;i<ms_popNum;i++)  ClusterPopulation<T,U> ::msp_subPop[i]->m_flag=false;
    //    m_numPeaksFound=0;

		return 1;
	}
	if(gIsTerminate()) return 0;
	this->computeCenter();
	this->updateCurRadius();
	return flag;
};


#endif // CLUSTERPOPULATION_H
