/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation as in version 2 of the License.

 * 
 * The iWear Framework is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWLOCATOR_LOCATIONMANAGER_H
#define __IWLOCATOR_LOCATIONMANAGER_H



#include "iwear/eventdispatcher.h"
#include "iwear/modulemanager.h"
#include "iwear/point.h"
#include "iwlocator/locator.h"
#include "iwlocator/locatorevent.h"
#include "iwsens/sensorchecking.h"

#include <list>

using namespace std;

namespace iwear
{
    namespace sensor
    {
	namespace location
	{

class LocationManager : public ModuleManager<locator_type,Locator,SensorManager>, public SensorChecking
{
public: 
   /**
    * We need the toplevel sensormanager for dynamic added modules.
    */
    LocationManager( SensorManager* mm, db::db* db, const string& tabname, Configuration& c );

    
    

   /**
    * Destroys us and the internal events saved, but not the single modules.
    */
    virtual ~LocationManager();

   /**
    * @todo what parameters do we need for this functionality ? It should be a
    * generic function for different locator types, this is a todo for the far
    * future.
    */
    void learn_location( );

   /**
    * Return a copy of the actual location, gathered and mixed by all known
    * locator types.
    */
    Location get_actual_location( locator_type = num_locator_type );

    virtual void register_external_module( Module* );
    virtual void register_internal_module( Module* );
    virtual void deregister_module( Module* );

    void check_locators( EventDispatcher& );
    virtual void check_sensors( EventDispatcher& evdis) { check_locators(evdis); }

    inline virtual locator_type get_type( const Locator& loc ) const
    {
	return loc.get_locator_type();
    }

    Location combine_locations( const vector<Location>& );

    Location fixup_location( const Location& );

    void check_evmap( float val, float&, multimap<float,SpeedLocatorEvent*>& , EventDispatcher& );

    const map<uint32_t,Location*> get_id_locmap( void ) const { return id_locmap; }
protected:

    void fixup_room( Location& );
    void check_loaded_locations( double x, double y );
    void load_box( const math::Point2D<double>& ll, const math::Point2D<double>& ur );

    /**
     * Sort this known locations by the lower left corner of the latitude
     * coordinates
     */
    map<double,Location*> coord_locmap;

    map<uint32_t,Location*> id_locmap;

    /**
     * These are the points of the currently loaded locations bounding boxes.
     */
    math::Point2D<double> loaded_ll;

    math::Point2D<double> loaded_ur;
    

    db::db* DB;
    string tablename;

    void check_locator_type( EventDispatcher& evdis );
    void check_locator( EventDispatcher& evdis );
    void check_locator_type_sh( EventDispatcher& evdis );
    void check_locator_sh( EventDispatcher& evdis );

    void check_locators_symbolic( EventDispatcher& evdis );
    void check_locators_distance( EventDispatcher& evdis );
    void check_locators_heading( EventDispatcher& evdis );
    void check_locators_speed( EventDispatcher& evdis );

    /**
     * @{
     * @name Event registration container {{{
     */
    
    /**
     * For speed events on specific locator types.
     *
     * map first by locator_type and then have a pair of a float, holding the
     * last value of the sensor, at the last check. 
     * the multimap within that is sorted by the upper bound, similarily to
     * whats done in the sensorhandler.
     */
    map<locator_type,pair<float, multimap<float,SpeedLocatorEvent*> > > lt_speed_evmap;

    /**
     * this is the same for the heading.
     */
    map<locator_type,pair<float, multimap<float,SpeedLocatorEvent*> > > lt_head_evmap;

    /**
     * here we have the same as above but first sorted by an actual locator, not just the type
     */
    map<Locator* , pair<float, multimap<float,SpeedLocatorEvent*> > > loc_speed_evmap;
    
    /**
     * the same for heading
     */
    map<Locator* , pair<float, multimap<float,SpeedLocatorEvent*> > > loc_head_evmap;

    /**
     * Here we just go for some Locationevent...
     */
    multimap<locator_type,LocLocatorEvent* > lt_loc_map;

    /**
     * and here for the locator and not only type...
     */
    multimap<Locator*  ,LocLocatorEvent* > loc_loc_map;

    /**
     * save checks for similar locations of the symbolic information(s)
     * Here we can use the operator< of a pair to also sort after the second
     * locator pointer in the pair. So if it happens that multiple events are
     * registered for the same pair of locators, we do not need to get the
     * location from the locator again for the second time we check this pair.
     * @note You should always re-order the locators before inserting into this
     * map, since the user might have changed their order, but meaning the same
     * pair.
     */
    multimap<pair<Locator*, Locator*>, LocLocatorEvent* > lolo_map;

//    multimap<pair<Locator*, Locator*>, SpeedLocatorEvent* > losp_map;
//    multimap<pair<Locator*, Locator*>, HeadLocatorEvent*  > lohd_map;
    map<pair<Locator*, Locator*>, multimap<float, BufferLocatorEvent* > > lods_map;

