// WeightedSquareGrid4.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_WEIGHTEDSQUAREGRID4_HPP
#define ALIB2_WEIGHTEDSQUAREGRID4_HPP

#include <alib/tuple>
#include <edge/weighted/WeightedEdge.hpp>

#include "SquareGrid.hpp"

namespace grid {

template<typename TCoordinate, typename TEdge>
class WeightedSquareGrid4 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;

// ---------------------------------------------------------------------------------------------------------------------
 protected:
  typename edge_type::weight_type m_unit;

// =====================================================================================================================
// Constructor, Destructor, Operators
 public:
  explicit WeightedSquareGrid4(TCoordinate height, TCoordinate width, typename edge_type::weight_type unit = 1);
// ---------------------------------------------------------------------------------------------------------------------

  typename edge_type::weight_type getUnit() const;

  void setUnit(typename edge_type::weight_type unit);

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

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

  virtual int compare(const WeightedSquareGrid4 &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>
typename TEdge::weight_type WeightedSquareGrid4<TCoordinate, TEdge>::getUnit() const {
  return m_unit;
}

template<typename TCoordinate, typename TEdge>
void WeightedSquareGrid4<TCoordinate, TEdge>::setUnit(typename TEdge::weight_type unit) {
  if (unit > 0) m_unit = unit;
}

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

template<typename TCoordinate, typename TEdge>
WeightedSquareGrid4<TCoordinate, TEdge>::WeightedSquareGrid4(TCoordinate height,
                                                             TCoordinate width,
                                                             typename TEdge::weight_type unit)
    : SquareGrid<TCoordinate, TEdge>(height, width), m_unit(unit) {
}

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

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

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

template<typename TCoordinate, typename TEdge>
bool WeightedSquareGrid4<TCoordinate, TEdge>::isValidDirection(WeightedSquareGrid4::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>
std::string WeightedSquareGrid4<TCoordinate, TEdge>::name() const {
  return "SqaureGrid4Weighted";
}

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

template<typename TCoordinate, typename TEdge>
int WeightedSquareGrid4<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 WeightedSquareGrid4<TCoordinate, TEdge>::compare(const WeightedSquareGrid4 &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::WeightedSquareGrid4 < TCoordinate, TEdge > > {
  static grid::WeightedSquareGrid4<> eval(grid::WeightedSquareGrid4<TCoordinate, TEdge> &&value) {
    grid::WeightedSquareGrid4<> grid(value.getHeight(), value.getWidth(), value.getUnit());

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

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

    return grid;
  }
};

} // namespace grid

// =====================================================================================================================
#endif //ALIB2_WEIGHTEDSQUAREGRID4_HPP
