/////////////////////////////////////////////////////////////////////////////
// Name:        MyFSWatcher.cpp
// Purpose:     Backport for wx2.8 of various wxWidgets filesystemwatcher 
//              classes for 4Pane. Note that these are no longer cross-platform
// Modified by: David Hart 2012
// Copyright:   wxWidgets developers
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#include "wx/platform.h"
#include "MyFSWatcher.h"
#if USE_MYFSWATCHER && defined(__LINUX__) && defined(__WXGTK__)


#include "wx/evtloop.h"
#include "wx/treectrl.h"
#include <sys/inotify.h>
#include <gtk/gtk.h>
#include "../../Filetypes.h"

// inotify watch descriptor => mywxFSWatchEntry* map
WX_DECLARE_HASH_MAP(int, mywxFSWatchEntry*, wxIntegerHash, wxIntegerEqual,
                                              mywxFSWatchEntryDescriptors);

// inotify event cookie => inotify_event* map
WX_DECLARE_HASH_MAP(int, inotify_event*, wxIntegerHash, wxIntegerEqual,
                                                      wxInotifyCookies);

DEFINE_EVENT_TYPE(wxEVT_FSWATCHER);

static wxString GetFSWEventChangeTypeName(int type)
{
    switch (type)
    {
    case wxFSW_EVENT_CREATE:
        return wxT("CREATE");
    case wxFSW_EVENT_DELETE:
        return wxT("DELETE");
    case wxFSW_EVENT_RENAME:
        return wxT("RENAME");
    case wxFSW_EVENT_MODIFY:
        return wxT("MODIFY");
    case wxFSW_EVENT_ACCESS:
        return wxT("ACCESS");
    case wxFSW_EVENT_ATTRIB:
        return wxT("ATTRIBUTE");
    case wxFSW_EVENT_UNMOUNT:
        return wxT("UNMOUNT");
    case wxFSW_EVENT_WARNING:
        return wxT("WARNING");
    case wxFSW_EVENT_ERROR:
        return wxT("ERROR");
    }

    // should never be reached!
    wxFAIL_MSG(wxT("Unknown change type"));
    return wxT("INVALID_TYPE");
}

wxString wxFileSystemWatcherEvent::ToString() const
{
    return wxString::Format(wxT("FSW_EVT type=%d (%s) path='%s'"), m_changeType,
            GetFSWEventChangeTypeName(m_changeType).c_str(), GetPath().GetFullPath().c_str());
}

extern "C"
{
static gboolean mywx_on_channel_event(GIOChannel *channel,
                                    GIOCondition condition,
                                    gpointer data)
{
    wxUnusedVar(channel); // Unused if !wxUSE_LOG || !wxDEBUG_LEVEL

    mywxFSWSourceHandler* const handler = static_cast<mywxFSWSourceHandler*>(data);

    if (condition & G_IO_IN || condition & G_IO_PRI)
        handler->OnReadWaiting();
    if (condition & G_IO_OUT)
        handler->OnWriteWaiting();
    else if (condition & G_IO_ERR || condition & G_IO_NVAL)
        handler->OnExceptionWaiting();

    // we never want to remove source here, so always return true
    return TRUE;
}
}

class mywxFSWatcherImplUnix : public mywxFSWatcherImpl
{
public:
    mywxFSWatcherImplUnix(MyFileSystemWatcher* watcher) :
        mywxFSWatcherImpl(watcher),
       // m_source(NULL),
        m_ifd(-1)
    {
      m_sourceId = 0;  
      m_handler = new mywxFSWSourceHandler(this);
    }

    ~mywxFSWatcherImplUnix()
    {
        // we close inotify only if initialized before
        if (IsOk())
        {
            g_source_remove(m_sourceId);

            Close();
        }
    }

    bool Init()
    {
        wxCHECK_MSG( !IsOk(), false, wxT("Inotify already initialized") );

/*        wxEventLoop *loop = wxEventLoop::GetActive();
        wxCHECK_MSG( loop, false, wxT("File system watcher needs an event loop") );*/

        m_ifd = inotify_init();
        if ( m_ifd == -1 )
        {
            wxLogSysError( wxT("Unable to create inotify instance") );
            return false;
        }
// Abstracted from wxGUIEventLoop::AddSourceForFD
    int condition = G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL; // We don't really need the last 3; but why not...
    GIOChannel* channel = g_io_channel_unix_new(m_ifd);
    m_sourceId  = g_io_add_watch(channel, (GIOCondition)condition, &mywx_on_channel_event, m_handler);
    // it was ref'd by g_io_add_watch() so we can unref it here
    g_io_channel_unref(channel);
    
    //if ( !m_sourceId )  return false;
    return m_sourceId != 0;

   // m_source = new mywxEventLoopSource(m_handler, wxEVENT_SOURCE_INPUT | wxEVENT_SOURCE_EXCEPTION);

   //     return m_source != NULL;
    }