    /**
     * Save to check if two locators have the same (bfzone) speed
     * In the Multimap we sort in the first field for dist-bfzone, the smalles
     * possible value at which this event will trigger.
     */
    map<pair<Locator*, Locator*>, multimap<float, BufferLocatorEvent* > > losp_map;

    /**
     * to check if they have the same heading
     */
    map<pair<Locator*, Locator*>, multimap<float, BufferLocatorEvent* > > losh_map;

    /**
     * @}
     * }}}
     */

public:
    /**
     * @{
     * @name Event registration functions for all events regarding speed {{{
     */

    /**
     * An event registered with this function will be triggered if the speed of
     * the desired locator_type is within the ranges further specified. If the
     * upper range isnt specified, a default of the highest float is choosen
     * (can be seen as open interval until infinity)
     * @param lt desired type of locator (will choose its combined location info)
     * @param lf the functor to be called if this event is triggered
     * @param low the lower boundary of the interval for which this event will be triggered
     * @param up the higher boundery, below this will be triggered
     */
    SpeedLocatorEvent& register_location_event_speed( locator_type lt, LEFunctor& lf,
	    float low, float up = numeric_limits<float>::max());

    /**
     * This is nearly the same, it just registers for a specific locator.
     */
    SpeedLocatorEvent& register_location_event_speed( Locator&, LEFunctor&,
	    float low, float up = numeric_limits<float>::max());

   /**
    * Register for a check if two locators have the same (similar) speed.
    * @param loc1 is the first locator
    * @param loc2 is the second locator
    * @param delta is the difference between the two locators speed in m/s
    * @param bfzone is a speed buffer zone to decrease jumpiness
    */
    BufferLocatorEvent& register_location_event_speed( Locator& loc1, LocFunctor&, 
	    Locator& loc2, float delta, float bfzone = 0.0 );
    /**
     * @}
     * }}}
     */

    /**
     * @{
     * @name Event registration functions for all events regarding users heading {{{
     */


    /**
     * Will be triggered if the heading is within the specified range. For
     * parameters @see register_location_event_speed
     */
    SpeedLocatorEvent& register_location_event_heading( locator_type, LEFunctor&,
	    float low, float up = numeric_limits<float>::max());
    /**
     * The same as the other one, except that we here explicitly pass the
     * locator where the information should be matched.
     */
    SpeedLocatorEvent& register_location_event_heading( Locator&, LEFunctor&,
	    float low, float up = numeric_limits<float>::max());

    /**
     * Check if two locators have the same heading. 
     * @param delta is to specify at which distance of their speed it will be
     * considered as equal.
     * @param bfzone is an additinonal buffer distanc around the deltato reduce
     * jumpiness if the values are near the delta border.
     */
    BufferLocatorEvent& register_location_event_heading( Locator& loc1, LocFunctor&, 
	    Locator& loc2, float delta, float bfzone = 0.0 );
    /**
     * @}
     * }}}
     */

    /**
     * @{
     * @name Event registration functions for all events that match symbolic location information {{{
     */

    /**
     * Registers a locator_type for a specific symbolic location. The event
     * will be triggered if all non-empty information in the LocationSymbol are
     * matched against the actual location. If the lazy option is passed, then
     * also non-empty in the given location information are matched against the
     * desired location. 
     * @warning Be aware that this can lead to unexpected results. As an
     * example, if you try to listen for several symbolic locations which are
     * all located in bremen, but a locator_type can also offer the location
     * information about bremen, all your symbolic locations will match against
     * it ! With "no-lazy" it will only match if all non-null strings will
     * match the given location.
     * @note A Later extension might be that symbolic informations contain area
     * mappings in form of polygons. In this case a coordinate match on this
     * area will be preferred, but this will probably extend the API at a later
     * point.
     * @param lt the desired type of the locator to match for
     * @param lf the functor to be called if the location matches
     * @param ls the symbolic location information to be matched against
     * @param the lazy evaluation flag (read the warning!)
     */
    LocLocatorEvent& register_location_event( locator_type lt, LocFunctor& lf,
	    const LocationSymbol& ls, bool lazy = false );

    LocLocatorEvent& register_location_event( Locator&, LocFunctor&,
	    const LocationSymbol&, bool lazy = false );

    /**
     * @}
     * }}}
     */

    /**
     * Register for some location, based on the coordinate information. Since
     * we will mostly never be able to directly match some coordinates, we will
     * pass a distance parameter. You can also specify a buffer zone (which
     * will default to 5% if passed as 0.0) which works as follows: If you are
     * come near to the target, the trigger will go off when u have passed the
     * buffer zone to the inner boundary. If you now move away, the trigger
     * will only go off if you pass the other side of the boundary zone (outer
     * boundary). This will prevent noisy event triggering in case the position
     * is "jumping" near your position. 
     */
    LocLocatorEvent& register_location_event( locator_type, LocFunctor&,
	    const LocationCoordinate&, float distance, float bfzone = 0.0 );

