//  RankingAndCrowdingSelection.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package jmetal.operators.selection;

import jmetal.core.Problem;
import jmetal.core.SolutionSet;
import jmetal.util.Configuration;
import jmetal.util.Distance;
import jmetal.util.JMException;
import jmetal.util.Ranking;
import jmetal.util.comparators.CrowdingComparator;

import java.util.Comparator;
import java.util.HashMap;

/** 
 * This class implements a selection for selecting a number of solutions from
 * a solutionSet. The solutions are taken by mean of its ranking and 
 * crowding ditance values.
 * NOTE: if you use the default constructor, the problem has to be passed as
 * a parameter before invoking the execute() method -- see lines 67 - 74
 */
public class RankingAndCrowdingSelection extends Selection {

  /**
   * stores the problem to solve 
   */
  private Problem problem_ = null ;
  
  /**
   * stores a <code>Comparator</code> for crowding comparator checking.
   */
  private static final Comparator crowdingComparator_ = 
                                  new CrowdingComparator();

  
  /**
   * stores a <code>Distance</code> object for distance utilities.
   */
  private static final Distance distance_ = new Distance();
  
  /**
   * Constructor
   */
  public RankingAndCrowdingSelection(HashMap<String, Object> parameters) {
    super(parameters) ;

  	if (parameters.get("problem") != null)
  		problem_ = (Problem) parameters.get("problem") ;  		

    if (problem_ == null) {
      Configuration.logger_.severe("RankingAndCrowdingSelection.execute: " +
          "problem not specified") ;
      Class cls = java.lang.String.class;
      String name = cls.getName(); 
    } // if

  } // RankingAndCrowdingSelection
  
  /**
   * Constructor
   * @param problem Problem to be solved
   */
  //public RankingAndCrowdingSelection(Problem problem) {
  //  problem_ = problem;
  //} // RankingAndCrowdingSelection

  /**
  * Performs the operation
  * @param object Object representing a SolutionSet.
  * @return an object representing a <code>SolutionSet<code> with the selected parents
   * @throws JMException 
  */
  public Object execute (Object object) throws JMException {
    SolutionSet population = (SolutionSet)object;
    int populationSize     = (Integer)parameters_.get("populationSize");
    SolutionSet result     = new SolutionSet(populationSize);
    
    //->Ranking the union
    Ranking ranking = new Ranking(population);                        

    int remain = populationSize;
    int index  = 0;
    SolutionSet front = null;
    population.clear();

    //-> Obtain the next front
    front = ranking.getSubfront(index);

    while ((remain > 0) && (remain >= front.size())){                
      //Asign crowding distance to individuals
      distance_.crowdingDistanceAssignment(front,problem_.getNumberOfObjectives());                
      //Add the individuals of this front
      for (int k = 0; k < front.size(); k++ ) {
        result.add(front.get(k));
      } // for

      //Decrement remaint
      remain = remain - front.size();

      //Obtain the next front
      index++;
      if (remain > 0) {
        front = ranking.getSubfront(index);
      } // if        
    } // while

    //-> remain is less than front(index).size, insert only the best one
    if (remain > 0) {  // front containt individuals to insert                        
      distance_.crowdingDistanceAssignment(front,problem_.getNumberOfObjectives());
      front.sort(crowdingComparator_);
      for (int k = 0; k < remain; k++) {
        result.add(front.get(k));
      } // for

      remain = 0; 
    } // if

    return result;
  } // execute    
} // RankingAndCrowding
