// {{{ the iWear header 
/**
 * @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
 */
// }}}

#ifndef __APPLICATIONSERVICE_H
#define __APPLICATIONSERVICE_H

#ifndef __APPLICATION_H
#include <iwear_uiservices/applicationbase.h>
#endif

// Our namespaces
using namespace std;
using namespace iwear;

namespace iwear{

    // Forward declarations (namespace iwear)
    class ServiceManager;
    class EventDispatcher;

    namespace uiservices{

	// Forward Declarations (namespace iwear::uiservices)
	class SimpleMenuFunctor;
	class ApplicationServiceExitFunctor;
	class ApplicationLauncher;
	class Daemon;


enum APPLICATION_SERVICE_STATE
{
    STATE_RUNNING,
    STATE_NUKE_REQUESTED,
    STATE_DONE,
    num_APPLICATION_SERVICE_STATE
};


inline const char* to_string(APPLICATION_SERVICE_STATE state)
{
    switch(state){
    case STATE_RUNNING :
	return "APPLICATION_SERVICE_STATE::STATE_RUNNING";
	break;
    case STATE_NUKE_REQUESTED :
	return "APPLICATION_SERVICE_STATE::STATE_NUKE_REQUESTED";
	break;
    case STATE_DONE :
	return "APPLICATION_SERVICE_STATE::STATE_DONE";
	break;
    default :
	return "APPLICATION_SERVICE_STATE::<invalid_information>";
    }
}




class ApplicationService : public ApplicationBase
{
    /**
     * @see ApplicationServiceExitFunctor
     */
    friend class ApplicationServiceExitFunctor;

    /**
     * Dynamic applications need means to get pointer to the
     * Input-, Output- and ServiceManager. Since we don't want to
     * provide public getter for everybody we declare the application
     * class as friend of the ApplicationService class
     */
    friend class Application;

    /**
     * Dynamic daemons just like dynamic applications need means
     * to get pointer to the Input-, Output- and ServiceManager.
     * Therefore we use the same mechanism
     */
    friend class Daemon;

    friend class ApplicationLauncher;

public: 

    /**
     * The constructor for the application service
     * @param sm pointer to the service manager
     * @param im pointer to the input manager
     * @param om pointer to the output manager. This handle
     *        is used to display output data and to inform the
     *        output manager that a new application should
     *        be put in focus
     */
    ApplicationService(Configuration* config,
		       EventDispatcher* ed,
		       ServiceManager* sm,
		       InputManager* im,
		       OutputManager* om,
		       SensorManager* sens);

    /**
     * The destructor
     */
    virtual ~ApplicationService(void);

    /**
     * This method is called when the application service thread
     * is started
     */
    virtual void Run(void);

    /**
     * Use dispatch from Event Dispatcher.
     */
    inline virtual void dispatch(EventBase& evt,
				 event_priority prio = prio_normal)
    {
	this->event_dispatcher->dispatch(evt, prio);
    }

    /**
     * Register an application launcher --> There should
     * only be one allowed!
     */
    void register_application_launcher(ApplicationLauncher* launcher);

     inline void push_exit_event( const uid* app_id ){
	 ThreadLocker tl(Mutex);
	 if(app_id != NULL){
	     this->exit_event_queue.push(app_id);
	 }
    }


protected:

    void Init_Application(void);

    void Final_Application(void);

    /**
     * Notification method which is called by the application service
     * exit functor when the user activates (clicks) the button which
     * terminates an application
     */
    void handle_application_termination(const uid* app_id);

    /**
     * Notification method which is called by the application service
     * menu functor when the user activates (clicks) the button which
     * shuts down the whole framework --> application service and all 
     * dependent applications
     */
    void handle_system_termination(void);

    /**
     * Notification method which is called by the application service
     * menu functor when an application is selected (clicked) from the
     * active application bar. It is used to tell the input and output
     * manager that the application focus changes through delegation
     * @param app_id Important: This is the id of the button which
     * represents the application to receive the focus. An appropriate
     * mapping is yet to be done!
     */
    void switch_application_focus(const uint32_t app_button_id);

    /**
     * This method actually performs a focus change
     * @param app_id the ID of the application which should be set in
     *        focus. If the pointer is NULL the application service
     * itself receives the focus (default)
     */
    void switch_application_focus(const uid* app_id=NULL);

    /**
     * The most important method of this application. It 
     * synchronizes the internal list of active applications
     * with the information acquired from the ServiceManager 
     */
    void synchronize_active_applications( void );

    /**
     * Getter for the iWear configuration
     */
    inline Configuration* get_configuration( void ) const {
	return this->configuration;
    }

    /**
     * Getter for the OutputManager
     */
    inline OutputManager* get_output_manager( void ) const {
	return this->output_manager;
    }

    /**
     * Getter for the InputManager
     */
    inline InputManager* get_input_manager( void ) const {
	return this->input_manager;
    }

    /**
     * Getter for the ServiceManager
     */
    inline ServiceManager* get_service_manager( void ) const {
	return this->SerMan;
    }

    inline EventDispatcher* get_eventdispatcher() const { return event_dispatcher; }

    /**
     * Getter for the SensorManager
     */
    inline SensorManager* get_sensor_manager( void ) const {
	return this->sensor_manager;
    }

    /**
     * Map of applications which are currently active in the
     * iWear framework
     */
    map<const uid*, ApplicationBase*, deref_less<const uid*> > active_applications;

    /**
     * Mapping from application IDs to menu button ids 
     * We use this map to remember which button in the application
     * service's main menu represents which application
     */
    map<const uid*, uint32_t, deref_less<const uid*> > app_id2button_id;

    /**
     * The functor which is called by the application service menu
     * when either an application or the nuke button is clicked
     */
    SimpleMenuFunctor* menu_functor;

    /**
     * This is the structure where the exit functors for the 
     * active applications are stored.
     */
    map<const uid*, ApplicationServiceExitFunctor*, deref_less<const uid*> > 
    app_id2exit_func_id;

    /**
     * pointer to the event dispatcher which is required in order
     * to redirect dispatch-requests from other applications and
     * the application service itself.
     */
    EventDispatcher* event_dispatcher; 

    /**
     * The application launcher which is used in the framework
     * to load dynammic application from precompiled libraries
     * at runtime
     */
    ApplicationLauncher* application_launcher;

    /**
     * This is a flag which determines wheter the application
     * service should leave its runtime loop or not. Note that
     * this is irreversible
     */
    APPLICATION_SERVICE_STATE application_service_state;

    uint32_t nuke_button; 

    inline const uid* pop_exit_event( void ){
	ThreadLocker tl(Mutex);
	if(this->exit_event_queue.empty()){
	    return NULL;
	}else{
	    const uid* app_id 
		= this->exit_event_queue.front();
	    this->exit_event_queue.pop();
	    return app_id;
	}
    }
    
    queue<const uid*> exit_event_queue;

};

    } // namespace uiservices
} // namespace iwear

#endif // __TESTAPPLICATION_H


