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

#include <iwear-context/contextmanager.h>
#include <iwear-context/compositecontext.h>

#ifndef __SENSOREVENTFUNCTOR_H
#include <iwear-context/sensoreventfunctor.h>
#endif

#include <list>

namespace iwear{
namespace context{

    uint32_t ContextManager::cm_num = 0;

    ContextManager::~ContextManager(void){
	// stop the thread
	ccthread.stop();
	//ccthread.Terminate();

	d_dbg << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL 
	      << "listeners:" << listener_map.size() 
	      << "\tcontexts:" << registered_contexts.size() 
	      << "\tInstances: " << cm_num << endl;
	
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator tmp;

	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator it
	    = listener_map.begin();
	while(it != listener_map.end())
	{
	    //deregister_context_listener(it->second, it->first);
	    //++it;
	 
	    tmp = it;
	    ++it;
	    deregister_context_listener(tmp->second, tmp->first);

	}

	d_dbg << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL << endl;
	
	ContextObject* tmp2;
	map<const string, ContextObject*>::iterator it2
	    = registered_contexts.begin();
	while(it2 != registered_contexts.end())
	{
	    //deregister_context(it2->second);
	    //++it2;

	    tmp2 = it2->second;
	    ++it2;
	    deregister_context(tmp2);
	}

	--cm_num;

	d_dbg << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL 
	      << "Finished destruction. Instances left: " << cm_num << endl;
	

    }
    
    uint32_t ContextManager::register_context(ContextObject* context_object){
	ThreadLocker tl(Mutex);
        // insert key-value-pair if key not already in the map
        if(registered_contexts.find(context_object->get_name()) 
	   != registered_contexts.end()){
            d_dbg << "ContextManager::register_context():\ncan't register context ("
                  << context_object->get_name()
                  << ") - it's already registered!" << endl;
            return false;
        }

        bool ret = registered_contexts.insert(
            pair<const string, ContextObject*>(
                context_object->get_name(),
                context_object)
            ).second;
        if(context_object->get_context_type() == COMPOSITE){
	    static_cast<CompositeContext*>(context_object)->register_children(this);
	}
	return ret;
    }

    uint32_t ContextManager::deregister_context(ContextObject* context_object){
	d_nons << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL 
	      << "will deregister " << context_object->get_name() << endl;

	ThreadLocker tl(Mutex);
	if(registered_contexts.find(context_object->get_name()) ==
	   registered_contexts.end())
	{
	    d_dbg << ANSI_RED << __FFL__ << ":\n\t" << ANSI_NORMAL 
		  << "can't deregister context " << context_object->get_name() 
		  << "because it's not registered" << endl;
	    return false;
	}

	// deregister the parents
	d_nons << ANSI_BROWN << __FFL__ << ":\n\t" << ANSI_NORMAL 
	       << "parents of " << context_object->get_name() << ": " 
	       << context_object->get_parents().size() << endl;

	map<const string, ContextObject*>::iterator it = 
	    context_object->get_parents().begin();
	while(it != context_object->get_parents().end()){
	    deregister_context(it->second);
	    ++it;
	}

	// check if the context is still in use --> then tell the listener(s)
	// to stop usage and delete them from the listener_map
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator lo = 
	    listener_map.lower_bound(context_object);
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator up = 
	    listener_map.upper_bound(context_object);
	if(lo != up){
	    multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator pos = 
		lo;
	    while(pos != up){
		switch(context_object->get_context_type()){
		    case ATOMIC:
			switch(static_cast<AtomicContext*>(context_object)->get_atomic_type()){
			    case SENSOR:
				// if we have a sensorcontext we have to deregister at
				// the sensormanager
				stop_monitoring(static_cast<SensorContext*>(context_object));
				break;
			    default:
				break;
			}
			break;
		    default:
			break;
		}
		pos->second->stop_listening(context_object);
		++pos;
	    }
	    // erase them from the list
	    listener_map.erase(lo, up);
	    
	    if(context_object->get_context_type() == COMPOSITE){
		// there are no more listeners for this context --> maybe some
		// related atomics can be removed
		vector<AtomicContext*> rel_acs 
		    = static_cast<CompositeContext*>(context_object)->get_related_atomics();
		vector<AtomicContext*>::iterator rel_acs_it = rel_acs.begin();
		while(rel_acs_it != rel_acs.end()){
		    // check if there are other contexts that have the
		    // actual atomic as a related atomic
		    related_atomics[*rel_acs_it].erase(static_cast<CompositeContext*>(context_object));
		    stop_monitoring(*rel_acs_it);
		    ++rel_acs_it;		
		}
	    }
	}

	// remove the children if context_object is composite
	if(context_object->get_context_type() == COMPOSITE){
	    CompositeContext* cc = static_cast<CompositeContext*>(context_object);
	    it = cc->get_children().begin();
	    while(it != cc->get_children().end()){
		cc->remove_child(it->second->get_name());
		++it;
	    }
	}

	// now all links to this context should be cleanly removed - finally
	// it can be removed
	d_dbg << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL 
	      << "deregistered context " << context_object->get_name() << endl;	
	return registered_contexts.erase(context_object->get_name());
    }

