/*
 * serialremote.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include <vdr/plugin.h>
#include <vdr/remote.h>
#include <vdr/thread.h>

#include <getopt.h>
#include <sys/time.h>

//#include <irman.h>

#define KEYPRESSDELAY 220 		 // timtout in ms between 2 keypresses
#define NOKEYPRESSES_REPEATSTART 3 // number of keypresses from where key-repeats will work

static const char *VERSION        = "0.0.1";
static const char *DESCRIPTION    = "A plugin for UIRT ir receiver";
static const char *MAINMENUENTRY  = NULL;//"Serialremote";

// for backward comp. ;)
int mytime_ms(void)
{
  static time_t t0 = 0;
  struct timeval t;
  if (gettimeofday(&t, NULL) == 0) {
     if (t0 == 0)
        t0 = t.tv_sec; // this avoids an overflow (we only work with deltas)
     return (t.tv_sec - t0) * 1000 + t.tv_usec / 1000;
     }
  return 0;
}

class cSerialRemote : public cRemote, private cThread
{
private:
  int fd;
  virtual void Action(void);
public:
  cSerialRemote(char* serport);
  ~cSerialRemote();
  //virtual bool Initialize(void);
};


cSerialRemote::cSerialRemote(char* serport)
	:cRemote("UIRT")
{
  struct termios options;
  char picRes[2];
  int result;

  // open serial device
  fd = open(serport, O_RDWR | O_NOCTTY | O_NDELAY);

  // check result
  if( fd == -1 )
  {
    // error - port could not be opened
    isyslog("serialremote-plugin: Port %s could not be opened",serport);
    return;
  }
  else
  {
      // ok - port opened
      fcntl(fd, F_SETFL, 0);
      // Get the current options for the port...
      tcgetattr(fd, &options);
      // Set the baud rates to 38400...
      cfsetispeed(&options, B9600);
      cfsetospeed(&options, B9600);
      // Enable the receiver and set local mode...
      options.c_cflag |= (CLOCAL);
      options.c_cflag &= ~PARENB;
      options.c_cflag &= ~CSTOPB;
      options.c_cflag &= ~CSIZE;
      options.c_cflag |= CS8;
      options.c_oflag &= ~OPOST;
      options.c_oflag &= ~(ICANON | ECHO | ECHOE | ISIG);
      options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
      options.c_cc[VMIN]  = 0;                  /* warten auf min. 0 Zeichen */
      options.c_cc[VTIME] = 1;                 /* Timeout in deciseconds */

      // Set the new options for the port...
      tcsetattr(fd, TCSANOW, &options);

      //fcntl(fd, F_SETFL, FNDELAY);

      // empty port (is necc. - but no clue, why)
      result = read(fd,picRes,1);
      while (result > 0)
      {
        result = read(fd,picRes,1);
      }

      write(fd,"I",1);
      usleep(2000);
      write(fd,"R",1);
      usleep(10000);

      result = read(fd,picRes,2);
      if ((picRes[0] == 'O') && (picRes[1] == 'K'))
        isyslog("serial-remote plugin: UIRT found");

      Start();
  }
}

cSerialRemote::~cSerialRemote()
{
	Cancel();
	close(fd);
}


void cSerialRemote::Action(void)
{
	int LastTime = 0;
	int repeatcounter = 0;
	int todo;
	int ircode[6];
	char message[32];
	char lastmessage[32];

	while (1)
	{
		todo = 1;
		while (todo)
		{
			if (read(fd,&ircode[0],1) != 1)
				todo = 0;
			else
			{
				for (int i=1; i< 6; i++)
				{
					if (read(fd,&ircode[i],1) != 1)
					{
						todo = 0;
						break;
					}
				}
			}

			if (todo == 1)
			{
				sprintf(message,"%02x%02x%02x%02x%02x%02x",ircode[0],ircode[1],ircode[2],ircode[3],ircode[4],ircode[5]);

				if (strcmp(lastmessage,message) == 0)
				{
					repeatcounter++;
					
					if (((mytime_ms() - LastTime) >= KEYPRESSDELAY))
					{
						repeatcounter = 0;
						Put(message);

					}
					if (repeatcounter > NOKEYPRESSES_REPEATSTART)
					{
						isyslog("put repeated message %d",repeatcounter);
						Put(message);
					}

					LastTime  = mytime_ms();
				}
				else
				{
					strcpy(lastmessage,message);
					LastTime  = mytime_ms();
					repeatcounter = 0;
					Put(message);
				}
			}
		}
		usleep(100000);
	}
}

class cPluginSerialremote : public cPlugin {
private:
  // Add any member variables or functions you may need here.
  cSerialRemote *mSerRem;
  char* mSerDev;
public:
  cPluginSerialremote(void);
  virtual ~cPluginSerialremote();
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return DESCRIPTION; }
  virtual const char *CommandLineHelp(void);
  virtual bool ProcessArgs(int argc, char *argv[]);
  virtual bool Initialize(void);
  virtual bool Start(void);
  virtual void Stop(void);
  virtual void Housekeeping(void);
  virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
  virtual cOsdObject *MainMenuAction(void);
  virtual cMenuSetupPage *SetupMenu(void);
  virtual bool SetupParse(const char *Name, const char *Value);
  };

cPluginSerialremote::cPluginSerialremote(void)
{
  // Initialize any member variables here.
  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
}

cPluginSerialremote::~cPluginSerialremote()
{
  // Clean up after yourself!
}

const char *cPluginSerialremote::CommandLineHelp(void)
{
  return "  -p dev,   --port=dev    serial port (/dev/ttySX)\n";
}

bool cPluginSerialremote::ProcessArgs(int argc, char *argv[])
{
  static struct option long_options[] = {
       { "port",      required_argument, NULL, 'p' },
       { NULL }
     };

  int c;
  while ((c = getopt_long(argc, argv, "p:", long_options, NULL)) != -1) {
        switch (c) {
          case 'p': mSerDev = optarg;
                    break;
          default:  return false;
          }
        }

  return true;
}

bool cPluginSerialremote::Initialize(void)
{
  // Initialize any background activities the plugin shall perform.
  return true;
}

bool cPluginSerialremote::Start(void)
{
  if (!mSerDev)
    mSerRem = new cSerialRemote("/dev/ttyS0");
  else
    mSerRem = new cSerialRemote(mSerDev);
  return true;
}

void cPluginSerialremote::Stop(void)
{
  // Stop any background activities the plugin shall perform.
}

void cPluginSerialremote::Housekeeping(void)
{
  // Perform any cleanup or other regular tasks.
}

cOsdObject *cPluginSerialremote::MainMenuAction(void)
{
  // Perform the action when selected from the main VDR menu.
  return NULL;
}

cMenuSetupPage *cPluginSerialremote::SetupMenu(void)
{
  // Return a setup menu in case the plugin supports one.
  return NULL;
}

bool cPluginSerialremote::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  return false;
}

VDRPLUGINCREATOR(cPluginSerialremote); // Don't touch this!
