// SquareGrid4.hpp
//
//     Created on: 31. 01. 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_SQUAREGRID4_HPP
#define ALIB2_SQUAREGRID4_HPP

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

#include "SquareGrid.hpp"

namespace grid {

template<typename TCoordinate, typename TEdge>
class SquareGrid4 final : public SquareGrid<TCoordinate, TEdge> {
// ---------------------------------------------------------------------------------------------------------------------

 public:
  using coordinate_type = TCoordinate;
  using edge_type = TEdge;
  using node_type = ext::pair<TCoordinate, TCoordinate>;
  using direction_type = SquareGridDirections;

// =====================================================================================================================
// Constructor, Destructor, Operators
 public:
  explicit SquareGrid4(TCoordinate height, TCoordinate width);

// =====================================================================================================================
// GridBase interface
 public:
  int compare(const GridBase &other) const override;

  virtual int compare(const SquareGrid4 &other) const;

// =====================================================================================================================
// GridInterface interface

 public:

  bool isValidDirection(direction_type direction) const override;

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

 protected:
  TEdge createEdge(const node_type &a, const node_type &b) const override;

// ---------------------------------------------------------------------------------------------------------------------
// =====================================================================================================================
// GraphInterface interface

 public:

  std::string name() const override;

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

};

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

template<typename TCoordinate, typename TEdge>
SquareGrid4<TCoordinate, TEdge>::SquareGrid4(TCoordinate height, TCoordinate width)
    : SquareGrid<TCoordinate, TEdge>(height, width) {
}

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

template<typename TCoordinate, typename TEdge>
bool SquareGrid4<TCoordinate, TEdge>::isValidDirection(SquareGrid4::direction_type direction) const {
  switch (direction) {
    case SquareGridDirections::north : return true;
    case SquareGridDirections::south : return true;
    case SquareGridDirections::east : return true;
    case SquareGridDirections::west : return true;
    default: return false;
  }
}

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

template<typename TCoordinate, typename TEdge>
TEdge SquareGrid4<TCoordinate, TEdge>::createEdge(const SquareGrid4::node_type &a,
                                                  const SquareGrid4::node_type &b) const {
  return TEdge(a, b);
}

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

template<typename TCoordinate, typename TEdge>
std::string SquareGrid4<TCoordinate, TEdge>::name() const {
  return "SquareGrid4";
}

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

template<typename TCoordinate, typename TEdge>
int SquareGrid4<TCoordinate, TEdge>::compare(const GridBase &other) const {
  if (ext::type_index(typeid(*this)) == ext::type_index(typeid(other))) return this->compare((decltype(*this)) other);
  return ext::type_index(typeid(*this)) - ext::type_index(typeid(other));
}

template<typename TCoordinate, typename TEdge>
int SquareGrid4<TCoordinate, TEdge>::compare(const SquareGrid4 &other) const {
  auto first = ext::tie(this->m_obstacles, this->m_height, this->m_width);
  TCoordinate height = other.getHeight();
  TCoordinate width = other.getWidth();
  auto second = ext::tie(other.getObstacleList(), height, width);

  static ext::compare<decltype(first)> comp;

  return comp(first, second);
}

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

} // namespace grid

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

namespace core {

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
 *
 * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
 */

template<typename TCoordinate, typename TEdge>
struct normalize < grid::SquareGrid4 < TCoordinate, TEdge > > {
  static grid::SquareGrid4<> eval(grid::SquareGrid4<TCoordinate, TEdge> &&value) {
    grid::SquareGrid4<> grid(value.getHeight(), value.getWidth());

    // Copy obstacles
    for (auto &&i: ext::make_mover(std::move(value).getObstacleList())) {
      DefaultSquareGridNodeType
          obstacle = common::Normalize::normalizeObstacle(std::move(i));

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

    return grid;
  }
};

} // namespace grid

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

#endif //ALIB2_SQUAREGRID4_HPP