    uint32_t ContextManager::register_context_listener(ContextListener* listener, 
				       ContextObject* context){
	d_nons << ANSI_GREEN << __FFL__ << ":\n\t" << ANSI_NORMAL 
	      << "will register listener for: " << context->get_name() << endl;	
	ThreadLocker tl(Mutex);
	// check if the pair already exists
	if(check_context_listener(context, listener) != listener_map.end()){
	    return false;
	}

	// insert a new pair
	listener_map.insert(
	    pair<ContextObject*, ContextListener*>(context, listener));

	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator lo = 
	    listener_map.lower_bound(context);
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator up = 
	    listener_map.upper_bound(context);

	++lo;
	if(lo == up){
	    // first listener for this context - start monitoring the related atomics
	    vector<AtomicContext*>::iterator it;
	    vector<AtomicContext*> rel_acs;
	    switch(context->get_context_type()){
		case COMPOSITE:
		    // get all atomics and start monitoring them
		    rel_acs = static_cast<CompositeContext*>(context)->get_related_atomics();
		    it = rel_acs.begin();
		    while(it != rel_acs.end()){
			// so we later know, that the atomic is needed by context
			related_atomics[*it].insert(static_cast<CompositeContext*>(context));
			if(related_atomics[*it].size() == 1){
			    start_monitoring(*it);
			}
			++it;
		    }
		    break;
		case ATOMIC:
		    start_monitoring(static_cast<AtomicContext*>(context));
		    break;
		default:
		    stringstream stream;
		    stream << __FFL__ << ":\nfound an invalid context type in context object (" 
			   << context->get_name() << ") context-type: " 
			   << context->get_context_type() << endl;
		    throw consistency_error(stream.str());
	    }
	}

	return true;
    }

    uint32_t ContextManager::deregister_context_listener(ContextListener* listener, 
							 ContextObject* context){
	d_nons << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL 
	      << "will deregister listener-pair for " << context->get_name() << endl;	

	ThreadLocker tl(Mutex);

	// get the position
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator pos
	    = check_context_listener(context, listener);

	// found?
	if(pos == listener_map.end()){
	    d_warn << ANSI_DARKRED << __FFL__ << ":\n\t" << ANSI_NORMAL 
		  << "listener-pair not found for " << context->get_name() << endl;
	    return false;
	}
	// erase the found element
	listener_map.erase(pos++);

	// check if we erased the last listener for this context
	// then stop monitoring the related atomics
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator lo = 
	    listener_map.lower_bound(context);
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator up = 
	    listener_map.upper_bound(context);

	if(lo == up){
	    // was the last listener for this context - check if we can stop
	    // monitoring some related atomics
	    vector<AtomicContext*>::iterator it;
	    vector<AtomicContext*> rel_acs;
	    switch(context->get_context_type()){
		case COMPOSITE:
		    // get all atomics and stop monitoring them
		    rel_acs = static_cast<CompositeContext*>(context)->get_related_atomics();
		    it = rel_acs.begin();
		    while(it != rel_acs.end()){
			// check if there are other contexts that have the
			// actual atomic as a related atomic
			related_atomics[*it].erase(static_cast<CompositeContext*>(context));
			stop_monitoring(*it);
			++it;
		    }
		    break;
		case ATOMIC:
		    stop_monitoring(static_cast<AtomicContext*>(context));
		    break;
		default:
		    stringstream stream;
		    stream << __FFL__ << ":\nfound an invalid context type in context object (" 
			   << context->get_name() << ") context-type: " 
			   << context->get_context_type() << endl;
		    throw consistency_error(stream.str());
	    }
	}

	d_dbg << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL 
	      << "deregistered listener-pair for " << context->get_name() << endl;	
	return true;
    }

    multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator
    ContextManager::check_context_listener(ContextObject* context, ContextListener* listener){
	ThreadLocker tl(Mutex);
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator lo = 
	    listener_map.lower_bound(context);
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator up = 
	    listener_map.upper_bound(context);
	    
	// check if the pair already exists - if it does, return an iterator
	// of its position
	if(lo != up){
	    multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator pos = lo;
	    for(; pos != up; ++pos){
		if(pos->second == listener){
		    return pos;
		}
	    }
	}
	return listener_map.end();
    }

    bool ContextManager::start_monitoring(AtomicContext* ac){
	ThreadLocker tl(Mutex);

	switch(ac->get_atomic_type()){
	    case SENSOR:
		// check if we're already monitoring it
		if(active_sensorcontexts.find(ac->get_name()) != 
		   active_sensorcontexts.end()){
		    d_nons << ANSI_GREEN << __FFL__ << ":\n\t" << ANSI_NORMAL 
			  << "returning - already monitoring: " << ac->get_name() << endl;
		    return true;
		}

		SensorContext* sc;
		sc = static_cast<SensorContext*>(ac);
		d_dbg << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL 
		      << "will create sensor event functor: " << ac->get_name() << endl;
		sc->create_sensor_event_functor(this);
		// register at the sensor-manager
		d_dbg << "\t" 
		      << sc->get_related_datasensor_type() << "\n\t"
		      << "lower: " << sc->get_lower() << "\n\t"
		      << "upper: " << sc->get_upper() << endl;
		sc->set_datasensorevent(
		    sensor_manager->register_sensor_event(
			sc->get_related_datasensor_type(), 
			*(sc->get_sensor_event_functor()),
			sc->get_lower(),
			sc->get_upper()
			)
		    );
			
		// add to active_sensorcontexts
		active_sensorcontexts.insert(
		    pair<const string, SensorContext*>(
			sc->get_name(),
			sc));
		break;
	    default:
		return false;
	}

	return true;
    }
    
    bool ContextManager::stop_monitoring(AtomicContext* ac){
	ThreadLocker tl(Mutex);
	// we can't stop monitoring if there is another context that needs
	// this atomic
	if(related_atomics[ac].size() != 0){
	    d_warn << ANSI_RED << __FFL__ << ":\n\t" << ANSI_NORMAL
		   << "won't stop monitoring the atomic because there are other "
		   << "contexts that depend on this atomic!" 
		   << endl;
	    return false;
	}

	switch(ac->get_atomic_type()){
	    case SENSOR:{
		SensorContext* sc = static_cast<SensorContext*>(ac);
		try{
		    if(active_sensorcontexts.find(sc->get_name()) == active_sensorcontexts.end()){
			d_dbg << ANSI_BLUE << __FFL__ << "\n\t" << ANSI_NORMAL 
			      << "want to deregister sev that's not registered!!\treturning true anyway" 
			      << endl;
			return true;
			break;
		    }
		    sensor_manager->deregister_sensor_event(sc->get_datasensorevent());
		    active_sensorcontexts.erase(sc->get_name());
		} catch(const exception& e){
		    d_warn << ANSI_RED << __FFL__ << "\n\t" << ANSI_NORMAL 
			   << "exception!!!: \t " << e.what() << endl;
		    return false;
		}
		break;
	    }
	    default:
		break;
	}

	d_dbg << ANSI_BLUE << __FFL__ << ":\n\t" << ANSI_NORMAL 
	      << "stopped monitoring for " << ac->get_name() << endl;
	return true;
    }

