/*
 * 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.
 */


#include "autocalibration.hh"

#include "stock.hh"
#include "configuration.hh"
#include "posexcept.hh"

#include <string>
#include <iostream>

using namespace std ;



/* *** Static attribute definitions *** */

uint32_t Autocalibration::nb_virtual_mobiles = 0 ;



/* *** Operations *** */


/**
 * The reference point P we want to generate will end up containing
 * as many calibration requests as the total number of CPs.
 * For each of these requests, we consider a transmitter TRX (which is
 * a virtual mobile located in P) and a receiver RX (the current CP).
 * To generate the signal strength (SS) received by RX from TRX,
 * we first select two reference CPs REF1 and REF2. Then, we average
 * the real measurements REF1->RX and REF2->RX, weighting in function
 * of the angles REF-RX-TRX and the distance from RX to TRX to compute
 * the SS.
 * Hope this is clear enough…
 */
void Autocalibration::generate_reference_point()
{
  // If the point is the coordinates of an existing CP, we don't
  // need to generate it
  if (Stock::is_cp_coordinate(point))
    return ;

  /* Get/create the reference point */
  const ReferencePoint
  &current_rp = Stock::find_create_reference_point(point) ;

  /* Prepare the virtual mobile */
  string vmob_mac(PosUtil::int_to_mac(nb_virtual_mobiles++)) ;
  // The gain and trx power of the mobile will be the average of
  // those of all the known CPs (could be better, probably)
  vmob_gain = 0 ;
  vmob_pow = 0 ;

  /* Generate the SS(s) for each CP */
  for (rx = Stock::cps.begin() ; rx != Stock::cps.end() ; ++rx)
    {
      if (init_ss_generation())
        generate_ss() ;
    }
  assert(! measurements.empty()) ;

  /* Create the virtual mobile */
  Mobile vmob("", vmob_mac, vmob_gain, vmob_pow) ;
  const Mobile &mobile = Stock::find_create_mobile(vmob) ;

  /* Create the calibration request */
  CalibrationRequest cr(OWL_REQUEST_GENERATED) ;
  cr.set_time_sent(cr.get_time_received()) ;
  cr.set_mobile(&mobile) ;
  cr.set_measurements(measurements) ;
  cr.set_reference_point(const_cast<ReferencePoint*>(&current_rp)) ;

  Stock::store_calibration_request(cr) ;
}


/**
 * @returns `true` if the SS can be generated.
 * @returns `false` in case of error.
 */
bool Autocalibration::init_ss_generation()
{
  /* Update the mobile's attributes */
  vmob_gain += rx->second.get_antenna_gain() / Stock::cps.size() ;
  vmob_pow += rx->second.get_trx_power() / Stock::cps.size() ;

  /* Skip the RX CP if it is not at the same floor as the point
   * to generate */
  const Point3D &rx_coord = rx->second.get_coordinates() ;
  if (rx_coord.get_z() != point.get_z())
    return false ;

  return true ;
}


/**
 * This function is a simple wrapper around compute_single_packet_ss()
 * and compute_multi_packet_ss(), depending on the configuration.
 */
void Autocalibration::compute_ss()
{
  if (Configuration::
      bool_value("positioning.generate-multi-packet-reference-points"))
    compute_multi_packet_ss() ;
  else
    compute_single_packet_ss(0) ;
}
