# Version 0.2.3 (2005-01-18)
#   function ignore_dir : was added
#
# Version 0.2.2 (2005-01-18)
#   cleanning code (big thanks to gnome at #ruby-lang)
#   rename next_event in each_event (thanks kig)
#
# Version 0.2.1 (2005-01-18)
#   class Events : use real mask
#
# Version 0.2.0 (2005-01-18)
#   function watch_dir : only watch
#   function next_event : was added
#   function watch_dir_recursively : was added
#
# Version 0.1.1 (2005-01-17)
# Correct IN_ var for inotify 0.18

module INotify
	require 'find'

	INOTIFY_WATCH    = 0x80085101
	INOTIFY_IGNORE   = 0x80045102

	Mask             = Struct::new(:value, :name)

	Masks = {
		:IN_ACCESS        => Mask::new(0x00000001, 'access'),
		:IN_MODIFY        => Mask::new(0x00000002, 'modify'),
		:IN_ATTRIB        => Mask::new(0x00000004, 'attrib'),
		:IN_CLOSE_WRITE   => Mask::new(0x00000008, 'close_write'),
		:IN_CLOSE_NOWRITE => Mask::new(0x00000010, 'close_nowrite'),
		:IN_OPEN          => Mask::new(0x00000020, 'open'),
		:IN_MOVED_FROM    => Mask::new(0x00000040, 'moved_from'),
		:IN_MOVED_TO      => Mask::new(0x00000080, 'moved_to'),
		:IN_DELETE_SUBDIR => Mask::new(0x00000100, 'delete_subdir'),
		:IN_DELETE_FILE   => Mask::new(0x00000200, 'delete_file'),
		:IN_CREATE_SUBDIR => Mask::new(0x00000400, 'create_subdir'),
		:IN_CREATE_FILE   => Mask::new(0x00000800, 'create_file'),
		:IN_DELETE_SELF   => Mask::new(0x00001000, 'delete_self'),
		:IN_UNMOUNT       => Mask::new(0x00002000, 'unmount'),
		:IN_Q_OVERFLOW    => Mask::new(0x00004000, 'q_overflow'),
		:IN_IGNORED       => Mask::new(0x00008000, 'ignored'),
		:IN_ALL_EVENTS    => Mask::new(0xffffffff, 'all_events')
	}

	Masks.each {|key, value|
		const_set(key, value)
	}
	
	class INotify
		def initialize
			@watch_dir = Array.new
			@io = File.open('/dev/inotify')
		end	

		def close
			@io.close
		end

		def watch_dir (dir, option)
			arg = [dir, option.value]
			arg = arg.pack("PL")
			wd = @io.ioctl(INOTIFY_WATCH, arg)
			if (wd >= 0)
				@watch_dir[wd] = dir
			else
				return -1
			end
		end

		def ignore_dir (dir)
			@io.ioctk(INOTIFY_IGNORE, dir)
		end
	
		def watch_dir_recursively (dir, option)
			Find.find(dir) {|sub_dir|
				watch_dir(sub_dir, option) if (File::directory?(sub_dir) == true)
			}
		end
	
		def each_event
			loop {
				begin
					read_cnt = @io.sysread(268)
					wd, event, cookie, filename = read_cnt.unpack('lLLZ*')
				end while ((event & IN_Q_OVERFLOW.value) != 0 && sleep(0.05))
				evts = Events.new(path(wd), event, cookie, filename)
				evts.each {|event| 
					yield event
				}
			}
		end
	
		def path (wd)
			@watch_dir[wd]
		end
	
	end
	
	class Events
		def initialize (path, event, cookie, filename)
			@event_list = Array.new
			
			Masks.each_value {|mask|
				next if mask.value == IN_ALL_EVENTS.value
				if ((mask.value & event) != 0)
					@event_list.push Event.new(path, mask.name, cookie, filename)
				end
			}
		end

		def each
			@event_list.each {|event| yield event}
		end
	end

	class Event
		attr_reader :filename, :event, :path

		def initialize (path, event, cookie, filename)
			@path        = path
			@event       = event
			@cookie      = cookie
			@filename    = filename
		end

		def dump
			"path: "+@path.to_s+" event: "+event.to_s+" cookie: "+@cookie.to_s+" filename: "+@filename.to_s
		end
	end
end
