/*
 * 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_REFERENCEPOINT_HH_
#define _OWLPS_POSITIONING_REFERENCEPOINT_HH_

class CapturePoint ;
class CalibrationRequest ;
class Request ;

#include "point3d.hh"
#include "measurement.hh"

#include <vector>
#include <ostream>
#include <unordered_map>

/// Represents a reference point in 3-D space
class ReferencePoint: public Point3D
{
protected:
  /// List of CalibrationRequest associated with the ReferencePoint
  /** Note that `requests` is a *pointer* list: only pointers
      are stored, not values. */
  std::vector<CalibrationRequest*> requests ;

  /** @name Read accessors */
  //@{
  std::unordered_map<std::string, Measurement>
  get_all_measurements(void) const ;
  /// Returns all the measurements sent by the given mobile
  std::unordered_map<std::string, Measurement>
  get_all_measurements(const std::string &mac_transmitter) const ;
  //@}

public:
  ReferencePoint(const float _x = 0,
                 const float _y = 0,
                 const float _z = 0): Point3D(_x, _y, _z) {}

  ReferencePoint(const Point3D &p): Point3D(p) {}

  ReferencePoint(const ReferencePoint &source):
    Point3D(source), requests(source.requests) {}

  ~ReferencePoint(void) ;

  /** @name Read accessors */
  //@{
  /// Returns all the calibration requests
  const std::vector<CalibrationRequest*>& get_requests(void) const ;
  /// Returns the calibration requests sent by the given mobile
  const std::vector<CalibrationRequest*> get_requests(
    const std::string &mac_transmitter) const ;
  /// Returns the average SS of all the packets sent by `mac_transmitter`
  double average_measurements(const std::string &mac_transmitter) const ;
  //@}

  /** @name Write accessors */
  //@{
  /// Adds a Request to the [requests' list](@ref #requests)
  void add_request(const CalibrationRequest *const r) ;
  /// Deletes a Request from the [requests' list](@ref #requests)
  void delete_request(const CalibrationRequest *const r) ;
  /// Deletes all the requests contained in #requests
  void delete_requests(void) ;
  /// Deletes the requests that are not sent by a CP
  bool delete_generated_requests(void) ;
  //@}

  /** @name Operations */
  //@{
  /// Computes the similarity of the ReferencePoint and a Request
  float similarity(const Request &source) const ;
  /// Computes the Friis index for the given CapturePoint
  float friis_index_for_cp(const std::string &cp_mac) const ;
  /// Computes the Friis indexes sum for the given CapturePoint
  float friis_indexes_for_cp(const CapturePoint &cp,
                             const double const_term,
                             int &nb_indexes) const ;
  /// Computes the Friis index for the given CapturePoint and packet ID
  float friis_index_for_cp(
    const std::string &cp_mac, const pkt_id_t pkt_id) const ;
  //@}

  /** @name Operators */
  //@{
  ReferencePoint& operator=(const ReferencePoint &source) ;
  bool operator==(const ReferencePoint &source) const ;
  bool operator!=(const ReferencePoint &source) const ;
  //@}

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



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


inline const
std::vector<CalibrationRequest*>& ReferencePoint::get_requests() const
{
  return requests ;
}



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


/**
 * @param r A pointer to the CalibrationRequest to add. If it is
 * null, nothing will be done.
 * The memory pointed by this pointer must not be deallocated before
 * the ReferencePoint destruction (do *not* pass a pointer to a local
 * variable!).
 */
inline void ReferencePoint::add_request(const CalibrationRequest *r)
{
  if (r)
    requests.push_back(const_cast<CalibrationRequest*>(r)) ;
}



/* *** Operators *** */


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



namespace std
{
template<> struct hash<ReferencePoint>
{
public:
  /**
   * This is a simple call to the hash function for Point3D, because we
   * do not want to take care of the CalibrationRequest list to hash the
   * ReferencePoint.
   */
  size_t operator()(const ReferencePoint &source) const
  {
    hash<Point3D> h ;
    return h(source) ;
  }
} ;

template<> struct equal_to<ReferencePoint>
{
public:
  /**
   * We want to take into account only the coordinates when comparing
   * two ReferencePoint in a container.
   */
  bool operator()(const ReferencePoint &source1,
                  const ReferencePoint &source2) const
  {
    return
      static_cast<Point3D>(source1) == static_cast<Point3D>(source2) ;
  }
} ;
}



#endif // _OWLPS_POSITIONING_REFERENCEPOINT_HH_
