//  BMP
//  Copyright (C) 2005-2007 BMP development.
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
//  --
//
//  The BMPx project hereby grants permission for non GPL-compatible GStreamer
//  plugins to be used and distributed together with GStreamer and BMPx. This
//  permission is above and beyond the permissions granted by the GPL license
//  BMPx is covered by.

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif //HAVE_CONFIG_H

#include <glibmm.h>
#include "glib-inotify.hh"

namespace Bmp
{
  INotify::~INotify ()
  {
    m_has_eof_lock.lock();
    m_eof = true;
    m_has_eof_lock.unlock();
  
    m_has_eof_lock.lock ();
    while (!m_has_eof)
        m_has_eof_cond.wait (m_has_eof_lock);
    m_has_eof_lock.unlock ();

    m_thread->join ();

    close (m_fd);
  }

  INotify::INotify (std::string const& file)
  : m_eof (false)
  , m_has_eof (false)
  {
    m_fd = inotify_init ();
    if (m_fd < 0)
      throw UnableToInitInotify();

    int id = inotify_add_watch (m_fd, file.c_str(), IN_CREATE | IN_MOVED_TO | IN_DELETE); 
    if( id < 0 )
    {
      close( m_fd );
      throw UnableToInitInotify();
    }

    m_thread = Glib::Thread::create( sigc::bind( sigc::mem_fun( *this, &INotify::poll_fd ), m_fd ), true);
  }

  void
  INotify::poll_fd (int x_fd)
  {
    while (1)
    {
      struct timeval tv;
      int r;
      fd_set rfds;
      FD_ZERO(&rfds);
      FD_SET(x_fd, &rfds);
    
      tv.tv_sec = 0;
      tv.tv_usec = 500000;

      r = select (x_fd+1, &rfds, NULL, NULL, &tv);

      m_has_eof_lock.lock();
      bool eof = m_eof;
      m_has_eof_lock.unlock();
      
      if (eof)
      {
        m_has_eof_lock.lock();
        m_has_eof = true;
        m_has_eof_cond.signal();
        m_has_eof_lock.unlock();
        return;
      }

      if (r > 0)  
      {
        char buffer[16384];
        ssize_t buffer_i, r, event_size;
        struct inotify_event *pevent, *event;
        int count = 0;
        
        m_queue_lock.lock();
        r = read( x_fd, buffer, 16384 );
        if (r > 0) 
        {
          buffer_i = 0;
          while (buffer_i < r)
          {
            pevent = (struct inotify_event *)&buffer[buffer_i];
            event_size = sizeof(struct inotify_event) + pevent->len;

            INotifyEvent ev;
            ev.wd = pevent->wd;
            ev.mask = pevent->mask;
            ev.cookie = pevent->cookie;
            ev.len = pevent->len;
            ev.name = (pevent->name ? pevent->name : "");

            m_events.push_front (ev);
            buffer_i += event_size;
          } 
        }
        m_queue_lock.unlock();
        signal_new_events_.emit();
      }
    }
  }
}
