/**
 * @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_QINFO_H
#define __IWLOCATOR_QINFO_H

#ifndef __IWLOCATOR_APINFO_H
#include <iwlocator/apinfo.h>
#endif

#ifndef __IWEAR_TRIPLE_H
#include <iwear/triple.h>
#endif

extern "C" {
#include <time.h>
#include <stdint.h>
}
#define HIST_SIZE 256
namespace iwear
{
    namespace sensor
    {
	namespace location
	{
/**
 * The QInfo class can operate in two different modes.
 */
enum qinfo_mode
{
    estimating, ///< Only collect the data needed to estimate a position
    learning   ///< Additionally collect the data needed when learning a position
};
    
#define AVRGSTEPS 5

/**
 * This class models some quality information for a wireless access point. It
 * uses a multistep average for smoothing the curves. Additionally it now holds
 */
class Qinfo
{
private:
    /**
     * In learning mode we will here keep track of signal strength sample
     * category.
     */
    uint32_t* siglevs; 
    /**
     * This is the count of different signal strengths we have yet.
     */
    uint32_t siglevcnt;
    /**
     * This is the total number of signal samples we have collected so far in
     * the actual learning period.
     */
    uint32_t sigsamples;

    /**
     * This is a list of levels and noise of the last N samples collected so
     * far in estimation mode.
     */
    float level[AVRGSTEPS];
    float noise[AVRGSTEPS];
    /**
     * This is to keep track of the current position in the level and noise arrays.
     */
    uint32_t qpos;
    
    /**
     * This is the current levels in the N/2 of N best average mode.
     */
    float clevel;
    float cnoise;

    /**
     * Amount of samples collected so far in estimation mode (in contrast to
     * sigsamples above which is only for learning mode)
     */
    uint32_t amnt;

   /**
    * We dont need more resolution than seconds, this is for AP vanishing only.
    */
    time_t lastupdate;

    /**
     * A Pointer to the accespoint this qinfo belongs to. Just for convenienve.
     */
    APInfo* my_ap;

    Qinfo( );


#if 0
    /**
     * This stuff is for generating some predefined data to easily calculate
     * bezier splines.
     */
    // This is acutally not beeing used any more, but maybe someday in the
    // future we decide to use beziers again ( insteand of current cubics) so
    // we leave it here
    static double** bez_table;
    void gen_bezier_table( uint32_t, double );

#endif
    /**
     * Current operation mode.
     */
    qinfo_mode qmode;
public:

    struct short_info
    {
	float noise;
	float level;
	float raw_noise;
	float raw_level;

	APInfo apinfo;

	short_info( float n, float l, float rn, float rl, const APInfo* a )
	    : noise(n), level(l), raw_noise(rn), raw_level(rl), apinfo(*a)
	{
	}
    };

    short_info get_short_info( void ) { return short_info(get_noise(), get_level(), get_raw_noise(), get_raw_level(), get_apinfo() ); }

    Qinfo( APInfo* );

    ~Qinfo( );

    /**
     * Add a new set of quality data for this access point.
     * @param l is the level/strength of the signal
     * @param n is the noise strength received by the card
     * @param now is the time at which the signal was received (in seconds since epoch)
     */
    void add_levels( float l, float n, double now );

    /**
     * Internal function to record the levels for the position learning mode.
     */
    void add_poslevels( float l, float n, double now );

    inline const APInfo * get_apinfo( void ) const { return my_ap; }
    inline APInfo * get_apinfo( void ) { return my_ap; }

    /**
     * @returns true if we have valid data to estimate a position. This means
     * that we need at least that many samples we average over.
     */
    inline bool has_valid( void ) const { return ( amnt >= AVRGSTEPS); }

    /**
     * Check against a timeout in seconds if this information has timed out.
     * That means that the last update of this information was more than
     * timeout seconds ago.
     */
    inline bool still_valid( time_t timeout ) const { return ((time(NULL) - lastupdate) <= timeout); }

    /**
     * Get a simple N-step averaged noise value
     */
    inline double get_noise( void ) const { if ( amnt < AVRGSTEPS ) return 0.0; return (cnoise / AVRGSTEPS); }

    /**
     * Get a simple N-step averaged level/strength value
     */
    inline double get_level( void ) const { if ( amnt < AVRGSTEPS ) return 0.0; return (clevel / AVRGSTEPS); }

    /**
     * Get the last added raw noise value
     */
    inline double get_raw_noise( void ) const { return noise[qpos]; }

    /**
     * Get the last added raw level/strength value
     */
    inline double get_raw_level( void ) const { return level[qpos]; }

    triple<float,float,float> get_best( void ) const;

    /**
     * Calculate a rangeinfo structure after the learning process.
     * @note This does not reset the internal gathering structures anymore,
     * which means that in theory you should be able to call this during a
     * learning process.
     */
    RangeInfo* calc_ri( uint32_t );

    void set_mode( qinfo_mode qm ) 
    { 
//	if( qmode == qm ) return; // dont do anything when set to the same again.
	// Initialize learning structures
	if( qm == learning )
	{
	    // In case someone calls two times in a row with learning
	    if( ! siglevs )
	    {
		siglevs = new uint32_t[HIST_SIZE];
	    }
	    d_dbg << "Setting mode and resetting stuff" << endl;
	    memset( siglevs, 0, HIST_SIZE );
	    siglevcnt = 0;
	    sigsamples = 0;
	}
	else
	{
	    delete[] siglevs;
	    siglevs = 0;
	}
	qmode = qm;
    }
	
};

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