package org.uma.jmetal.algorithm.multiobjective.spea2.util;

import org.apache.commons.lang3.tuple.Pair;
import org.uma.jmetal.operator.SelectionOperator;
import org.uma.jmetal.solution.Solution;
import org.uma.jmetal.util.SolutionListUtils;
import org.uma.jmetal.util.comparator.StrengthFitnessComparator;
import org.uma.jmetal.util.solutionattribute.impl.LocationAttribute;
import org.uma.jmetal.util.solutionattribute.impl.StrengthRawFitness;

import java.util.*;

/**
 * @author Juanjo Durillo
 * @param <S>
 */
@SuppressWarnings("serial")
public class EnvironmentalSelection<S extends Solution<?>> implements SelectionOperator<List<S>,List<S>> {

  private int solutionsToSelect = 0;
  private final StrengthRawFitness<S> strengthRawFitness= new StrengthRawFitness<S>();

  public EnvironmentalSelection(int solutionsToSelect) {
    this.solutionsToSelect = solutionsToSelect;
  }

  @Override
  public List<S> execute(List<S> source2) {
    int size;
    List<S> source = new ArrayList<>(source2.size());
    source.addAll(source2);
    if (source2.size() < this.solutionsToSelect) {
      size = source.size();
    } else {
      size = this.solutionsToSelect;
    }

    List<S> aux = new ArrayList<>(source.size());

    int i = 0;
    while (i < source.size()){
      double fitness = (double) this.strengthRawFitness.getAttribute(source.get(i));
      if (fitness<1.0){
        aux.add(source.get(i));
        source.remove(i);
      } else {
        i++;
      }
    }

    if (aux.size() < size){
      StrengthFitnessComparator<S> comparator = new StrengthFitnessComparator<S>();
      Collections.sort(source,comparator);
      int remain = size - aux.size();
      for (i = 0; i < remain; i++){
        aux.add(source.get(i));
      }
      return aux;
    } else if (aux.size() == size) {
      return aux;
    }

    double [][] distance = SolutionListUtils.distanceMatrix(aux);
    List<List<Pair<Integer, Double>> > distanceList = new ArrayList<>();
    LocationAttribute<S> location = new LocationAttribute<S>(aux);
    for (int pos = 0; pos < aux.size(); pos++) {
      List<Pair<Integer, Double>> distanceNodeList = new ArrayList<>();
      for (int ref = 0; ref < aux.size(); ref++) {
        if (pos != ref) {
          distanceNodeList.add(Pair.of(ref, distance[pos][ref]));
        }
      }
      distanceList.add(distanceNodeList);
    }


    for (int q = 0; q < distanceList.size(); q++){
      Collections.sort(distanceList.get(q),new Comparator<Pair<Integer, Double>> () {
        @Override
        public int compare(Pair<Integer, Double> pair1, Pair<Integer, Double> pair2) {
          if (pair1.getRight()  < pair2.getRight()) {
            return -1;
          } else if (pair1.getRight()  > pair2.getRight()) {
            return 1;
          } else {
            return 0;
          }
        }
      });

    }

    while (aux.size() > size) {
      double minDistance = Double.MAX_VALUE;
      int toRemove = 0;
      i = 0;
      Iterator<List<Pair<Integer, Double>>> iterator = distanceList.iterator();
      while (iterator.hasNext()){
        List<Pair<Integer, Double>> dn = iterator.next();
        if (dn.get(0).getRight() < minDistance){
          toRemove = i;
          minDistance = dn.get(0).getRight();
          //i y toRemove have the same distance to the first solution
        } else if (dn.get(0).getRight() == minDistance) {
          int k = 0;
          while ((dn.get(k).getRight() ==
              distanceList.get(toRemove).get(k).getRight()) &&
              k < (distanceList.get(i).size()-1)) {
            k++;
          }

          if (dn.get(k).getRight() <
              distanceList.get(toRemove).get(k).getRight()) {
            toRemove = i;
          }
        }
        i++;
      }

      int tmp =  (int) location.getAttribute(aux.get(toRemove));
      aux.remove(toRemove);
      distanceList.remove(toRemove);

      Iterator<List<Pair<Integer, Double>>> externIterator = distanceList.iterator();
      while (externIterator.hasNext()){
        Iterator<Pair<Integer, Double>> interIterator = externIterator.next().iterator();
        while (interIterator.hasNext()){
          if (interIterator.next().getLeft() == tmp){
            interIterator.remove();
            continue;
          }
        }
      }
    }
    return aux;
  }

}

@@ -2361,17 +2361,16 @@
 %0A    %7D%0A%0A
-%0A
     for 
@@ -2459,154 +2459,24 @@
 (q),
-new Comparator%3CPair%3CInteger, Double%3E%3E () %7B%0A        @Override%0A        public int compare(Pair%3CInteger, Double%3E pair1, Pair%3CInteger, Double%3E
+ (pair1,
  pair2) 
 %7B%0A  
@@ -2471,20 +2471,21 @@
  pair2) 
+-%3E 
 %7B%0A
-  
         
@@ -2534,26 +2534,24 @@
 %7B%0A          
-  
 return -1;%0A 
@@ -2553,26 +2553,24 @@
 -1;%0A        
- 
- 
 %7D else if (p
@@ -2614,26 +2614,24 @@
 %7B%0A          
-  
 return 1;%0A  
@@ -2632,26 +2632,24 @@
  1;%0A        
-  
 %7D else %7B%0A   
@@ -2651,26 +2651,24 @@
 %7B%0A          
- 
- 
 return 0;%0A  
@@ -2665,28 +2665,16 @@
 turn 0;%0A
-          %7D%0A
         
@@ -2685,17 +2685,16 @@
     %7D);%0A
-%0A
     %7D%0A%0A 
@@ -3206,20 +3206,24 @@
 tRight()
- == 
+.equals(
 minDista
@@ -3226,16 +3226,17 @@
 istance)
+)
  %7B%0A     
@@ -3289,19 +3289,24 @@
 tRight()
- ==
+.equals(
 %0A       
@@ -3357,16 +3357,17 @@
 Right())
+)
  &&%0A    

Fix a bug in class org.uma.jmetal.algorithm.multiobjective.spea2.util.EnviromentalSelection