    LocLocatorEvent& register_location_event( Locator&, LocFunctor&,
	    const LocationCoordinate&, float distance, float bfzone = 0.0 );

    /**
     * The LocationArea is in fact a Polygon, so this will trigger if the
     * lat/lon position is within the polygon.
     * We have nothing like a bufferzone or delta value here, this would
     * unnecessarily complicate things, although maybe sometimes reporting will
     * be a little bit noisier.
     */
    LocLocatorEvent& register_location_event( locator_type, LocFunctor&,
	    const LocationArea& );

    LocLocatorEvent& register_location_event( Locator&, LocFunctor&,
	    const LocationArea& );

    /**
     * Register an event on two locaters, if theyre below a certain distance.
     * Its with a bufferzone too.
     * This is a function you will need to deregister with the LocLocatorEvent,
     * otherwise removing all the instances of maybe other events with the same
     * locator combination....
     * @note This cannot be for locator_types currently
     * @todo TODO Implement adress reordering on multiple locator inserts...
     * Should speedup things, as well as prevent ordering mismatches by the
     * user.
     */
    BufferLocatorEvent& register_location_event( Locator&, LocFunctor&,
	    Locator&, float distance, float bfzone = 0.0 );

    /**
     * This is the same, but matches for the symbolic information, not the
     * coordinate ones.
     */
    LocLocatorEvent& register_location_event( Locator&, LocFunctor&,
	    Locator&, bool lazy = false );

    /**
     * Registers an event for location change. Whenever the system has detected
     * a change in the <b>symbolic</b> location, it will call the functor. Be aware
     * that this can be potentially really noisy.
     */
    LocLocatorEvent& register_location_event( Locator&, LocFunctor& );

    LocLocatorEvent& register_location_event( locator_type lt, LocFunctor& );

    // Every deregister function is the same as the register function,
    // including default parameters

    void deregister_location_event_speed( locator_type lt, LEFunctor& lf,
	    float low, float up = numeric_limits<float>::max());

    void deregister_location_event_speed( Locator&, LEFunctor&,
	    float low, float up = numeric_limits<float>::max());

    void deregister_location_event_speed( Locator& loc1, LocFunctor&, 
	    Locator& loc2, float delta, float bfzone = 0.0 );

    void deregister_location_event_heading( locator_type, LEFunctor&,
	    float low, float up = numeric_limits<float>::max());

    void deregister_location_event_heading( Locator&, LEFunctor&,
	    float low, float up = numeric_limits<float>::max());

    void deregister_location_event_heading( Locator& loc1, LocFunctor&, 
	    Locator& loc2, float delta, float bfzone = 0.0 );

    void deregister_location_event( locator_type lt, LocFunctor& lf,
	    const LocationSymbol& ls, bool lazy = false );

    void deregister_location_event( Locator&, LocFunctor&,
	    const LocationSymbol&, bool lazy = false );

    void deregister_location_event( locator_type, LocFunctor&,
	    const LocationCoordinate&, float distance, float bfzone = 0.0 );

    void deregister_location_event( Locator&, LocFunctor&,
	    const LocationCoordinate&, float distance, float bfzone = 0.0 );

    void deregister_location_event( locator_type, LocFunctor&,
	    const LocationArea& );

    void deregister_location_event( Locator&, LocFunctor&,
	    const LocationArea& );

    void deregister_location_event( Locator&, LocFunctor&,
	    Locator&, float distance, float bfzone = 0.0 );

    void deregister_location_event( Locator&, LocFunctor&,
	    Locator&, bool lazy = false );

    void deregister_location_event( Locator&, LocFunctor& );

    void deregister_location_event( locator_type lt, LocFunctor& );


    void deregister_location_event( LocLocatorEvent& );
    void deregister_location_event( SpeedLocatorEvent& );
    void deregister_location_event( BufferLocatorEvent& );
//    void deregister_location_event( HeadLocatorEvent& );

    /**
     * We will call all of them whenever we have changed some location
     * information. 
     */
    void register_dbchange( DualFunctor<uint32_t,bool>& fun );
    void deregister_dbchange( DualFunctor<uint32_t,bool>& fun );
protected:
    set<DualFunctor<uint32_t,bool>*> change_functors;

   /**
    * This is the most probable location. It will be updated on demand, that
    * means when the user requests a location, it asks the underlying modules
    * if they have any newer information than the last. If they have, the
    * locator will generate a new location, if not it will provide the last
    * generated and saved location.
    */
    Location mpl;
private:

   /**
    * We can never never permit a LocationManager without a master manager
    * which can handle generic modules.
    */
//    LocationManager();

};

} // namespace location
} // namespace  sensor
} // namespace iwear
#endif
