// RandomGridFactory.hpp
//
//     Created on: 29. 03. 2018
//         Author: Jan Uhlik
//    Modified by:
//
// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library

#ifndef ALIB2_RANDOMGRIDFACTORY_HPP
#define ALIB2_RANDOMGRIDFACTORY_HPP

#include <alib/pair>
#include <alib/tuple>
#include <alib/vector>
#include <random>

namespace graph {

namespace generate {

class RandomGridFactory {
// ---------------------------------------------------------------------------------------------------------------------
 public:

  template<typename TGrid>
  static
  ext::tuple<TGrid, typename TGrid::node_type, typename TGrid::node_type>
  randomGrid(unsigned long height, unsigned long widht, unsigned long max_obstacles);

// ---------------------------------------------------------------------------------------------------------------------

};

// =====================================================================================================================

template<typename TGrid>
ext::tuple<TGrid, typename TGrid::node_type, typename TGrid::node_type>
RandomGridFactory::randomGrid(unsigned long height,
                              unsigned long width,
                              unsigned long max_obstacles) {
  using coordinate_type = typename TGrid::coordinate_type;
  using node_type = typename TGrid::node_type;

  // Seed with a real random value, if available
  std::random_device r;
  std::default_random_engine e1(r());

  // Create distribution
  std::uniform_int_distribution<unsigned long> number_of_obstacles(0, max_obstacles); // Generate number of nodes
  std::uniform_int_distribution<coordinate_type> random_height(0, height - 1); // Generate height coordinate
  std::uniform_int_distribution<coordinate_type> random_width(0, width - 1); // Generate width coordinate
  unsigned long obstacle_cnt = number_of_obstacles(e1);

  TGrid grid(height, width);

  // Generate start and goal node
  node_type start(random_height(e1), random_width(e1));
  node_type goal(random_height(e1), random_width(e1));
 
  // Generate obstacles
  for (unsigned long i = 0; i < obstacle_cnt; ++i) {
    node_type obstacle(random_height(e1), random_width(e1));

    // Try to avoid creating obstacle on place of start or goal vertex
    if (obstacle == start || obstacle == goal) {
        continue;
    }

    grid.addObstacle(std::move(obstacle));
  }

  return ext::make_tuple(grid, start, goal);
}

// ---------------------------------------------------------------------------------------------------------------------

} // namespace generate

} // namespace graph

#endif //ALIB2_RANDOMGRIDFACTORY_HPP
