/*
	powermate.cc
	Ben Loftis
	Created: 03/26/07 20:07:56
*/


#include <linux/input.h>
#include <cstring>
#include <cerrno>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>

#include <i18n.h>
#include <pbd/xml++.h>
#include <pbd/error.h>
#include <glibmm.h>

#include "powermate.h"

using namespace ARDOUR;
using namespace std;
using namespace sigc;
using namespace PBD;

#define NUM_VALID_PREFIXES 2

static const char *valid_prefix[NUM_VALID_PREFIXES] = {
  "Griffin PowerMate",
  "Griffin SoundKnob"
};

#define NUM_EVENT_DEVICES 16

int open_powermate(const char *dev, int mode)
{
  if (!Glib::file_test (dev, Glib::FILE_TEST_EXISTS)) {
	  return -1;
  }
  int fd = open(dev, mode);
  int i;
  char name[255];

  if(fd < 0){
	  if (errno != EACCES) {
		  error << string_compose ("Unable to open \"%1\": %2", dev, strerror(errno)) << endmsg;
	  }
	  return -1;
  }

  if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0){
    error << string_compose ("\"%1\": EVIOCGNAME failed: %2", dev, strerror(errno)) << endmsg;
    close(fd);
    return -1;
  }

  // it's the correct device if the prefix matches what we expect it to be:
  for(i=0; i<NUM_VALID_PREFIXES; i++)
    if(!strncasecmp(name, valid_prefix[i], strlen(valid_prefix[i])))
      return fd;

  close(fd);
  return -1;
}

int find_powermate(int mode)
{
  char devname[256];
  int i, r;

  for(i=0; i<NUM_EVENT_DEVICES; i++){
    sprintf(devname, "/dev/input/event%d", i);
    r = open_powermate(devname, mode);
    if (r >= 0) {
	    return r;
    }
  }
  return -1;
}

PowermateControlProtocol::PowermateControlProtocol (Session& s)
	: ControlProtocol  (s, "powermate")
{
}

PowermateControlProtocol::~PowermateControlProtocol ()
{
	set_active (false);
}

bool
PowermateControlProtocol::probe ()
{
	int port = find_powermate( O_RDONLY ); 

	if (port < 0) {
 		printf ("powermate: Opening of powermate failed - %s\n", strerror(errno));
		close (port);
		return false;
	}

	close (port);
	return true;
}

int
PowermateControlProtocol::set_active (bool inActivate)
{
	if (inActivate != _active) {

		if (inActivate) {

			mPort = find_powermate(O_RDONLY);
			
			if ( mPort < 0 ) {
				return -1;
			}
			
			if (pthread_create (&mThread, 0, SerialThreadEntry, this) == 0) {
				_active = true;
			} else {
				return -1;
			}

			printf("Powermate Control Protocol activated\n");

		} else {
			pthread_cancel (mThread);
			close (mPort);
			_active = false;
			printf("Powermate Control Protocol deactivated\n");
		} 
	}

	return 0;
}

XMLNode&
PowermateControlProtocol::get_state () 
{
	XMLNode* node = new XMLNode (X_("Protocol"));
	node->add_property (X_("name"), _name);
	return *node;
}

int
PowermateControlProtocol::set_state (const XMLNode& node)
{
	return 0;
}


void*
PowermateControlProtocol::SerialThreadEntry (void* arg)
{
	return static_cast<PowermateControlProtocol*>(arg)->SerialThread ();
}

#define BUFFER_SIZE 32

bool held = false;
bool skippingMarkers = false;

void
PowermateControlProtocol::ProcessEvent(struct input_event *ev)
{
#ifdef VERBOSE
  fprintf(stderr, "type=0x%04x, code=0x%04x, value=%d\n",
	  ev->type, ev->code, (int)ev->value);
#endif

  switch(ev->type){
  case EV_MSC:
    printf("The LED pulse settings were changed; code=0x%04x, value=0x%08x\n", ev->code, ev->value);
    break;
  case EV_REL:
    if(ev->code != REL_DIAL)
      fprintf(stderr, "Warning: unexpected rotation event; ev->code = 0x%04x\n", ev->code);
    else{
    	if (held) {
		//click and hold to skip forward and back by markers
    		skippingMarkers = true;;
		if (ev->value > 0)
    			next_marker();
		else
			prev_marker();
    	} else {
		//scale the range so that we can go from +/-8x within 180 degrees, with less precision at the higher speeds 
    		float speed = get_transport_speed();
    	 	speed += (float)ev->value * 0.05;
		if (speed > 1.5 || speed < -1.5 )
			speed += ev->value;
		set_transport_speed( speed );
    	}
    }
    break;
  case EV_KEY:
    if(ev->code != BTN_0)
      fprintf(stderr, "Warning: unexpected key event; ev->code = 0x%04x\n", ev->code);
    else
      if (ev->value)
		held = true;
      else {
		held = false;
		if (skippingMarkers) {
			skippingMarkers = false;
		} else {
			if (get_transport_speed() == 0.0) {
				set_transport_speed(1.0);
			} else {
				set_transport_speed(0.0);
			}
		}
	}
    break;
  }

  fflush(stdout);
}

void*
PowermateControlProtocol::SerialThread ()
{
  struct input_event ibuffer[BUFFER_SIZE];
  int r, events, i;

  while(1){
    r = read(mPort, ibuffer, sizeof(struct input_event) * BUFFER_SIZE);
    if( r > 0 ){
		events = r / sizeof(struct input_event);
      for(i=0; i<events; i++)
		ProcessEvent(&ibuffer[i]);
    }else{
      fprintf(stderr, "read() failed: %s\n", strerror(errno));
      return (void*) 0;
    }
  }

	return (void*) 0;
}


