/////////////////////////////////////////////////////////////////////////////
// Name:        MyFSWatcher.h
// 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
/////////////////////////////////////////////////////////////////////////////

#ifndef MYFSWATCHERH
#define MYFSWATCHERH

#include "wx/version.h"
#include <wx/filename.h>

#if	wxVERSION_NUMBER < 3003 || wxVERSION_NUMBER == 3100
  #define USE_MYFSWATCHER true
#else
  #define USE_MYFSWATCHER false
#endif
      
#if USE_MYFSWATCHER && defined(__WXGTK__)

#include "wx/wx.h"

#define wxTRACE_FSWATCHER wxT("fswatcher")
#define wxTRACE_MYFSWATCHER wxT("myfswatcher")
#define wxTRACE_MYFSWATCHER_STALEDESCRIPTOR wxT("myfswatcherstale")

enum
{
    wxFSW_EVENT_CREATE = 0x01,
    wxFSW_EVENT_DELETE = 0x02,
    wxFSW_EVENT_RENAME = 0x04,
    wxFSW_EVENT_MODIFY = 0x08,
    wxFSW_EVENT_ACCESS = 0x10,
    wxFSW_EVENT_ATTRIB = 0x20,
    // error events
    wxFSW_EVENT_WARNING = 0x40,
    wxFSW_EVENT_ERROR = 0x80,
    wxFSW_EVENT_UNMOUNT = 0x2000,

    wxFSW_EVENT_ALL = wxFSW_EVENT_CREATE | wxFSW_EVENT_DELETE |
                         wxFSW_EVENT_RENAME | wxFSW_EVENT_MODIFY |
                         wxFSW_EVENT_ACCESS | wxFSW_EVENT_ATTRIB |
                         wxFSW_EVENT_WARNING | wxFSW_EVENT_ERROR
};

enum wxFSWPathType
{
    wxFSWPath_None,     // Invalid value for an initialized watch.
    wxFSWPath_File,     // Plain file.
    wxFSWPath_Dir,      // Watch a directory and the files in it.
    wxFSWPath_Tree      // Watch a directory and all its children recursively.
};

// Type of the warning for the events notifying about them.
enum wxFSWWarningType
{
    wxFSW_WARNING_NONE,
    wxFSW_WARNING_GENERAL,
    wxFSW_WARNING_OVERFLOW
};

/**
 * Event containing information about file system change.
 */
class  wxFileSystemWatcherEvent;
DECLARE_EVENT_TYPE(wxEVT_FSWATCHER,  wxFileSystemWatcherEvent);

class wxFileSystemWatcherEvent: public wxEvent
{
public:
    wxFileSystemWatcherEvent(int changeType, int watchid = wxID_ANY) :
        wxEvent(watchid, wxEVT_FSWATCHER),
        m_changeType(changeType), m_warningType(wxFSW_WARNING_NONE)
    {
    }

    wxFileSystemWatcherEvent(int changeType, const wxString& errorMsg,
                             int watchid = wxID_ANY) :
        wxEvent(watchid, wxEVT_FSWATCHER),
        m_changeType(changeType), m_warningType(wxFSW_WARNING_NONE), m_errorMsg(errorMsg)
    {
    }

    wxFileSystemWatcherEvent(int changeType,
                             const wxFileName& path, const wxFileName& newPath,
                             int watchid = wxID_ANY) :
         wxEvent(watchid, wxEVT_FSWATCHER),
         m_changeType(changeType), m_warningType(wxFSW_WARNING_NONE), m_path(path), m_newPath(newPath)
    {
    }

    /**
     * Returns the path at which the event occurred.
     */
    const wxFileName& GetPath() const
    {
        return m_path;
    }

    /**
     * Sets the path at which the event occurred
     */
    void SetPath(const wxFileName& path)
    {
        m_path = path;
    }

