/*
 * 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_STOCK_HH_
#define _OWLPS_POSITIONING_STOCK_HH_

class Autocalibration ;

#include "building.hh"
#include "waypoint.hh"
#include "mobile.hh"
#include "capturepoint.hh"
#include "referencepoint.hh"
#include "calibrationrequest.hh"

#include <unordered_map>
#include <unordered_set>

/// Storage class
class Stock
{
  friend class Autocalibration ;
  friend class AutocalibrationMesh ;
  friend class AutocalibrationLine ;


private:
  /// List of known Building
  /** The string key of the map is the Building name. */
  static std::unordered_map<std::string, Building> buildings ;

  /// List of known Waypoint
  static std::unordered_map<Point3D, Waypoint> waypoints ;

  /// List of known Mobile
  /** The string key of the map is the Mobile MAC address. */
  static std::unordered_map<std::string, Mobile> mobiles ;

  /// List of known CapturePoint
  /** The string key of the map is the CapturePoint MAC address. */
  static std::unordered_map<std::string, CapturePoint> cps ;

  /// List of known ReferencePoint
  static std::unordered_set<ReferencePoint> reference_points ;

  /// List of known CalibrationRequest
  static std::unordered_set<CalibrationRequest> calibration_requests ;

  /** @name CalibrationRequest operations */
  //@{
  /// Delete calibration requests that do not come from the CPs
  static void delete_non_cp_calibration_requests(void) ;
  //@}

  /** @name ReferencePoint operations */
  //@{
  /// Generates reference points according to a meshing
  static void generate_reference_points_mesh(void) ;
  /// Generates reference points according to a path
  static void generate_reference_points_line(void) ;
  /// Generates reference points between `start` and `end`
  static void generate_interpolated_reference_points(
    const Point3D &start, const Point3D &end,
    const CapturePoint *const cp1,
    const CapturePoint *const cp2 = nullptr) ;
  /// Generates reference points according to a list
  static void generate_reference_points_list(void) ;
  /// Generates a single reference point
  static void generate_reference_point(Autocalibration &ac) ;
  //@}


public:
  /** @name Building operations */
  //@{
  /// Returns the number of buildings
  static unsigned int nb_buildings(void) ;
  /// Returns the Building corresponding to a given name
  static const Building& get_building(const std::string &name) ;
  /// Searches for a Building and creates it if it does not exist
  static const Building& find_create_building(const std::string &name) ;
  /// Searches the Area in which `point` is
  static const Area* in_which_area_is(const Point3D &point) ;
  //@}

  /** @name Waypoint operations */
  //@{
  /// Returns the number of waypoints
  static unsigned int nb_waypoints(void) ;
  /// Searches for a Waypoint and adds it if it does not exist
  static const Waypoint&
  find_create_waypoint(const Waypoint &point) ;
  /// Searches for a Waypoint from its coordinates and adds it if it
  /// does not exist
  static const Waypoint&
  find_create_waypoint(const Point3D &point) ;
  /// Deletes a Building from the building list of a Waypoint; deletes
  /// the Waypoint if it is not linked to any Building any more
  static void waypoint_remove_building(const Waypoint &point,
                                       Building *const building) ;
  //@}

  /** @name Mobile operations */
  //@{
  /// Returns the number of mobiles
  static unsigned int nb_mobiles(void) ;
  /// Verify the existence of a mobile
  static bool mobile_exists(const std::string &mac) ;
  /// Returns the Mobile corresponding to a given MAC address
  static const Mobile& get_mobile(const std::string &mac) ;
  /// Searches for a Mobile given its MAC address and creates it if it
  /// does not exist
  static const Mobile& find_create_mobile(const std::string &mac) ;
  /// Searches for a Mobile and create it if it does not exist
  static const Mobile& find_create_mobile(const Mobile &source) ;
  /// Returns a reference to the Mobile corresponding to a given MAC
  /// address
  static Mobile& getw_mobile(const std::string &mac) ;
  //@}

  /** @name CapturePoint operations */
  //@{
  /// Returns the number of capture points
  static unsigned int nb_cps(void) ;
  /// Returns a reference to the CP list
  static
  std::unordered_map<std::string, CapturePoint>& get_cps(void) ;
  /// Verify the existence of a CP
  static bool cp_exists(const std::string &mac) ;
  /// Returns the CapturePoint corresponding to a given MAC address
  static const CapturePoint& get_cp(const std::string &mac) ;
  /// Searches for a CapturePoint given its MAC address and creates it
  /// if it does not exist
  static const CapturePoint& find_create_cp(const std::string &mac) ;
  /// Searches for a CapturePoint and create it if it does not exist
  static const CapturePoint& find_create_cp(const CapturePoint &source) ;
  /// Returns a reference to the CapturePoint corresponding to a given
  /// MAC address
  static CapturePoint& getw_cp(const std::string &mac) ;
  /// Updates the friis indexes of all the CPs
  static void update_all_friis_indexes(void) ;
  /// Returns the signal strenth received by a CP `mac_receiver` from
  /// a CP `mac_transmitter`
  static double cp_matrix_get_ss(const std::string &mac_transmitter,
                                 const std::string &mac_receiver) ;
  /// Checks if a Point3D is the coordinate of an existing CP
  static const CapturePoint* is_cp_coordinate(const Point3D &coord) ;
  /// Returns the distance from `location` to the closest CP
  static float distance_from_closest_cp(const Point3D &location) ;
  //@}

  /** @name ReferencePoint operations */
  //@{
  /// Returns the number of reference points
  static unsigned int nb_reference_points(void) ;
  /// Verify the existence of a reference point at the given coordinates
  static bool reference_point_exists(const ReferencePoint &point) ;
  /// Returns the ReferencePoint at the given coordinates
  static const ReferencePoint&
  get_reference_point(const Point3D &point) ;
  /// Searches for a ReferencePoint and adds it if it does not exist
  static const ReferencePoint&
  find_create_reference_point(const ReferencePoint &point) ;
  /// Searches for a ReferencePoint at the given coordinates and adds it
  /// if it doesn't exist
  static const ReferencePoint&
  find_create_reference_point(const Point3D &point);
  /// Searches for the closest ReferencePoint (in the signal strength
  /// space) to a given Request
  static const ReferencePoint&
  closest_reference_point(const Request &request) ;
  /// Generates reference points from the reference points corresponding
  /// to CPs
  static void regenerate_reference_points(void) ;
  //@}

  /** @name CalibrationRequest operations */
  //@{
  /// Returns the number of calibration requests
  static unsigned int nb_calibration_requests(void) ;
  /// Searches for a CalibrationRequest, adds it if it does not exist,
  /// and links it to its reference point
  static const CalibrationRequest& store_calibration_request(
    const CalibrationRequest &request) ;
  /// Deletes a CalibrationRequest
  static void delete_calibration_request(
    const CalibrationRequest &request) ;
  /// Deletes the calibration requests that are older than `timeout`
  /// seconds
  static void delete_calibration_requests_older_than(const int timeout) ;
  /// Searches for a CalibrationRequest and adds it if it does not exist
  static const CalibrationRequest&
  find_create_calibration_request(const CalibrationRequest &request) ;
  /// Searches for the closest CalibrationRequest (in the signal
  /// strength space) to a given Request
  static const CalibrationRequest&
  closest_calibration_request(const Request &request) ;
  //@}

  /// Deletes all elements in all attributes
  static void clear(void) ;
} ;



