/*
 * This file is part of the Owl Positioning System (OwlPS) project.
 * It is subject to the copyright notice and license terms in the
 * COPYRIGHT.t2t file found in the top-level directory of this
 * distribution and at
 *   https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t
 * No part of the OwlPS Project, including this file, may be copied,
 * modified, propagated, or distributed except according to the terms
 * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be
 * distributed along with this file, either separately or by replacing
 * this notice by the COPYRIGHT.t2t file's contents.
 */


#ifndef _OWLPS_POSITIONING_WAYPOINT_HH_
#define _OWLPS_POSITIONING_WAYPOINT_HH_

class Building ;

#include "point3d.hh"

#include <unordered_set>
#include <ostream>
#include <stdexcept>

/// Represents a junction point between several rooms (i.e. Area)
/**
 * A Waypoint is defined by its coordinates and linked to the buildings
 * it belongs to. You *should* always construct a Waypoint with at least
 * one associated Building, but you *can* do otherwise if needed, for
 * instance in an automatic construction (container…).
 */
class Waypoint: public Point3D
{
protected:
  /// List of Building associated with the Waypoint
  std::unordered_set<Building*> buildings ;

public:
  Waypoint(Building *const _b = nullptr, const float _x = 0,
           const float _y = 0, const float _z = 0) ;

  Waypoint(Building *const _b, const Point3D &p) ;

  explicit Waypoint(const Point3D &p): Point3D(p) {}

  Waypoint(const Waypoint &wp): Point3D(wp), buildings(wp.buildings) {}

  ~Waypoint(void) ;

  /** @name Read accessors */
  //@{
  /// #buildings's first element read accessor
  Building* get_1st_building(void) const ;
  const std::unordered_set<Building*>& get_buildings(void) const ;
  //@}

  /** @name Write accessors */
  //@{
  /// Adds a Building to the [buildings' list](@ref #buildings)
  void add_building(Building *const _b) ;
  /// Adds the Building list of `source` to #buildings
  void add_buildings(const Waypoint &source) ;
  /// Removes a Building from #buildings
  void remove_building(Building *const _b) ;
  //@}

  /** @name Operators */
  //@{
  Waypoint& operator=(const Waypoint &wp) ;
  bool operator==(const Waypoint &wp) const ;
  bool operator!=(const Waypoint &wp) const ;
  operator std::string(void) const ;
  //@}

  /// Displays a Waypoint
  friend std::ostream& operator<<(std::ostream &os, const Waypoint &wp) ;
} ;



/* *** Read accessors *** */


/**
 * @returns A pointer to the first Building associated with the Waypoint
 * (i.e. the first element of #buildings). If #buildings is empty,
 * nullptr is returned.
 */
inline Building* Waypoint::get_1st_building() const
{
  if (buildings.empty())
    return nullptr ;
  return *buildings.begin() ;
}


inline const std::unordered_set<Building*>&
Waypoint::get_buildings() const
{
  return buildings ;
}



/* *** Write accessors *** */


/**
 * @param _b A pointer to the Building to add. If it is null, nothing
 * will be done.
 * The memory pointed by this pointer must not be deallocated before
 * the Waypoint destruction (do *not* pass a pointer to a local
 * variable!).
 */
inline void Waypoint::add_building(Building *const _b)
{
  if (_b)
    buildings.insert(_b) ;
}


inline void Waypoint::add_buildings(const Waypoint &source)
{
  buildings.insert(source.buildings.begin(), source.buildings.end()) ;
}


inline void Waypoint::remove_building(Building *const _b)
{
  if (_b)
    buildings.erase(_b) ;
}



/* *** Operators *** */


inline bool Waypoint::operator!=(const Waypoint &wp) const
{
  return !(*this == wp) ;
}



namespace std
{
template<> struct hash<Waypoint>
{
public:
  /**
   * This is a simple call to the hash function for Point3D, because a
   * waypoints should have unique coordinates independently from the
   * building they belong to (the coordinate system is supposed to be
   * common to all the buildings).
   */
  size_t operator()(const Waypoint &source) const
  {
    hash<Point3D> h ;
    return h(source) ;
  }
} ;
}



#endif // _OWLPS_POSITIONING_WAYPOINT_HH_
