// ****************************************************************************
//
//          Aevol - An in silico experimental evolution platform
//
// ****************************************************************************
//
// Copyright: See the AUTHORS file provided with the package or <www.aevol.fr>
// Web: http://www.aevol.fr/
// E-mail: See <http://www.aevol.fr/contact/>
// Original Authors : Guillaume Beslon, Carole Knibbe, David Parsons, Jonathan Rouzaud-Cornabas
//
// This program 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 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ****************************************************************************

#include "FuzzyFactory.h"

#include <omp.h>

#include <cassert>
#include <list>
#include <memory>

#include "AbstractFuzzy.h"
#include "Discrete_Double_Fuzzy.h"
#include "Vector_Fuzzy.h"

namespace aevol {

FuzzyFlavor FuzzyFactory::flavor_ = FuzzyFlavor::UNSET;
size_t FuzzyFactory::sampling_ = 0;

std::vector<AbstractFuzzy*> FuzzyFactory::available_objects_ = {};
std::vector<std::unique_ptr<AbstractFuzzy>> FuzzyFactory::data_ = {};

FuzzyFactory::FuzzyFactory(const FuzzyFlavor& flavor, size_t sampling, size_t pop_size) {
  assert(flavor != FuzzyFlavor::UNSET); // Special value, not allowed
  assert(flavor_ == FuzzyFlavor::UNSET  // First instanciation
         or flavor == flavor_);         // New instance, uniform flavor across instances
  assert(sampling_ == 0                 // First instanciation
         or sampling == sampling_);     // New instance, uniform sampling across instances
  flavor_ = flavor;
  sampling_ = sampling;
  init(pop_size);
}

FuzzyFactory::~FuzzyFactory() = default;

auto FuzzyFactory::make_fuzzy(FuzzyFlavor flavor, size_t sampling) -> std::unique_ptr<AbstractFuzzy> {
  switch (flavor) {
    case FuzzyFlavor::VECTOR:
      return std::make_unique<Vector_Fuzzy>();
    case FuzzyFlavor::DISCRETE_DOUBLE_TABLE:
      return std::make_unique<Discrete_Double_Fuzzy>(sampling);
    default:
      return nullptr;
  }
}

auto FuzzyFactory::createFuzzy() const -> std::unique_ptr<AbstractFuzzy> {
  return make_fuzzy(flavor_, sampling_);
}

void FuzzyFactory::init(size_t pop_size) {
  if (not data_.empty()) { // Pool already exists, nothing to do
    return;
  }

  // Resize containers to the number of objects in pool
  auto pool_size = pop_size * 2;
  data_.resize(pool_size);
  available_objects_.resize(pool_size);

  // Create fuzzy objects and make them available
  for (auto i = size_t{0}; i < available_objects_.size(); ++i) {
    data_[i] = createFuzzy();
    available_objects_[i] = data_[i].get();
  }
}

}  // namespace aevol