    void Close()
    {
        wxCHECK_RET( IsOk(),
                    wxT("Inotify not initialized or invalid inotify descriptor") );

        //wxDELETE(m_source);

        if ( close(m_ifd) != 0 )
        {
            wxLogSysError( wxT("Unable to close inotify instance") );
        }
    }

    virtual bool DoAdd(wxSharedPtr<mywxFSWatchEntry> watch)
    {
        wxCHECK_MSG( IsOk(), false,
                    wxT("Inotify not initialized or invalid inotify descriptor") );

        int wd = DoAddInotify(watch.get());
        if (wd == -1)
        {
            wxLogSysError( wxT("Unable to add inotify watch") );
            return false;
        }

        mywxFSWatchEntryDescriptors::value_type val(wd, watch.get());
        if (!m_watchMap.insert(val).second)
        {
            wxFAIL_MSG( wxString::Format( wxT("Path %s is already watched"),
                                           watch->GetPath().c_str()) );
            return false;
        }

        return true;
    }

    virtual bool DoRemove(wxSharedPtr<mywxFSWatchEntry> watch)
    {
        wxCHECK_MSG( IsOk(), false,
                    wxT("Inotify not initialized or invalid inotify descriptor") );

        int ret = DoRemoveInotify(watch.get());
        if (ret == -1)
        {
            //wxLogSysError( wxT("Unable to remove inotify watch") );
            //return false;
        }

        if (m_watchMap.erase(watch->GetWatchDescriptor()) != 1)
        {
            wxFAIL_MSG( wxString::Format(wxT("Path %s is not watched"),
                                          watch->GetPath().c_str()) );
        }
        // Cache the wd in case any events arrive late
        m_staleDescriptors.Add(watch->GetWatchDescriptor());

        watch->SetWatchDescriptor(-1);
        return true;
    }

    virtual bool RemoveAll()
    {
        wxFSWatchEntries::iterator it = m_watches.begin();
        for ( ; it != m_watches.end(); ++it )
        {
            (void) DoRemove(it->second);
        }
        m_watches.clear();
        return true;
    }

    int ReadEvents()
    {
        wxCHECK_MSG( IsOk(), -1,
                    wxT("Inotify not initialized or invalid inotify descriptor") );

        // read events
        // TODO differentiate depending on params
        char buf[128 * sizeof(inotify_event)];
        int left = ReadEventsToBuf(buf, sizeof(buf));
        if (left == -1)
            return -1;

        // left > 0, we have events
        char* memory = buf;
        int event_count = 0;
        while (left > 0) // OPT checking 'memory' would suffice
        {
            event_count++;
            inotify_event* e = (inotify_event*)memory;

            // process one inotify_event
            ProcessNativeEvent(*e);

            int offset = sizeof(inotify_event) + e->len;
            left -= offset;
            memory += offset;
        }

        // take care of unmatched renames
        ProcessRenames();

        wxLogTrace(wxTRACE_FSWATCHER, wxT("We had %d native events"), event_count);
        return event_count;
    }

    bool IsOk() const
    {
        //return m_source != NULL;
        return m_sourceId != 0;
    }

protected:
    int DoAddInotify(mywxFSWatchEntry* watch)
    {
        int flags = Watcher2NativeFlags(watch->GetFlags());
        int wd = inotify_add_watch(m_ifd, watch->GetPath().fn_str(), flags);
        // finally we can set watch descriptor
        watch->SetWatchDescriptor(wd);
        return wd;
    }

    int DoRemoveInotify(mywxFSWatchEntry* watch)
    {
        return inotify_rm_watch(m_ifd, watch->GetWatchDescriptor());
    }