    /**
     * In case of rename(move?) events, returns the new path related to the
     * event. The "new" means newer in the sense of time. In case of other
     * events it returns the same path as GetPath().
     */
    const wxFileName& GetNewPath() const
    {
        return m_newPath;
    }

    /**
     * Sets the new path related to the event. See above.
     */
    void SetNewPath(const wxFileName& path)
    {
        m_newPath = path;
    }

    /**
     * Returns the type of file system event that occurred.
     */
    int GetChangeType() const
    {
        return m_changeType;
    }

    virtual wxEvent* Clone() const
    {
        wxFileSystemWatcherEvent* evt = new wxFileSystemWatcherEvent(*this);
        evt->m_errorMsg = m_errorMsg.c_str();
        evt->m_path = wxFileName(m_path.GetFullPath().c_str());
        evt->m_newPath = wxFileName(m_newPath.GetFullPath().c_str());
        evt->m_warningType = m_warningType;
        return evt;
    }


    /**
     * Returns if this error is an error event
     */
    bool IsError() const
    {
        return (m_changeType & (wxFSW_EVENT_ERROR | wxFSW_EVENT_WARNING)) != 0;
    }

    wxFSWWarningType GetWarningType() const
    {
        return m_warningType;
    }
    wxString GetErrorDescription() const
    {
        return m_errorMsg;
    }

    /**
     * Returns a wxString describing an event useful for debugging or testing
     */
    wxString ToString() const;

protected:
    int m_changeType;
    wxFSWWarningType m_warningType;
    wxFileName m_path;
    wxFileName m_newPath;
    wxString m_errorMsg;
};

typedef void (wxEvtHandler::*wxFileSystemWatcherEventFunction)
                                                (wxFileSystemWatcherEvent&);

#define wxFileSystemWatcherEventHandler(func) \
	(wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction)\
	wxStaticCastEvent(wxFileSystemWatcherEventFunction, &func)


#if wxVERSION_NUMBER < 3000
/////////////////////////////////////////////////////////////////////////////
// Name:        wx/sharedptr.h
// Purpose:     Shared pointer based on the counted_ptr<> template, which
//              is in the public domain
// Author:      Robert Roebling, Yonat Sharon
// RCS-ID:      $Id$
// Copyright:   Robert Roebling
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _MYWX_SHAREDPTR_H_
#define _MYWX_SHAREDPTR_H_

#include "wx/defs.h"

// ----------------------------------------------------------------------------
// wxSharedPtr: A smart pointer with non-intrusive reference counting.
// ----------------------------------------------------------------------------

template <class T>
class wxSharedPtr
{
public:
    typedef T element_type;

    wxEXPLICIT wxSharedPtr( T* ptr = NULL )
        : m_ref(NULL)
    {
        if (ptr)
            m_ref = new reftype(ptr);
    }

    ~wxSharedPtr()                           { Release(); }
    wxSharedPtr(const wxSharedPtr& tocopy)   { Acquire(tocopy.m_ref); }

    wxSharedPtr& operator=( const wxSharedPtr& tocopy )
    {
        if (this != &tocopy)
        {
            Release();
            Acquire(tocopy.m_ref);
        }
        return *this;
    }

    wxSharedPtr& operator=( T* ptr )
    {
        if (get() != ptr)
        {
            Release();
            if (ptr)
                m_ref = new reftype(ptr);
        }
        return *this;
    }

    // test for pointer validity: defining conversion to unspecified_bool_type
    // and not more obvious bool to avoid implicit conversions to integer types
    typedef T *(wxSharedPtr<T>::*unspecified_bool_type)() const;
    operator unspecified_bool_type() const
    {
        if (m_ref && m_ref->m_ptr)
           return  &wxSharedPtr<T>::get;
        else
           return NULL;
    }

    T& operator*() const
    {
        wxASSERT(m_ref != NULL);
        wxASSERT(m_ref->m_ptr != NULL);
        return *(m_ref->m_ptr);
    }

