/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 * In particular this file is part of the Framework Context Library
 *
 * 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 __CONTEXTMANAGER_H
#define __CONTEXTMANAGER_H

#ifndef __SENSORCONTEXT_H
#include <iwear-context/sensorcontext.h>
#endif

#ifndef __CONTEXTLISTENER_H
#include <iwear-context/contextlistener.h>
#endif

#ifndef __CONTEXTCHANGEDTHREAD_H
#include <iwear-context/contextchangedthread.h>
#endif

#ifndef __SENSORMANAGER_H
#include <iwsens/sensormanager.h>
#endif

#ifndef __CONTEXTENUMS_H
#include <iwear-context/contextenums.h>
#endif

#ifndef __DEBUGSTREAM_H
#include <iwear/debugstream.h>
#endif

#ifndef __UTILITY_H
#include <iwear/utility.h>
#endif

#ifndef __THREADLOCKER_H
#include <iwear/threadlocker.h>
#endif

#ifndef __THREADLOCKED_H
#include <iwear/threadlocked.h>
#endif

extern "C"{
#include <stdint.h>
}
#include <cstddef>
#include <deque>
#include <map>
#include <set>
#include <utility> // for pair used in maps
#include <string>
#include <algorithm> // stl-algos (sorting etc)

using namespace std;

using namespace iwear::sensor;

namespace iwear{
namespace context{
    class ContextListener;

    class SensorEventFunctor;

    class CompositeContext;
    /** 
     * This class provides functions to monitor different contexts that 
     * are directly or indirectly affected by input from the SensorManager. 
     * Therefore it implements an eventhandler where other objects may 
     * register and then be informed about specified contexts and their 
     * changes.
     */
    class ContextManager : public virtual ThreadLocked{
	/**
	 * The ContextChangedThread works on the deque changed_atomics which
	 * is filled by functors that are called when events occur e.g. at the
	 * sensormanager.
	 */
	friend class ContextChangedThread;

    private:

	static uint32_t cm_num;

	/** 
	 * There should be only one instance of ContextManager - so
	 * get_instance() has to be used to get it. Therefore the
	 * standard constructor is private to prevent accidental 
	 * instanciation.
	 */
 	ContextManager( EventDispatcher& ed )
	    : ccthread(this, ed)
	{
	}


	/** 
	 * Check if a pair already exists in the multimap.
	 * @param listener the ContextListener - the value - of the pair.
	 * @param context the ContextObject - the key - of the pair.
	 * @return an iterator to the pair found; if no pair equal to the
	 * given one exists the iterator end() will be returned.
	 */
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator
	check_context_listener(ContextObject* context, ContextListener* listener);

	/**
	 * Start to monitor an AtomicContext. 
	 *
	 * @return true if the operation was successful.
	 *
	 * @note At the moment there are only SensorContexts!
	 */
 	bool start_monitoring(AtomicContext* ac);

	/**
	 * Stop monitoring an AtomicContext. Is only done, when the last
	 * listener is has been removed.
	 *
	 * @param ac the context that should not be monitored anymore.
	 *
	 * @return if the operation was successful.
	 *
	 * @note At the moment there are only SensorContexts!
	 */
 	bool stop_monitoring(AtomicContext* ac);

	/**
	 * This function is called when a context changes and therefore all
	 * listeners have to be informed.
	 */
	void dispatch_events(ContextObject* co);

	/** 
	 * The SensorManager - there we register as a listener
	 * and then will be informed about the needed sensor data.
	 */
	SensorManager* sensor_manager;

	/** 
	 * All registered ContextObject sorted by their name.
	 */
	map<const string, ContextObject*> registered_contexts;
	
	/**
	 * All currently monitored (registered at the SensorManager)
	 * SensorContext objects sorted by their name.
	 */
	map<const string, SensorContext*> active_sensorcontexts;

	/**
	 * The mapping from ContextObject to the ContextListener.
	 * Makes it easy to find all interested listeners for a 
	 * ContextObject.
	 */
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> > listener_map;

	/**
	 * We need to know which AtomicContext is used by which
	 * CompositeContext to decide when we can stop monitoring the
	 * AtomicContext.
	 */
	map<AtomicContext*, set<CompositeContext*>, deref_less<AtomicContext*> > related_atomics;
	
	/**
	 * Elements are added when there is an event e.g. from the
	 * SensorManager - the ContextChangedThread removes contexts by
	 * updating and resolving the context-tree.
	 */
	deque<AtomicContext*> changed_atomics;

	/**
	 * The thread that works on the events from the SensorManager.
	 */
	ContextChangedThread ccthread;
    protected:

	/**
	 * Get the size of the changed_atomics queue.
	 * @return how many atomics not yet resolved.
	 */
	uint32_t get_changed_atomics_size(void){
	    ThreadLocker tl(Mutex);
	    return changed_atomics.size();
	}

	/**
	 * Pop a context from the front of the changed_atomics queue. Is
	 * called by the ContextChangedThread.
	 *
	 * @return the front element of the changed_contexts.
	 */
	AtomicContext* pop_changed_atomic(void){
	    ThreadLocker tl(Mutex);

	    AtomicContext* ret = changed_atomics.front();
	    changed_atomics.pop_front();
	    return ret;
	}