    void ProcessNativeEvent(const inotify_event& inevt)
    {
        wxLogTrace(wxTRACE_FSWATCHER, InotifyEventToString(inevt));

        // after removing inotify watch we get IN_IGNORED for it, but the watch
        // will be already removed from our list at that time
        if (inevt.mask & IN_IGNORED)
        {
            // It is now safe to remove it from the stale descriptors too, we
            // won't get any more events for it.
            // However if we're here because a dir that we're still watching
            // has just been deleted, its wd won't be on this list
            const int pos = m_staleDescriptors.Index(inevt.wd);
            if ( pos != wxNOT_FOUND )
            {
                m_staleDescriptors.RemoveAt(static_cast<size_t>(pos));
                wxLogTrace(wxTRACE_MYFSWATCHER_STALEDESCRIPTOR,
                       wxT("Removed wd %i from the stale-wd cache"), inevt.wd);
            }
            return;
        }

        // get watch entry for this event
        mywxFSWatchEntryDescriptors::iterator it = m_watchMap.find(inevt.wd);

        // wd will be -1 for IN_Q_OVERFLOW, which would trigger the wxFAIL_MSG
        if (inevt.wd != -1)
        {
            if (it == m_watchMap.end())
            {
                // It's not in the map; check if was recently removed from it.
                if (m_staleDescriptors.Index(inevt.wd) != wxNOT_FOUND)
                {
                    wxLogTrace(wxTRACE_MYFSWATCHER_STALEDESCRIPTOR,
                               wxT("Got an event for stale wd %s"), InotifyEventToString(inevt).c_str());
                }
                else
                {
                    wxFAIL_MSG(wxT("Event for unknown watch descriptor."));
                }

                // In any case, don't process this event: it's either for an
                // already removed entry, or for a completely unknown one.
                return;
            }
        }

        int nativeFlags = inevt.mask;
        int flags = Native2WatcherFlags(nativeFlags);

        // check out for error/warning condition
        if (flags & wxFSW_EVENT_WARNING || flags & wxFSW_EVENT_ERROR)
        {
            wxString errMsg = GetErrorDescription(nativeFlags);
            wxFileSystemWatcherEvent event(flags, errMsg);
            SendEvent(event);
            return;
        }

        if (inevt.wd == -1)
        {
            // We should have just dealt with IN_Q_OVERFLOW
            // Maybe there are other warnings/errors where wd is -1, but I've
            // had occasional -1 IN_ACCESS and IN_CREATEs events too
            wxLogWarning(
                wxT("Inotify event with an invalid watch descriptor value\n%s"),
                InotifyEventToString(inevt).c_str()
                        );
            return;
        }

        mywxFSWatchEntry& watch = *(it->second);

        // Now IN_UNMOUNT. We must do so here, as it's not in the watch flags
        if (nativeFlags & IN_UNMOUNT)
        {
            wxFileName path = GetEventPath(watch, inevt);
            wxFileSystemWatcherEvent event(wxFSW_EVENT_UNMOUNT, path, path);
            SendEvent(event);
        }

        // filter out ignored events and those not asked for.
        // we never filter out warnings or exceptions
        else if ((flags == 0) || !(flags & watch.GetFlags()))
        {
            return;
        }

        // Creation
        // We need do something here only if the original watch was recursive;
        // we don't watch a child dir itself inside a non-tree watch.
        // We watch only dirs explicitly, so we don't want file IN_CREATEs.
        // Distinguish by whether nativeFlags contain IN_ISDIR
        else if ((nativeFlags & IN_CREATE) &&
                 (watch.GetType() == wxFSWPath_Tree) && (inevt.mask & IN_ISDIR))
        {
            wxFileName fn = GetEventPath(watch, inevt);
            // Though it's a dir, fn treats it as a file. So:
            fn.AssignDir(fn.GetFullPath());

            if (m_watcher->AddAny(fn, wxFSW_EVENT_ALL,
                                   wxFSWPath_Tree, watch.GetFilespec()))
            {
                // Tell the owner, in case it's interested
                // If there's a filespec, assume he's not
                if (watch.GetFilespec().empty())
                {
                    wxFileSystemWatcherEvent event(flags, fn, fn);
                    SendEvent(event);
                }
            }
        }

        // Deletion
        // We watch only dirs explicitly, so we don't want file IN_DELETEs.
        // We obviously can't check using DirExists() as the object has been
        // deleted; and nativeFlags here doesn't contain IN_ISDIR, even for
        // a dir. Fortunately IN_DELETE_SELF doesn't happen for files. We need
        // to do something here only inside a tree watch, or if it's the parent
        // dir that's deleted. Otherwise let the parent dir cope
        else if ((nativeFlags & IN_DELETE_SELF) &&
                    ((watch.GetType() == wxFSWPath_Dir) ||
                     (watch.GetType() == wxFSWPath_Tree)))
        {
            // We must remove the deleted directory from the map, so that
            // DoRemoveInotify() isn't called on it in the future. Don't assert
            // if the wd isn't found: repeated IN_DELETE_SELFs can occur
            wxFileName fn = GetEventPath(watch, inevt);
            wxString path(fn.GetPathWithSep());
            wxString filespec(watch.GetFilespec());

            if (m_watchMap.erase(inevt.wd) == 1)
            {
                // Delete from wxFileSystemWatcher
                m_watcher->OnDirDeleted(path);

                // Now remove from our local list of watched items
                wxFSWatchEntries::iterator wit =
                                        m_watches.find(path);
                if (wit != m_watches.end())
                {
                    m_watches.erase(wit);
                }

                // Cache the wd in case any events arrive late
                m_staleDescriptors.Add(inevt.wd);
            }

            // Tell the owner, in case it's interested
            // If there's a filespec, assume he's not
            if (filespec.empty())
            {
                wxFileSystemWatcherEvent event(flags, fn, fn);
                SendEvent(event);
            }
        }

        // renames
        else if (nativeFlags & IN_MOVE)
        {
            wxInotifyCookies::iterator it2 = m_cookies.find(inevt.cookie);
            if ( it2 == m_cookies.end() )
            {
                int size = sizeof(inevt) + inevt.len;
                inotify_event* e = (inotify_event*) operator new (size);
                memcpy(e, &inevt, size);

                wxInotifyCookies::value_type val(e->cookie, e);
                m_cookies.insert(val);
            }
            else
            {
                inotify_event& oldinevt = *(it2->second);

                // Tell the owner, in case it's interested
                // If there's a filespec, assume he's not
                if ( watch.GetFilespec().empty() )
                {
                    mywxFSWatchEntry* oldwatch;
                    mywxFSWatchEntryDescriptors::iterator oldwatch_it = 
                                                m_watchMap.find(oldinevt.wd);
                    if (oldwatch_it != m_watchMap.end())
                    {
                        oldwatch = oldwatch_it->second;
                    }
                    else
                    {
                        wxLogTrace(wxTRACE_MYFSWATCHER_STALEDESCRIPTOR,
                            wxT("oldinevt's watch descriptor not in the watch map"));
                        // For want of a better alternative, use 'watch'. That
                        // will work fine for renames, though not for moves
                        oldwatch = &watch;
                    }
                    wxFileSystemWatcherEvent event(flags);
                    if ( inevt.mask & IN_MOVED_FROM )
                    {
                        event.SetPath(GetEventPath(watch, inevt));
                        event.SetNewPath(GetEventPath(*oldwatch, oldinevt));
                    }
                    else
                    {
                        event.SetPath(GetEventPath(*oldwatch, oldinevt));
                        event.SetNewPath(GetEventPath(watch, inevt));
                    }
                    SendEvent(event);
                }

                m_cookies.erase(it2);
                delete &oldinevt;
            }
        }
        // every other kind of event
        else
        {
            wxFileName path = GetEventPath(watch, inevt);
            // For files, check that it matches any filespec
            if ( MatchesFilespec(path, watch.GetFilespec()) )
            {
                wxFileSystemWatcherEvent event(flags, path, path);
                SendEvent(event);
            }
        }
    }

