#ifdef __APPLE__ 
#include "definitions_mac.h" 
#else 
#include "definitions.h" 
#endif 

 #ifdef ENABLE_FORCE_FEEDBACK

#include <iostream>
#include <string>
#include "forcefeedback.h"
using std::string; using std::cout; using std::endl;

FORCEFEEDBACK::FORCEFEEDBACK( string device )
  : device_name(device), enabled(true), stop_and_play(false)
{
	unsigned long key_bits[32],abs_bits[32],ff_bits[32];
	struct input_event event;
	int valbuf[16];

	/* Open event device with write permission */
	device_handle = open(device_name.c_str(),O_RDWR|O_NONBLOCK);
	if (device_handle<0) {
		cout << "ERROR: can not open " << device_name << " (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Which buttons has the device? */
	memset(key_bits,0,32*sizeof(unsigned long));
	if (ioctl(device_handle,EVIOCGBIT(EV_KEY,32*sizeof(unsigned long)),key_bits)<0) {
		cout << "ERROR: can not get key bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Which axes has the device? */
	memset(abs_bits,0,32*sizeof(unsigned long));
	if (ioctl(device_handle,EVIOCGBIT(EV_ABS,32*sizeof(unsigned long)),abs_bits)<0) {
		cout << "ERROR: can not get abs bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Now get some information about force feedback */
	memset(ff_bits,0,32*sizeof(unsigned long));
	if (ioctl(device_handle,EVIOCGBIT(EV_FF ,32*sizeof(unsigned long)),ff_bits)<0) {
		cout << "ERROR: can not get ff bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Which axis is the x-axis? */
	if      (TEST_BIT(ABS_X    ,abs_bits)) axis_code=ABS_X;
	else if (TEST_BIT(ABS_RX   ,abs_bits)) axis_code=ABS_RX;
	else if (TEST_BIT(ABS_WHEEL,abs_bits)) axis_code=ABS_WHEEL;
	else {
		cout << "ERROR: no suitable x-axis found [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* get axis value range */
	if (ioctl(device_handle,EVIOCGABS(axis_code),valbuf)<0) {
		cout << "ERROR: can not get axis value range (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}
	axis_min=valbuf[1];
	axis_max=valbuf[2];
	if (axis_min>=axis_max) {
		cout << "ERROR: bad axis value range (" << axis_min << "," << axis_max << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* force feedback supported? */
	if (!TEST_BIT(FF_CONSTANT,ff_bits)) {
		cout << "ERROR: device (or driver) has no force feedback support [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Switch off auto centering */
	memset(&event,0,sizeof(event));
	event.type=EV_FF;
	event.code=FF_AUTOCENTER;
	event.value=0;
	if (write(device_handle,&event,sizeof(event))!=sizeof(event)) {
		cout << "ERROR: failed to disable auto centering (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Initialize constant force effect */
	memset(&effect,0,sizeof(effect));
	effect.type=FF_CONSTANT;
	effect.id=-1;
	effect.trigger.button=0;
	effect.trigger.interval=0;
	effect.replay.length=0xffff;
	effect.replay.delay=0;
	effect.u.constant.level=0;
	effect.direction=0xC000;
	effect.u.constant.envelope.attack_length=0;
	effect.u.constant.envelope.attack_level=0;
	effect.u.constant.envelope.fade_length=0;
	effect.u.constant.envelope.fade_level=0;

	/* Upload effect */
	if (ioctl(device_handle,EVIOCSFF,&effect)==-1) {
		cout << "ERROR: uploading effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Start effect */
	memset(&event,0,sizeof(event));
	event.type=EV_FF;
	event.code=effect.id;
	event.value=1;
	if (write(device_handle,&event,sizeof(event))!=sizeof(event)) {
		cout << "ERROR: starting effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}
}


void FORCEFEEDBACK::update(double force, double * position)
{
	if( !enabled )
		return;

        struct input_event event;

        /* Delete effect */
        if (stop_and_play && effect.id!=-1) {
                if (ioctl(device_handle,EVIOCRMFF,effect.id)==-1) {
                        cout << "ERROR: removing effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
                        disable();
			return;
                }
                effect.id=-1;
        }

        /* Set force */
        if (force>1.0) force=1.0;
        if (force<-1.0) force=1.0;
        effect.u.constant.level=(short)(force*32767.0); /* only to be safe */
        effect.direction=0xC000;
        effect.u.constant.envelope.attack_level=(short)(force*32767.0); /* this one counts! */
        effect.u.constant.envelope.fade_level=(short)(force*32767.0); /* only to be safe */

        /* Upload effect */
        if (ioctl(device_handle,EVIOCSFF,&effect)==-1) {
              cout << "error uploading effect" << endl;
                /* We do not exit here. Indeed, too frequent updates may be
                 * refused, but that is not a fatal error */
        }

        /* Start effect */
        if (stop_and_play && effect.id!=-1) {
                memset(&event,0,sizeof(event));
                event.type=EV_FF;
                event.code=effect.id;
                event.value=1;
                if (write(device_handle,&event,sizeof(event))!=sizeof(event)) {
                        cout << "ERROR: re-starting effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
                        disable();
			return;
                }
        }

        /* Get events */
        while (read(device_handle,&event,sizeof(event))==sizeof(event)) {
                if (event.type==EV_ABS && event.code==axis_code) {
                        *position=((double)(((short)event.value)-axis_min))*2.0/(axis_max-axis_min)-1.0;
                        if (*position>1.0) *position=1.0;
                        else if (*position<-1.0) *position=-1.0;
                }
        }
}

#endif // ENABLE_FORCE_FEEDBACK
