/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 * In particular this file is part of the (WLAN) Location package
 *
 * 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_WLANLOCATOR_H
#define __IWLOCATOR_WLANLOCATOR_H

#include <iwlocator/locator.h>
#include "iwlocator/qinfo.h"
#include <iwear/utility.h>
#include <iwear/functor.h>

extern "C" {
#include <iwlocator/iwlib.h>
}
#include <list>

namespace iwear
{
    namespace sensor
    {
	namespace location
	{

typedef map<string ,Qinfo* > QInfoMap;

class wlan_locator : public Locator
{
friend void * start_wlan_locating( void * v );
public:
   /**
    * @param lm The LocationManager this Locator will be attached to
    * @param ifname The Device name, e.g. /dev/eth0 where this locator will
    * gather its information from.
    * @param db is a pointer to the Database. If this is NULL, then the locator
    * will never try to access any database but try to live only from the data
    * in the file.
    * @param threaded is true if the locator should run threaded. false should
    * only be set as a debugging value.
    * @param r The number of scans done for a single location value. 5 is a
    * reasonable default.
    * @param t Timeout in seconds after which an inactive AP will be removed
    * from the list. Should be nonzero obiously
    */
    wlan_locator( LocationManager* lm, const string& ifname, db::db* db, 
	    bool threaded = true, time_t t=5);

    typedef uid (*uid_generator)( const std::string& );

    wlan_locator( LocationManager* lm, const string& ifname, db::db* db, 
	    uid_generator ugen, bool threaded = true, time_t t=5);

    WLocation last_location;
   /**
    */
    virtual ~wlan_locator();

   /**
    * @name Generic Module interfafe functionality
    * @{
    */

   /**
    * @todo This is currentyl unimplemented, and throw an exception, but I'm
    * really sure that we should be able to set the wlan device into a power
    * saving mode.
    */
    virtual uint32_t activate_power_state( power_state );

   /**
    * @}
    */

   /**
    * @name Locator Interface functionality
    * @{
    */

    /**
     * Returns the most probable location
     */
    virtual const Location& get_actual_location( void ) const;

    /**
     * Get my type of locator
     */
    virtual locator_type get_locator_type( void ) const { return locator_wlan; }

   /**
    * @}
    */

   /**
    * @name WLAN specific functionality
    * @{
    */

    /**
     * Gets some alternative location, with the next lower probability
     */
    virtual const Location& get_alternative_location( void ) const;


    /**
     * Gets a map of all current known ap quality info mappings. This is quite
     * usefull for displaying all APs signal strength in the vicinity.
     */
    inline const QInfoMap & get_qualities(void) {return qmap;}

    /**
     * Create a new location. The UpFunc will be called for progress
     * information. If this Functor determines that there is no need for
     * creation any more (e.g. the user presses ESC) it may throw an
     * abort_error() exception so that this aborts its current operations.
     */
    int CreateNewLocation( WLocation& , int , Uint32Uint32Functor& UpFunc );

    /**
     * Gathers all the data that is needed to determine our actual location and
     * sets the location internally.
     * This is the "entry point" for location generation.
     */
    void gather_actual_location( void );

    /**
     * Returns our own bssid
     */
    const string& get_bssid( void ) const { return bssid; }

    /**
     * To Check if scanning is supported by this interface
     */
    bool is_scan_supported( void ) const { return scan_supported; }
   /**
    * @}
    */
    void pause_scan( void )   { auto_scan = false; }

    void unpause_scan( void ) { auto_scan = true;   }

    // The following functions are experimental for the new gui support
    // abstraction interface
    /**
     * The Functors registered with this will be called prior to deletion of an
     * Location thats not needed any more, and after creating a brand new
     * Location (Or Many of them).
     * The Parameters to the Functor are:
     * 1. The Database ID of the changed Item. If this is 0, it means that
     * multiple Items have been changed and the complete list should be
     * re-read.
     * 2. removal flag. If its true, the Item was removed, if its false, the
     * item was a new one.
     */
    void register_dbchange( DualFunctor<uint32_t,bool>& fun );
    void deregister_dbchange( DualFunctor<uint32_t,bool>& fun );

    /**
     * This function will attempt to do a very soft reset on the interface
     * device. Quite often the device gets stuck on scanning and doesnt seem to
     * come back again, especially on older devices. In this case we issue a
     * commit ioctl to the device, which should re-initialize a few internal
     * data structures. This seems to be the softest way to reset a device
     * without disturbing any connectivity.
     */
    void soft_reset( void );

    typedef map<uint32_t,WLocation*> IDtoLocMap;

    const map<uint32_t,WLocation*>& get_id_locmap( void ) const { return IDtoLoc; }
private:
    /**
     * Gathers all the data about strenghtes and checks internal structures
     */
    void gather_position_data( void );

    /**
     * Set internal operation mode (learnign/estimating)
     */
    void set_mode( qinfo_mode qm );

    /**
     * Alternative position with the next lower probability
     */
    WLocation alternative;

   /**
    * Socket that is beeing used to access the Wireless extensions ioctl()
    */
    int skfd;

   /**
    * AP deletion timeout in seconds
    */
    time_t timeout;

   /**
    * Operation mode of the Locator. If true, at startup a thread is spawned to
    * read the locations periodically.
    */
    bool thread_mode;

    /**
     * Flag if we support Scanning on desired iface.
     */
    bool scan_supported;

    /**
     * Save how many scan tokens have been processed to see if any was there
     */
    int scan_tokens;

    /**
     * A pointer to the database we should use
     */
    db::db* DBP;

    /**
     * The thread of the wlanlocator periodically gathering information bout
     * locations
     */
    pthread_t LocThread;

    /**
     * Our own bssid
     */
    string bssid;

    /**
     * Actual operation mode
     */
    qinfo_mode qmode;

    /**
     * Shall we do auto_scan ? 
     */ 
    bool auto_scan;
    
   /**
    * The qmap maps bssids of access points to their actual quality information
    * Although the Qinfo already contains the bssid string, we use a string
    * here, since through the GNU STL implementations reference counting
    * feature this will usually only occupy the space of an usual data-pointer
    */
    mutable QInfoMap qmap;

    /**
     * A list of accespoints we know
     */
    mutable set<APInfo*> aps;

    /**
     * Mapping the bssids to the range infos
     */
    mutable map<string,RangeInfo*> rmap;

   /**
    * Mapping of Location pointers to their db-id
    */
    mutable IDtoLocMap IDtoLoc;
   
   /**
    * The actual bssid we are gathering information for
    */ 
    mutable string actual;

    /**
     * Name of the interface we are scanning with
     */
    string if_name;

    /**
     * Init all we need and the thread
     */
    void Initialize( void );

    void interpret_scan_token(iw_event*, iw_range*);
    APInfo* load_new_ap_information( const string& essid, const string& bssid);

    void load_range_infos( const string& bssid );
    void load_range_infos( WLocation*, uint32_t );

    void release_ap_information( const string& essid);
    void gather_scan_information( );

    WLocation* get_or_load_location( uint32_t dbid );

    set<DualFunctor<uint32_t,bool>*> change_functors;
};

typedef wlan_locator WlanLocator;

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

