//
// File:   inotify-example.cpp
// Date:   July 16, 2013
// Author: Peter Krnjevic <pkrnjevic@gmail.com>, on the shoulders of many others
//         Adaptation to the use in SHINS by Marco De Pascale <marco.depascale@inaf.it>
//
// This is a simple inotify sample program monitoring changes to "./tmp" directory (create ./tmp beforehand)
// Recursive monitoring of file and directory create and delete events is implemented, but
// monitoring pre-existing "./tmp" subfolders is not.
// A C++ class containing a couple of maps is used to simplify monitoring.
// The Watch class is minimally integrated, so as to leave the main inotify code
// easily recognizeable.
//
// *N.B.*
// 1. This code is meant to illustrate inotify usage, and not intended to be
//    production ready. Exception and error handling is largely incomplete or missing.
// 2. inotify has a fundamental flaw for which there is no real solution: if subdirectories are
//    created too quickly, create events will be lost, resulting in those subdirectories (as well
//    as their children) going unwatched.
// 3. fanotify, available in newer kernels, can monitor entire volumes, and is often a better solution.
//
//
// This code sample is released into the Public Domain.
//
//
// To compile:
//    $ g++ inotify-example.cpp -o inotify-example
//    $ g++ -I/usr/include/boost -L/lib64  inotify-example-boost_thread.cpp -o inotify-example-boost_thread
// To run:
//    $ ./inotify-example
//
// To exit:
//    control-C
//
//

// Adapting the use of Watch class to boost::thread for SHINS
// have to be added:
// - error and exception control
// - recurrent call to second thread to mother directory when directory is missing
//   (this happens once per night at first observation, which creates both directory
//   FITS file. Ideally, the second thread ends before the first, which ends on FITS file
//   creation. WARNING: the creation of directory and the creation of FITS file are
//   evets near in time)
// - main function becomes the function called by the boost thread
// - thread creation via boost::thread class

#include <stdio.h>
#include <unistd.h>  // this was missing
#include <signal.h>
#include <limits.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <iostream>
#include <string>
#include <map>

#include <boost/thread.hpp>
#include <boost/filesystem/path.hpp>

// using std::map;
// using std::string;
// using std::cout;
// using std::endl;

#define EVENT_SIZE          ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN       ( 1024 * ( EVENT_SIZE + NAME_MAX + 1) )
#define WATCH_FLAGS         ( IN_CREATE )


// Watch class keeps track of watch descriptors (wd), parent watch descriptors (pd), and names (from event->name).
// The class provides some helpers for inotify, primarily to enable recursive monitoring:
// 1. To add a watch (inotify_add_watch), a complete path is needed, but events only provide file/dir name with no path.
// 2. Delete events provide parent watch descriptor and file/dir name, but removing the watch (infotify_rm_watch) needs a wd.
//
namespace shins{
  namespace util{

    class Watch {
      struct wd_elem {
        int pd;
        std::string name;
        bool operator() (const wd_elem &l, const wd_elem &r) const
        { return l.pd < r.pd ? true : l.pd == r.pd && l.name < r.name ? true : false; }
      };
      std::map<int, wd_elem> watch;
      std::map<wd_elem, int, wd_elem> rwatch;
    public:
      // Insert event information, used to create new watch, into Watch object.
      void insert( int pd, const std::string &name, int wd );
      // Erase watch specified by pd (parent watch descriptor) and name from watch list.
      // Returns full name (for display etc), and wd, which is required for inotify_rm_watch.
      std::string erase( int pd, const std::string &name, int *wd );
      // Given a watch descriptor, return the full directory name as string. Recurses up parent WDs to assemble name,
      // an idea borrowed from Windows change journals.
      std::string get( int wd );
      // Given a parent wd and name (provided in IN_DELETE events), return the watch descriptor.
      // Main purpose is to help remove directories from watch list.
      int get( int pd, std::string name );
      void cleanup( int fd );
      void stats();
    };
  }
}



// Keep going  while run == true, or, in other words, until user hits ctrl-c
// static bool run = true;

/*
static bool run = true;

void sig_callback( int sig )
{
    run = false;
}



int main( )
{
  std::string pathToFile;
  INotify inotifyInstance;
  pathToFile= "/home/sharknirmgr/sandbox/bipdir";
  boost::filesystem::path p(pathToFile);
  std::cout << p.parent_path().string() << std::endl;
  boost::thread fits_notify_thread(inotifyInstance.start, pathToFile); // create and start thread
  fits_notify_thread.join();                                  // Wait for thread to finish
}
*/
