#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/select.h>

#include "inotify.h"

#define ALL_MASK 0xffffffff

int open_dev ()
{
	int fd;

	fd = open("/dev/inotify", O_RDONLY);

	if (fd < 0) {
		perror ("open(\"/dev/inotify\", O_RDONLY) = ");
	}

	return fd;
}

int close_dev (int fd)
{
	int r;

	if ( (r = close (fd)) < 0) {
		perror ("close (fd) = ");
	}

	return r;
}


void print_mask(int mask)
{
	printf("EVENT MASK = ");
	if (mask & IN_ACCESS)
	{
		printf("ACCESS ");
	}
	if (mask & IN_MODIFY)
	{
		printf("MODIFY ");
	}
	if (mask & IN_ATTRIB)
	{
		printf("ATTRIB ");
	}
	if (mask & IN_CLOSE_WRITE)
	{
		printf("CLOSE_WRITE ");
	}
	if (mask & IN_CLOSE_NOWRITE)
	{
		printf("CLOSE_NOWRITE ");
	}
	if (mask & IN_OPEN)
	{
		printf("OPEN ");
	}
	if (mask & IN_MOVED_FROM)
	{
		printf("MOVE_FROM ");
	}
	if (mask & IN_MOVED_TO)
	{
		printf("MOVE_TO ");
	}
	if (mask & IN_DELETE_SUBDIR)
	{
		printf("DELETE_SUBDIR ");
	}
	if (mask & IN_DELETE_FILE)
	{
		printf("DELETE_FILE ");
	}
	if (mask & IN_CREATE_SUBDIR)
	{
		printf("CREATE_SUBDIR ");
	}
	if (mask & IN_CREATE_FILE)
	{
		printf("CREATE_FILE ");
	}
	if (mask & IN_DELETE_SELF)
	{
		printf("DELETE_SELF ");
	}
	if (mask & IN_UNMOUNT)
	{
		printf("UNMOUNT ");
	}
	if (mask & IN_Q_OVERFLOW)
	{
		printf("Q_OVERFLOW ");
	}
	if (mask & IN_IGNORED)
	{
		printf("IGNORED" );
	}

	printf("\n");
}

void print_event (struct inotify_event event)
{
	printf ("EVENT ON WD=%d\n", event.wd);
	print_mask (event.mask);
	printf ("FILENAME=%s\n", event.filename);
}

int read_event (int fd, struct inotify_event *event)
{
	event->wd = 0xdeadbeef;
	event->mask = 0xdeadbeef;
	return read (fd, event, sizeof(struct inotify_event));
}

int event_check (int fd)
{
	struct timeval timeout;
	int r;
	fd_set rfds;

	timeout.tv_sec = 0;
	timeout.tv_usec = 10000;

	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);

	r = select (fd+1, &rfds, NULL, NULL, &timeout);

	return r;
}

int read_and_print_events (int fd)
{
	struct inotify_event event;

	while (1)
	{
		if (event_check(fd) > 0) {
			int r;
			r = read_event(fd, &event);

			if (r > 0) {
				print_event (event);
			} else {
				break;
			}
		}
	}

	return 0;
}

int watch_dir (int fd, const char *dirname, unsigned long mask)
{
	struct inotify_watch_request iwr;
	iwr.dirname = strdup (dirname);
	iwr.mask = mask;
	int wd;

	wd = ioctl(fd, INOTIFY_WATCH, &iwr);

	if (wd < 0) {
		printf("ioctl(fd, INOTIFY_WATCH, {%s, %u}) =", iwr.dirname, iwr.mask);
		fflush(stdout);
		perror (" ");
	}

	free (iwr.dirname);

	printf("%s WD=%d\n", dirname, wd);
	return wd;
}

int ignore_wd (int fd, int wd)
{
	int r;

	r = ioctl(fd, INOTIFY_IGNORE, &wd);

	if (r < 0) {
		perror("ioctl(fd, INOTIFY_IGNORE, &wid) = ");
	}

	return r;
}


int dev_stats (int fd)
{
	int r = ioctl(fd, INOTIFY_STATS, 0);

	if (r < 0) {
		perror("ioctl(fd, INOTIFY_STATS, 0) = ");
	}

	return r;
}

int dev_setdebug (int fd, int mask)
{
	int a = mask;

	int r = ioctl(fd, INOTIFY_SETDEBUG, &a);

	if (r < 0) {
		perror("ioctl(fd, INOTIFY_SETDEBUG, &mask) = ");
	}

	return r;
}