	/**
	 * Is called e.g. by the SensorEventFunctor when an event occured in one of
	 * the monitored DataSensors.
	 * 
	 * @param atomic_context the affected AtomicContext.
	 */
	void atomic_context_changed(AtomicContext* atomic_context);
	
    public:
	/** 
	 * The constructor used in get_instance(SensorManager*)
	 * @param sensor_manager there we have to register to get
	 * information about the sensor data.
	 */
 	ContextManager(SensorManager* sensor_manager, EventDispatcher& ed)
	    : ccthread(this, ed)
	{
	    if(sensor_manager == NULL){
		stringstream error_msg; 
		error_msg << "ContextManager::(SensorManager*):\n"
			  << "We need a SensorManager that is not NULL at the "
			  << "instantiation!";
		throw invalidargument_error(error_msg.str(), 
					    sensor_manager);
	    }
	    this->sensor_manager = sensor_manager;
	    ccthread.Start();

	    ++cm_num;

	    d_dbg << ANSI_GREEN << __FFL__ << "\n\t" << ANSI_NORMAL 
		  << "Instances: " << cm_num << endl;

 	}

	/**
	 * Get the single instance of the ContextManager.  
	 * 
	 * @return the only existing instance of the ContextManager.
	 *
	 * @throws invalidargument_error if this is the first call of the
	 * function because we need a valid SensorManager.
	 */
/*	static ContextManager* get_instance(void){
	    if(context_manager == NULL){
		throw unsupported_error(
		    ((string) "ContextManager::get_instance(void):\n")+
		    "We need a SensorManager at the "+
		    "first instantiation - call "+
		    "getInstance(SensorManager sm) "+
		    "please");
	    }

	    return context_manager;
	}
*/
	/**
	 * Get the single instance of the ContextManager.  
	 * 
	 * @param sm the SensorManager to be passed to the constructor if this
	 * function is called the first time; in preceeding calls the former
	 * SensorManager of this class will be overridden.
	 *
	 * @return the only existing instance of the ContextManager.
	 *
	 * @throws invalidargument_error if this is the first call of the
	 * function and the given SensorManager is NULL.
	 */
/*	static ContextManager* get_instance(SensorManager* sm){
	    if(context_manager == NULL){
		if(sm == NULL){
		    throw invalidargument_error(
			((string) "ContextManager::get_instance(SensorManager*):\n")+
			"We need a SensorManager that is not NULL at the "+
			"first instantiation!");
		}
		new ContextManager(sm);
	    } else{
		if(sm != NULL){
		    // TODO: should we call a destructor here?!?
		    sensor_manager = NULL;
		    sensor_manager = sm;
		}
	    }

	    return context_manager;
	}
*/
	/**
	 * The destructor.
	 */
	virtual ~ContextManager(void);

	/**
	 * Add a new ContextObject to the registered_constexts.
	 * 
	 * @param context_object the new context to be registered.
	 * 
	 * @return a value different from 0 if successfully added.
	 */
	uint32_t register_context(ContextObject* context_object);

	/**
	 * Remove the ContextObject with the specified type. All
	 * ContextListeners will also be informed and removed. Additionally
	 * all parent-contexts are recursively deregistred too. It will also
	 * deregister itself from its children.
	 *
	 * @param type the type of the ContextObject to be removed.
	 *
	 * @return a value different from 0 if the operation was successful.
	 */
	uint32_t deregister_context(ContextObject* context_object);

	/**
	 * Get a ContextObject specified by its type.
	 *
	 * @param name the name of the ContextObject to be searched for.
	 *
	 * @return the ContextObject with the specified name or NULL if such
	 * an object does not exist.
	 */
	inline ContextObject* get_context(const string& name){
	    ThreadLocker tl(Mutex);
	    map<const string, ContextObject*>::iterator pos 
		= registered_contexts.find(name);
	    if(pos != registered_contexts.end()){
		return pos->second;
	    } else{
		return NULL;
	    }
	}

	/**
	 * Add a pair of ContextObject and ContextListener - where the
	 * listener wants to be informed about changes of the given context.
	 * 
	 * @param listener the ContextListener to be added.
	 *
	 * @param context the ContextObject the listener is interested in.
	 *
	 * @return a value different from 0 if the operation was successful.
	 */
	uint32_t register_context_listener(ContextListener* listener, 
						  ContextObject* context);
	/**
	 * Remove a pair of ContextObject and ContextListener.
	 *
	 * @param listener the ContextListener to be removed.
	 *
	 * @param context the ContextObject the listener was interested in.
	 *
	 * @return a value different from 0 if the operation was successful.
	 */
	uint32_t deregister_context_listener(ContextListener* listener, 
					     ContextObject* context);

	/**
	 * Add a context to the changed_atomics so the ContextChangedThread
	 * will work on it. No element is inserted twice.
	 *
	 * @param ac the context to be put in the queue
	 */
	void push_changed_atomic(AtomicContext* ac){
	    ThreadLocker tl(Mutex);

	    deque<AtomicContext*>::iterator it = changed_atomics.begin();
	    while(it != changed_atomics.end()){
		if(**it == *ac){
		    return;
		}
		++it;
	    }
	    changed_atomics.push_back(ac);
	}

    }; // class ContextManager
} // namespace context
} // namespace iwear 

#endif // __CONTEXTMANAGER_H

