
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/input.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>

#include "lircd.h"

int sockfd,lastvalue,lastcount;
char *lircdfile = "/dev/lircd";
char *progname = "lirc.c";
mode_t permission=S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
int clients[16] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
int num_clients = 0;

inline int max(int a,int b)
{
   return(a>b ? a:b);
}

void nolinger(int sock)
{
   static struct linger  linger = {0, 0};
   int lsize  = sizeof(struct linger);
   setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, lsize);
}

int setupSocket()
{
   int ret;
   int new = 0;
   struct sockaddr_un serv_addr;
   struct stat s;

   /* create socket */
   sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
   if(sockfd == -1) {
      fprintf(stderr, "%s: could not create socket\n", progname);
      perror(progname);
      goto start_server_failed0;
   }

   /*
    *         get owner, permissions, etc.
    *         so new socket can be the same since we
    *         have to delete the old socket.
    **/
   ret = stat(lircdfile, &s);
   if(ret == -1 && errno != ENOENT) {
      fprintf(stderr,
              "%s: could not get file information for %s\n",
              progname, lircdfile);
      perror(progname);
      goto start_server_failed1;
   }
   if(ret != -1) {
      ret = unlink(lircdfile);
      if(ret == -1) {
         fprintf(stderr, "%s: could not delete %s\n",
                 progname, lircdfile);
         perror(NULL);
         goto start_server_failed1;
      }
   }

   serv_addr.sun_family = AF_UNIX;
   strcpy(serv_addr.sun_path, lircdfile);
   if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
      == -1) {
      fprintf(stderr, "%s: could not assign address to socket\n",
              progname);
      perror(progname);
      goto start_server_failed1;
   }

   if(new ?
      chmod(lircdfile, permission) :
      (chmod(lircdfile, s.st_mode) == -1 ||
       chown(lircdfile, s.st_uid, s.st_gid) == -1)
     ) {
      fprintf(stderr, "%s: could not set file permissions\n",
              progname);
      perror(progname);
      goto start_server_failed1;
   }

   listen(sockfd, 3);
   nolinger(sockfd);

   return 0;

   start_server_failed0:
   start_server_failed1:
   return -1;
}

int main()
{
   int fd, rfd;
   int clilen;
   struct sockaddr client_addr;
   //char name[256] = "Unknown";
   char *device = "/dev/input/event0";

   if((rfd = open (device, O_RDONLY)) == -1) {
      printf("Can't open input event device.\n");
      return -1;
   }
/*
   ioctl (rfd, EVIOCGNAME (sizeof (name)), name);
   printf ("Reading From : %s (%s)\n", device, name);
*/
   if(setupSocket() == -1) {
      printf("Can't init socket.\n");
      return -1;
   }

   signal(SIGPIPE, SIG_IGN);

   clilen=sizeof(client_addr);

   while(1) {
      /*Wait until someone connects*/
      fd=accept(sockfd,(struct sockaddr *)&client_addr,&clilen);
      if(fd==-1) {
         // Unfortunately this is deadly right now.
         return -1;
      }
      else {
         clients[0] = fd;
         num_clients++;

         nolinger(fd);
      }

      /*This is the real duty cycle loop*/

      while(num_clients) {
         int i=0;
         int n,retval;
         fd_set fds;
         struct timeval tv;

         tv.tv_sec = 30;
         tv.tv_usec = 0;

         FD_ZERO(&fds);

         FD_SET(rfd, &fds);
         n=rfd;

         if(num_clients < 16) {
            FD_SET(sockfd, &fds);
            n = max(sockfd,n);
         }

         retval = select(n+1, &fds, NULL, NULL, &tv);
         if(retval == -1) {
            perror("select()");
         }
         else if(retval) {
            if( FD_ISSET(sockfd, &fds)) {
               int nfd;
               nfd=accept(sockfd,(struct sockaddr *)&client_addr,&clilen);
               if(nfd==-1) {
                  //hello ??
               }
               else {
                  int a=0;
                  for(a=0 ; a<16 ; a++)
                     if( clients[a] == -1 ) {
                        clients[a]=nfd;
                        break;
                     }
                  num_clients++;
               }
            }
            if(FD_ISSET(rfd, &fds)) {
               char *message;
               message = getKey(rfd);

               if(message) {
                  for(i=0 ; i<16 ; i++) {
                     if( clients[i] != -1 ) {
                        int done,todo,len;
                        char *buf;

                        len=strlen(message);
                        buf=message;
                        todo=len;

                        while(todo) {
                           done=write(clients[i],buf,todo);
                           if(done<=0) {
                              clients[i]=-1;
                              num_clients--;
                              break;
                           }
                           buf+=done;
                           todo-=done;
                        }
                     }
                  }
               }
            }
         }
         else {
            /* Timeout: ignore*/
         }
      }
   }
   unlink(lircdfile);
   return 0;
}