/* *** Building operations *** */


inline unsigned int Stock::nb_buildings()
{
  return buildings.size() ;
}



/* *** Waypoint operations *** */


inline unsigned int Stock::nb_waypoints()
{
  return waypoints.size() ;
}


inline const Waypoint& Stock::
find_create_waypoint(const Waypoint &point)
{
  Waypoint& found =
    const_cast<Waypoint&>(
      find_create_waypoint(static_cast<Point3D>(point))) ;
  found.add_buildings(point) ;
  return found ;
}



/* *** Mobile operations *** */


inline unsigned int Stock::nb_mobiles()
{
  return mobiles.size() ;
}


/**
 * If the Mobile corresponding to `mac` does not exist, it is created.
 * @param mac The MAC address of the Mobile to search for. It must be a
 * valid MAC address, as no check is performed.
 * @returns A modifiable reference to the Mobile.
 */
inline Mobile& Stock::getw_mobile(const std::string &mac)
{
  return mobiles[mac] ;
}



/* *** CapturePoint operations *** */


inline unsigned int Stock::nb_cps()
{
  return cps.size() ;
}


inline
std::unordered_map<std::string, CapturePoint>& Stock::get_cps()
{
  return cps ;
}


/**
 * If the CapturePoint corresponding to `mac` does not exist, it is
 * created.
 * @param mac The MAC address of the CapturePoint to search for.
 * It must be a valid MAC address, as no check is performed.
 * @returns A modifiable reference to the CapturePoint.
 */
inline CapturePoint& Stock::getw_cp(const std::string &mac)
{
  return cps[mac] ;
}



/* *** ReferencePoint operations *** */


inline unsigned int Stock::nb_reference_points()
{
  return reference_points.size() ;
}


inline const ReferencePoint&
Stock::find_create_reference_point(const Point3D &point)
{
  return find_create_reference_point(ReferencePoint(point));
}




/* *** CalibrationRequest operations *** */


inline unsigned int Stock::nb_calibration_requests()
{
  return calibration_requests.size() ;
}



#endif // _OWLPS_POSITIONING_STOCK_HH_