    void ProcessRenames()
    {
        wxInotifyCookies::iterator it = m_cookies.begin();
        while ( it != m_cookies.end() )
        {
            inotify_event& inevt = *(it->second);

            wxLogTrace(wxTRACE_FSWATCHER, wxT("Processing pending rename events"));
            wxLogTrace(wxTRACE_FSWATCHER, InotifyEventToString(inevt));

            // get watch entry for this event
            mywxFSWatchEntryDescriptors::iterator wit = m_watchMap.find(inevt.wd);
            wxCHECK_RET(wit != m_watchMap.end(),
                             wxT("Watch descriptor not present in the watch map!"));

            // Tell the owner, in case it's interested
            // If there's a filespec, assume he's not
            mywxFSWatchEntry& watch = *(wit->second);
            if ( watch.GetFilespec().empty() )
            {
                int flags = Native2WatcherFlags(inevt.mask);
                wxFileName path = GetEventPath(watch, inevt);
                {
                    wxFileSystemWatcherEvent event(flags, path, path);
                    SendEvent(event);
                }
            }

            m_cookies.erase(it);
            delete &inevt;
            it = m_cookies.begin();
        }
    }

    void SendEvent(wxFileSystemWatcherEvent& evt)
    {
        wxLogTrace(wxTRACE_FSWATCHER, evt.ToString());
        m_watcher->GetOwner()->ProcessEvent(evt);
    }