    void ContextManager::atomic_context_changed(AtomicContext* ac)
    {
	ThreadLocker tl(Mutex);

	// resolve the whole context-tree/graph that starts upwards from
	// the given leaf (name) and consider the changes due to the new
	// situation (value)
        vector<CompositeContext*> to_resolve;
	// dispatch the events for the sensorcontext
	dispatch_events(ac);

        // put parents of the given sensorcontext in a vector
        map<const string, ContextObject*>::iterator it = ac->get_parents().begin();
        while(it != ac->get_parents().end()){
            to_resolve.push_back(static_cast<CompositeContext*>(it->second));
            ++it;
        }

        // sort the vector by comparing in the levels of the contexts - in
        // ascending order
        sort(to_resolve.begin(), to_resolve.end(), 
            deref_less<CompositeContext*>());

	bool appended = false;
        for(uint32_t i = 0; i < to_resolve.size(); i++){
            // resolve the context and add its parents to the contexts to
            // be resolved if the context has changed
            if(to_resolve[i]->resolve_context()){
		// maybe there are listeners interested in the change
		dispatch_events(to_resolve[i]);

                map<const string, ContextObject*>::iterator tmpit
                    = to_resolve[i]->get_parents().begin();
                // append the parents to the list of the contexts that
                // have to be resolved if they are not already in it
                while(tmpit != to_resolve[i]->get_parents().end()){
                    if(find(to_resolve.begin(), to_resolve.end(), tmpit->second) 
                        == to_resolve.end())
                    {
                        to_resolve.push_back(
                            static_cast<CompositeContext*>(tmpit->second));
                        appended = true;
                    }
                    ++tmpit;
                }
                // sort them again
                if(appended){
                    sort(to_resolve.begin(), to_resolve.end(), 
                        deref_less<CompositeContext*>());
                    appended = false;
                }
            }
	}
    }

    void ContextManager::dispatch_events(ContextObject* co){
	ThreadLocker tl(Mutex);
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator lo = 
	    listener_map.lower_bound(co);
	multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator up = 
	    listener_map.upper_bound(co);
	/* First the current registered listeners are copied to a
	   list. This list will not change if the listener
	   deregisteres itself. Working directly on the map would
	   cause problems with the iterators since the map will be
	   changed on the deregistring. */
	list<ContextListener*> context_listeners;
	while(lo != up){
	    context_listeners.push_back(lo->second);
	    ++lo;
	}
	/* iterator over the contextlisteners and call context_changed. */
	list<ContextListener*>::iterator iter = context_listeners.begin();
	while(iter != context_listeners.end() ){
	    
	    /* check if the listener is still registered. */
	    multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator check_lo = 
		listener_map.lower_bound(co);
	    multimap<ContextObject*, ContextListener*, deref_less<ContextObject*> >::iterator check_up = 
		listener_map.upper_bound(co);
	    bool is_still_registered = false;
	    /* check if the listener is still registered. The context
	       changed call must not be called within the while loop
	       because of the problem which was described above. */
	    while(check_lo != check_up){
		is_still_registered = (check_lo->second == *iter);
		/* break if the listener is found. */
		if (is_still_registered) {
		    break;
		}
		++check_lo;
	    }
	    /* if it is still registered call context changed. */
	    if (is_still_registered) {
		d_nons  << __FL__ << " context name: " << co->get_name() 
			<< " context value: " <<  co->get_value() 
			<< " last timestamp: "<< co->get_last_timestamp()  
			<< " address: " << *iter << endl;
		
		(*iter)->context_changed(co->get_name(),
					 co->get_value(), 
					 co->get_last_timestamp());
	    }
	    ++iter;
	}
    }
    

} // namespace context
} // namespace iwear