    T* operator->() const
    {
        wxASSERT(m_ref != NULL);
        wxASSERT(m_ref->m_ptr != NULL);
        return m_ref->m_ptr;
    }

    T* get() const
    {
        return m_ref ? m_ref->m_ptr : NULL;
    }

    void reset( T* ptr = NULL )
    {
        Release();
        if (ptr)
            m_ref = new reftype(ptr);
    }

    bool unique()   const    { return (m_ref ? m_ref->m_count == 1 : true); }
    long use_count() const   { return (m_ref ? (long)m_ref->m_count : 0); }

private:

    struct reftype
    {
        reftype( T* ptr = NULL, unsigned count = 1 ) : m_ptr(ptr), m_count(count) {}
        T*          m_ptr;
        int m_count;
    }* m_ref;

    void Acquire(reftype* ref)
    {
        m_ref = ref;
        if (ref)
            ++ref->m_count;
    }

    void Release()
    {
        if (m_ref)
        {
            --m_ref->m_count;
            if (m_ref->m_count == 0)
            {
                delete m_ref->m_ptr;
                delete m_ref;
            }
            m_ref = NULL;
        }
    }
};

template <class T, class U>
bool operator == (wxSharedPtr<T> const &a, wxSharedPtr<U> const &b )
{
    return a.get() == b.get();
}

template <class T, class U>
bool operator != (wxSharedPtr<T> const &a, wxSharedPtr<U> const &b )
{
    return a.get() != b.get();
}

#endif // _MYWX_SHAREDPTR_H_
#endif //  wxVERSION_NUMBER < 3000

class mywxFSWatcherImplUnix;
class MyFileSystemWatcher;

#if wxVERSION_NUMBER < 3000
  enum
  {
      wxEVENT_SOURCE_INPUT = 0x01,
      wxEVENT_SOURCE_OUTPUT = 0x02,
      wxEVENT_SOURCE_EXCEPTION = 0x04,
      wxEVENT_SOURCE_ALL = wxEVENT_SOURCE_INPUT |
                           wxEVENT_SOURCE_OUTPUT |
                           wxEVENT_SOURCE_EXCEPTION
  };
#endif

/**
 * Handler for handling i/o from inotify descriptor
 */
class mywxFSWSourceHandler
{
public:
    mywxFSWSourceHandler(mywxFSWatcherImplUnix* service) :  m_service(service) {}

    virtual void OnReadWaiting();
    virtual void OnWriteWaiting();
    virtual void OnExceptionWaiting();

protected:
    mywxFSWatcherImplUnix* m_service;
};


// Simple container to store information about one watched path.
class mywxFSWatchInfo
{
public:
    mywxFSWatchInfo() :
        m_events(-1), m_type(wxFSWPath_None), m_refcount(-1)
    {
    }

    mywxFSWatchInfo(const wxString& path,
                  int events,
                  wxFSWPathType type,
                  const wxString& filespec = wxString()) :
        m_path(path), m_filespec(filespec), m_events(events), m_type(type),
        m_refcount(1)
    {
    }

    const wxString& GetPath() const
    {
        return m_path;
    }

    const wxString& GetFilespec() const { return m_filespec; }

    int GetFlags() const
    {
        return m_events;
    }

    wxFSWPathType GetType() const
    {
        return m_type;
    }

    // Reference counting of watch entries is used to avoid watching the same
    // file system path multiple times (this can happen even accidentally, e.g.
    // when you have a recursive watch and then decide to watch some file or
    // directory under it separately).
    int IncRef()
    {
        return ++m_refcount;
    }

    int DecRef()
    {
        wxASSERT_MSG( m_refcount > 0, wxT("Trying to decrement a zero count") );
        return --m_refcount;
    }

protected:
    wxString m_path;
    wxString m_filespec;      // For tree watches, holds any filespec to apply
    int m_events;
    wxFSWPathType m_type;
    int m_refcount;
};

WX_DECLARE_STRING_HASH_MAP(mywxFSWatchInfo, mywxFSWatchInfoMap);