    int ReadEventsToBuf(char* buf, int size)
    {
        wxCHECK_MSG( IsOk(), false,
                    wxT("Inotify not initialized or invalid inotify descriptor") );

        memset(buf, 0, size);
        ssize_t left = read(m_ifd, buf, size);
        if (left == -1)
        {
            wxLogSysError(wxT("Unable to read from inotify descriptor"));
            return -1;
        }
        else if (left == 0)
        {
            wxLogWarning(wxT("EOF while reading from inotify descriptor"));
            return -1;
        }

        return left;
    }

    static wxString InotifyEventToString(const inotify_event& inevt)
    {
        wxString mask = (inevt.mask & IN_ISDIR) ?
                        wxString::Format(wxT("IS_DIR | %u"), inevt.mask & ~IN_ISDIR) :
                        wxString::Format(wxT("%u"), inevt.mask);
        const char* name = "";
        if (inevt.len)
            name = inevt.name;
        return wxString::Format(wxT("Event: wd=%d, mask=%s, cookie=%u, len=%u, name=%s"), inevt.wd, mask.c_str(), inevt.cookie,
                                inevt.len, name);
    }

    static wxFileName GetEventPath(const mywxFSWatchEntry& watch,
                                   const inotify_event& inevt)
    {
        // only when dir is watched, we have non-empty e.name
        wxFileName path = watch.GetPath();
        if (path.IsDir() && inevt.len)
        {
            path = wxFileName(path.GetPath(), wxString(inevt.name, wxConvUTF8));
        }
        return path;
    }

    static int Watcher2NativeFlags(int flags)
    {
        // Start with the standard case of wanting all events
        if (flags == wxFSW_EVENT_ALL)
        {
            return IN_ALL_EVENTS;
        }

        static const int flag_mapping[][2] = {
            { wxFSW_EVENT_ACCESS, IN_ACCESS   },
            { wxFSW_EVENT_MODIFY, IN_MODIFY   },
            { wxFSW_EVENT_ATTRIB, IN_ATTRIB   },
            { wxFSW_EVENT_RENAME, IN_MOVE     },
            { wxFSW_EVENT_CREATE, IN_CREATE   },
            { wxFSW_EVENT_DELETE, IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF },
            { wxFSW_EVENT_UNMOUNT, IN_UNMOUNT }
            // wxFSW_EVENT_ERROR/WARNING make no sense here
        };

        int native_flags = 0;
        for ( unsigned int i=0; i < WXSIZEOF(flag_mapping); ++i)
        {
            if (flags & flag_mapping[i][0])
                native_flags |= flag_mapping[i][1];
        }

        return native_flags;
    }

    static int Native2WatcherFlags(int flags)
    {
        static const int flag_mapping[][2] = {
            { IN_ACCESS,        wxFSW_EVENT_ACCESS }, // generated during read!
            { IN_MODIFY,        wxFSW_EVENT_MODIFY },
            { IN_ATTRIB,        wxFSW_EVENT_ATTRIB },
            { IN_CLOSE_WRITE,   0 },
            { IN_CLOSE_NOWRITE, 0 },
            { IN_OPEN,          0 },
            { IN_MOVED_FROM,    wxFSW_EVENT_RENAME },
            { IN_MOVED_TO,      wxFSW_EVENT_RENAME },
            { IN_CREATE,        wxFSW_EVENT_CREATE },
            { IN_DELETE,        wxFSW_EVENT_DELETE },
            { IN_DELETE_SELF,   wxFSW_EVENT_DELETE },
            { IN_MOVE_SELF,     wxFSW_EVENT_DELETE },

            { IN_UNMOUNT,       wxFSW_EVENT_UNMOUNT  },
            { IN_Q_OVERFLOW,    wxFSW_EVENT_WARNING},

            // ignored, because this is generated mainly by watcher::Remove()
            { IN_IGNORED,        0 }
        };

        unsigned int i=0;
        for ( ; i < WXSIZEOF(flag_mapping); ++i) {
            // in this mapping multiple flags at once don't happen
            if (flags & flag_mapping[i][0])
                return flag_mapping[i][1];
        }

        // never reached
        wxFAIL_MSG(wxString::Format(wxT("Unknown inotify event mask %u"), flags));
        return -1;
    }

