##############################################################################
#
# Copyright (c) 2011 Nexedi SARL and Contributors. All Rights Reserved.
#                    Julien Muchembled <jm@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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.
#
##############################################################################

import errno, glob, os, threading
from AccessControl import ClassSecurityInfo
from Acquisition import aq_base
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type.TransactionalVariable import TransactionalResource
from Products.ERP5Type import Permissions
from Products.ERP5.mixin.timer_service import TimerServiceMixin

# TODO: Current API was designed to avoid compability issues in case it is
#       reimplemented using http://pypi.python.org/pypi/inotifyx

IN_CREATE = 1
IN_MODIFY = 2
IN_DELETE = 512

timerservice_lock = threading.Lock()
inotify_state_dict = {}

class InotifyTool(TimerServiceMixin, BaseTool):

  id = 'portal_inotify'
  meta_type = 'ERP5 Inotify Tool'
  portal_type = 'Inotify Tool'

  def resetCache(self):
    self._p_changed = 1
    try:
      del self._v_notify_list
    except AttributeError:
      pass

  def process_timer(self, tick, interval, prev="", next=""):
    if timerservice_lock.acquire(0):
      try:
        try:
          notify_list = aq_base(self)._v_notify_list
        except AttributeError:
          current_node = self.getCurrentNode()
          self._v_notify_list = notify_list = [x.getId()
            for x in self.objectValues()
            if x.isEnabled() and current_node in x.getNodeList()]
        update_state_dict = {}
        for notify_id in notify_list:
          notify = self._getOb(notify_id)
          inode_path = notify.getInodePath()
          if inode_path:
            path = notify.getPath()
            state = inotify_state_dict.get(path, {})
            new_state = {}
            for inode_path in glob.glob(inode_path):
              for name in os.listdir(inode_path):
                p = os.path.join(inode_path, name)
                try:
                  s = os.lstat(p)
                except OSError, e:
                  if e.errno != errno.ENOENT:
                    raise
                else:
                  new_state[p] = s.st_mtime, s.st_size
            if new_state != state:
              update_state_dict[path] = new_state
              events = [{'path': p, 'mask': IN_DELETE}
                for p in set(state).difference(new_state)]
              for p, m in new_state.iteritems():
                if p in state:
                  if m == state[p]:
                    continue
                  mask = IN_MODIFY
                else:
                  mask = IN_CREATE
                events.append({'path': p, 'mask': mask})
              getattr(notify, notify.getSenseMethodId())(events)
        if update_state_dict:
          TransactionalResource(tpc_finish=lambda txn:
            inotify_state_dict.update(update_state_dict))
      finally:
        timerservice_lock.release()