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

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <sys/time.h>
#include <math.h>
#include <bitset>
#include <vector>

#ifndef __DEVCOM_H
#include <iwear_iowarrior/devcom.h>
#endif

#ifndef __MODEFUNCTOR_H
#include <iwear_iowarrior/iowfunctor.h>
#endif

#include <iwear/thread.h>
#include <iwear/threadlocked.h>

using namespace std;

namespace iwear {
namespace iowarrior {

// modes of the leds
enum led_mode {IOW_OFF=0, IOW_ON=1, IOW_FSLOW=2, IOW_FFAST=3};
    
// num of LEDs of the IO Warrior hardware 
const int NUM_OF_LEDS = 4;

// num of states of the switch of the IO Warrior hardware
const int NUM_OF_MODES = 6;

// num of buttons of the IO Warrior hardware
const int NUM_OF_BUTTONS = 1;

// ports of the IO Warrior hardware the LEDs are connected at
const int LED[NUM_OF_LEDS] = {0, 1, 2, 3};

// ports of the IO Warrior hardware the switch is connected at
const int MODE[NUM_OF_MODES] = {4, 5, 6, 7, 9, 11};

// ports of the IO Warrior hardware the buttons are connected at
const int BUTTON[NUM_OF_BUTTONS] = {13};

// the connect state of the IO Warrior
extern int connected;

// the complete message, which has to be sent at every communication
extern bitset<16> msg;

// the actual LED state
extern int led_state[NUM_OF_LEDS];
// the actual LED flash state
extern int led_flash_state[NUM_OF_LEDS];

// List of registered functors
extern vector <IOWFunctor*> iowf_list;
	
// memorize the old mode to check, if mode has changed
extern switch_mode old_smode;
extern switch_mode new_smode;

// memorize the old button mode to check, if mode has changed
extern button_mode old_bmode;

// memorize the old time to check, if mode has changed and is stable
extern double old_time;

class IWearIOWarrior; // <-- neccessary forward-declaration...
extern IWearIOWarrior* iowarrior_instance;

/**
 * This class provides functions for controlling the iWear IO Warrior.
 * You can switch LEDs on or off or let them flash and you can
 * request the position of the six state switch. 
 */
class IWearIOWarrior: public Devcom, Thread{

    private:

	/**
	 * Instance of the Devcom class
	 */    
	// Devcom iow;

	/**
	 * Send a message to the IO Warrior hardware as a bitset
	 * @param msg the message to be sent
	 */    
	void send_msg(bitset<16> msg);

	/**
	 * Get the port data from the IO Warrior hardware as a bitset
	 * @return the actual port data
	 */    
	bitset<16> get_msg();

	/**
	 * Is set to true in the function stop() if the thread of this module should stop execution.
	 */
	bool stop_now;

        bool should_stop_now();
    
	/**
	 * Get an instance of this class. This constructor already tries to
	 * connect to the IO Warrior using the connect function, if it's connected.
	 */    
        IWearIOWarrior(EventDispatcher& ed);

    public:

        static IWearIOWarrior* get_instance( EventDispatcher& ed ){
	    if(iowarrior_instance == NULL){
		iowarrior_instance = new IWearIOWarrior(ed);
	    }
	    return iowarrior_instance;
	}

	/**
	 * Look continuously for the actual mode and let the leds flash if necessary
	 */
	virtual void Run(void);


	/**
	 * Delete an instance of this class, and disconnect the IO Warrior using
	 * the function disconnect.
	 */    
        ~IWearIOWarrior(void);

	/**
	 * Register a functor for executing, if the mode has changed
	 */
	void register_functor(IOWFunctor* iowf);
	
	/**
	 * Deregister a functor for executing, if the mode has changed
	 */
	void deregister_functor(IOWFunctor* iowf);

	/**
	 * Connect to the IO Warrior for using the switch and the LEDs.
	 * Use this function for trying to connect to the IO Warrior,
	 * if it wasn't connected with the constructor.
	 * @return error message
	 */    
	int connect();

	/**
	 * Disconnect the IO Warrior. The destructor makes it, too.
	 * @return error message
	 */    
	int disconnect();
	
	/**
	 * Test, if the IO Warrior is connected to any usb port or not
	 * @return true, if the IO Warrior is connected at any usb port, false otherwise
	 */    
	bool is_connected();

	/**
	 * Start the functions of this module
	 */
	void start();

	/**
	 * Stops the thread of this module.
	 */
	void stop();
	
	/**
	 * Switch the LED ledref to the given mode.
	 * @param ledref the LED is to be switched, possible LEDs are 0, 1, 2, 3
	 * @param mode the mode the LED is to be switched at, possible modes are
	 * IOW_OFF (LED is switched off), IOW_ON (LED is switched on),
	 * IOW_FSLOW (LED is flashing slow), IOW_FFAST (LED is flashing fast)
	 */    
	void switch_led(int ledref, int mode);

	/**
	 * Switch all the LEDs to the given modes.
	 * @param leds a string which contains all modes all LEDs are to be set at.
	 * The string has a length of four characters between '0' and '3', where '0' means
	 * LED off, '1' LED on,  '2' LED is flashing slow and '3' LED is flashing fast.
	 * For example: "2103" means: LED1 is flashing slow, LED2 is turned on, LED3
	 * is turned off and LED4 is flashing fast.
	 */    
	void switch_leds(const string& leds);

	/**
	 * Check if any mode has changed, then execute the registered functors
	 */
	void check_mode();

	/**
	 * Check if after a detected deconnection we can find the device
	 * again... otherwise sleep ~500ms
	 */
	void check_connection();
	
	/**
	 * Get the position of the six state switch
	 * @return the actual switch position: IOW_MOD0, IOW_MOD1, IOW_MOD2, IOW_MOD3,
	 * IOW_MOD4 or IOW_MOD5
	 */    
	switch_mode get_switch_mode();

	/**
	 * Get the position of the button - up or down
	 * @return the actual button position: IOW_BUTTONDOWN or IOW_BUTTONUP
	 */    
	button_mode get_button_mode();
};
} // namespace iowarrior
} // namespace iwear

#endif // __IWEARIOWARRIOR_H