class mywxFSWatchEntry : public mywxFSWatchInfo
{
public:
    mywxFSWatchEntry(const mywxFSWatchInfo& winfo) :
        mywxFSWatchInfo(winfo)
    {
    }

    int GetWatchDescriptor() const
    {
        return m_wd;
    }

    void SetWatchDescriptor(int wd)
    {
        m_wd = wd;
    }

private:
    int m_wd;

};

WX_DECLARE_STRING_HASH_MAP(wxSharedPtr<mywxFSWatchEntry>,wxFSWatchEntries);


class mywxFSWatcherImpl
{
public:
    mywxFSWatcherImpl(MyFileSystemWatcher* watcher) :  m_watcher(watcher)  {}

    virtual ~mywxFSWatcherImpl()  { (void) RemoveAll(); }

    virtual bool Init() = 0;

    virtual bool Add(const mywxFSWatchInfo& winfo)
    {
        if ( m_watches.find(winfo.GetPath()) != m_watches.end() )
        {
            wxLogTrace(wxTRACE_FSWATCHER,
                       wxT("Path '%s' is already watched"), winfo.GetPath().c_str());
            // This can happen if a dir is watched, then a parent tree added
            return true;
        }

        // construct watch entry
        wxSharedPtr<mywxFSWatchEntry> watch(new mywxFSWatchEntry(winfo));

        if (!DoAdd(watch))
            return false;

        // add watch to our map (always succeedes, checked above)
        wxFSWatchEntries::value_type val(watch->GetPath(), watch);
        return m_watches.insert(val).second;
    }

    virtual bool Remove(const mywxFSWatchInfo& winfo)
    {
        wxFSWatchEntries::iterator it = m_watches.find(winfo.GetPath());
        if ( it == m_watches.end() )
        {
            wxLogTrace(wxTRACE_FSWATCHER,
                       wxT("Path '%s' is not watched"), winfo.GetPath().c_str());
            // This can happen if a dir is watched, then a parent tree added
            return true;
        }
        wxSharedPtr<mywxFSWatchEntry> watch = it->second;
        m_watches.erase(it);
        return DoRemove(watch);
    }

    virtual bool RemoveAll()
    {
        m_watches.clear();
        return true;
    }

    // Check whether any filespec matches the file's ext (if present)
    bool MatchesFilespec(const wxFileName& fn, const wxString& filespec) const
    {
        return filespec.empty() || wxMatchWild(filespec, fn.GetFullName());
    }

protected:
    virtual bool DoAdd(wxSharedPtr<mywxFSWatchEntry> watch) = 0;

    virtual bool DoRemove(wxSharedPtr<mywxFSWatchEntry> watch) = 0;

    wxFSWatchEntries m_watches;
    MyFileSystemWatcher* m_watcher;
};


class MyFileSystemWatcher
{
public:
MyFileSystemWatcher();
virtual ~MyFileSystemWatcher() { RemoveAll(); }

virtual bool Add(const wxFileName& path, int events = wxFSW_EVENT_ALL);
virtual bool Remove(const wxFileName& path);
virtual bool RemoveAll();
virtual void OnDirDeleted(const wxString& path);
    
bool AddAny(const wxFileName& path, int events, wxFSWPathType type, const wxString& filespec = wxString())
  { wxCHECK_MSG(true, false, wxT("This function wasn't supposed to be called!")); return false; }

int GetWatchedPathsCount() const { return m_watches.size(); }
int GetWatchedPaths(wxArrayString* paths) const;
wxEvtHandler* GetOwner() const { return m_owner; }

void SetOwner(wxEvtHandler* handler)  { m_owner = handler; }

protected:
mywxFSWatchInfoMap m_watches;
mywxFSWatcherImpl* m_service; 
wxEvtHandler* m_owner;

//friend class mywxFSWatcherImpl;
};


#endif // wxVERSION_NUMBER < 3000
#endif // define MYFSWATCHERH