    /**
     * Returns error description for specified inotify mask
     */
    static const wxString GetErrorDescription(int flag)
    {
        switch ( flag )
        {
        case IN_Q_OVERFLOW:
            return wxT("Event queue overflowed");
        }

        // never reached
        wxFAIL_MSG(wxString::Format(wxT("Unknown inotify event mask %u"), flag));
        return wxEmptyString;
    }

    mywxFSWSourceHandler* m_handler;        // handler for inotify event source
    mywxFSWatchEntryDescriptors m_watchMap; // inotify wd=>mywxFSWatchEntry* map
    wxArrayInt m_staleDescriptors;        // stores recently-removed watches
    wxInotifyCookies m_cookies;           // map to track renames
    //mywxEventLoopSource* m_source;          // our event loop source
    unsigned int m_sourceId;          // Stores the gtk source id
  
    // file descriptor created by inotify_init()
    int m_ifd;
};

//------------------------------------------------------------------------------

MyFileSystemWatcher::MyFileSystemWatcher()
{
m_service = new mywxFSWatcherImplUnix(this);
m_service->Init(); 
}

bool MyFileSystemWatcher::Add(const wxFileName& path, int events)
{
FileData fd(path.GetFullPath());
if (!fd.IsValid()) { wxLogTrace(wxT("Can't monitor non-existent path \"%s\" for changes."), path.GetFullPath().c_str()); return false; }


    // adding a path in a platform specific way
    mywxFSWatchInfo watch(path.GetFullPath(), events, wxFSWPath_Dir);
    if ( !m_service->Add(watch) )
        return false;

    // on success, either add path to our 'watch-list'
    // or, if already watched, inc the refcount. This may happen if
    // a dir is Add()ed, then later AddTree() is called on a parent dir
    mywxFSWatchInfoMap::iterator it = m_watches.find(path.GetFullPath());
    if ( it == m_watches.end() )
    {
        mywxFSWatchInfoMap::value_type val(path.GetFullPath(), watch);
        m_watches.insert(val).second;
    }
    else
    {
        mywxFSWatchInfo& watch = it->second;
        int count = watch.IncRef();
        wxLogTrace(wxTRACE_FSWATCHER,
                  wxT( "'%s' is now watched %d times"), path.GetFullPath().c_str(), count);
    }
    return true;
}

bool MyFileSystemWatcher::Remove(const wxFileName& path)
{
    // args validation & consistency checks
    wxString canonical = path.GetFullPath();
    if (canonical.IsEmpty())
        return false;

    mywxFSWatchInfoMap::iterator it = m_watches.find(canonical);
    wxCHECK_MSG(it != m_watches.end(), false,
                wxString::Format(wxT("Path '%s' is not watched"), canonical.c_str()));

    // Decrement the watch's refcount and remove from watch-list if 0
    bool ret = true;
    mywxFSWatchInfo& watch = it->second;
    if ( !watch.DecRef() )
    {
        // remove in a platform specific way
        ret = m_service->Remove(watch);

        m_watches.erase(it);
    }
    return ret;
}

bool MyFileSystemWatcher::RemoveAll()
{
    m_service->RemoveAll();
    m_watches.clear();
    return true;
}

void MyFileSystemWatcher::OnDirDeleted(const wxString& path)
{
    if (!path.empty())
    {
        mywxFSWatchInfoMap::iterator it = m_watches.find(path);
        wxCHECK_RET(it != m_watches.end(),
                    wxString::Format(wxT("Path '%s' is not watched"), path.c_str()));

        // path has been deleted, so we must forget it whatever its refcount
        m_watches.erase(it);
    }
}

int MyFileSystemWatcher::GetWatchedPaths(wxArrayString* paths) const
{
    wxCHECK_MSG( paths != NULL, -1, wxT("Null array passed to retrieve paths"));

    mywxFSWatchInfoMap::const_iterator it = m_watches.begin();
    for ( ; it != m_watches.end(); ++it)
    {
        paths->push_back(it->first);
    }

    return m_watches.size();
}

//--------------------------------------------
void mywxFSWSourceHandler::OnReadWaiting()
{
    wxLogTrace(wxTRACE_FSWATCHER, wxT("--- OnReadWaiting ---"));
    m_service->ReadEvents();
}

void mywxFSWSourceHandler::OnWriteWaiting()
{
    wxFAIL_MSG(wxT("We never write to inotify descriptor."));
}

void mywxFSWSourceHandler::OnExceptionWaiting()
{
    wxFAIL_MSG(wxT("We never receive exceptions on inotify descriptor."));
}

#endif // wxVERSION_NUMBER < 3000 && defined(__LINUX__) && defined(__WXGTK__)
