#include <stdexcept>
#include <string.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>

int main(int argc, char** argv)
{
  int fd;
  if ((fd = open("/dev/input/uinput", O_RDWR | O_NDELAY)) < 0)
    {
      std::cout << "Error: /dev/input/uinput: " << strerror(errno) << std::endl;
    }
  else
    {  
      std::cout << "Create stuff " << std::endl;
      ioctl(fd, UI_SET_EVBIT, EV_KEY);
      ioctl(fd, UI_SET_KEYBIT, BTN_A);

      ioctl(fd, UI_SET_EVBIT, EV_FF);
      ioctl(fd, UI_SET_FFBIT, FF_PERIODIC);
      ioctl(fd, UI_SET_FFBIT, FF_RUMBLE);
      
      struct uinput_user_dev uinp;
      memset(&uinp,0,sizeof(uinp));
  
      strncpy(uinp.name, "ffdrv test", UINPUT_MAX_NAME_SIZE);

      uinp.ff_effects_max = 16;

      uinp.id.version = 0;
      uinp.id.bustype = BUS_USB;
      uinp.id.vendor  = 0x045e; // FIXME: this shouldn't be hardcoded
      uinp.id.product = 0x028e;
      std::cout << "Setting stuff" << std::endl;
      if (write(fd, &uinp, sizeof(uinp)) < 0)
        throw std::runtime_error(strerror(errno));
      std::cout << "Writing stuff" << std::endl;
      if (ioctl(fd, UI_DEV_CREATE))
        {
          throw std::runtime_error("Unable to create UINPUT device.");
        }
      std::cout << "Starting Loop" << std::endl;

      struct input_event ev;
      while(1)
        {
          std::cout << "Looping" << std::endl;
          int ret;
          if ((ret = read(fd, &ev, sizeof(ev))) == sizeof(ev))
            {
              std::cout << "type: " << ev.type << " code: " << ev.code << " value: " << ev.value << std::endl;

              switch(ev.type)
                {
                  case EV_FF:
                    std::cout << "EV_FF: playing effect: effect_id = " << ev.code << " value: " << ev.value << std::endl;
                    break;

                  case EV_UINPUT:
                    switch (ev.code)
                      {
                        case UI_FF_UPLOAD:
                          {
                            struct uinput_ff_upload upload;
                            memset(&upload, 0, sizeof(upload));

                            // *VERY* important, without this you
                            // break the kernel and have to reboot due
                            // to dead hanging process
                            upload.request_id = ev.value;

                            ioctl(fd, UI_BEGIN_FF_UPLOAD, &upload);

                            std::cout << "FF_UPLOAD: rumble upload: effect_id = " << upload.effect.id << std::endl;

                            upload.retval = 0;
                            
                            ioctl(fd, UI_END_FF_UPLOAD, &upload);
                          }
                          break;

                        case UI_FF_ERASE:
                          {
                            struct uinput_ff_erase erase;
                            memset(&erase, 0, sizeof(erase));

                            // *VERY* important, without this you
                            // break the kernel and have to reboot due
                            // to dead hanging process
                            erase.request_id = ev.value;

                            ioctl(fd, UI_BEGIN_FF_ERASE, &erase);

                            std::cout << "FF_ERASE: rumble erase: effect_id = " << erase.effect_id << std::endl;
                            erase.retval = 0; // FIXME: is this used?
                            
                            ioctl(fd, UI_END_FF_ERASE, &erase);
                          }
                          break;
                      }
                    break;
                }
              std::cout << "--------------------------------" << std::endl;
            }
          if (ret < 0 && errno != EAGAIN)
            std::cout << "Error: " << strerror(errno) << " " << ret << std::endl;
          usleep(100000);
        }
      ioctl(fd, UI_DEV_DESTROY);
      close(fd);
    }
}

/* EOF */

