#include "event_id.h"
#include <algorithm>
#include <functional>
#include "entry_event_mgr.h"

                                                // Blackboard name
template<class T>
const std::string File_bb_event_poller<T>::FILE_BB_EVENT_POLLER_NAME(
                                                    "File_bb_event_poller/");

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::File_bb_event_poller
//
// Purpose:
//
//    The constructor
//
// Description:
//
//    Stores references to the wrapped Blackboard and Entry_event_mgr,
//    and creates the polling thread. This class takes ownership of the 
//    wrapped Blackboard object and the Entry_event_mgr and deletes them
//    in the destructor. Therefore, both of these objects should be
//    instantiated off the heap.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    none
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
File_bb_event_poller<T>::File_bb_event_poller(
                                Blackboard* bb,     // wrapped Blackboard
                                Entry_event_mgr* l, // event manager
                                const int i)        // polling interval
                                          : die(false), core(bb), evt_mgr(l), 
                                            target(new T), polling_interval(i),
                                            event_id(new Event_id)
{
   target->search_fill_all();
   pthread_mutex_init(&die_lock, 0);
   pthread_mutex_init(&event_id_lock, 0);
   pthread_create(&poller, 0, &poller_run, this);
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::~File_bb_event_poller
//
// Purpose:
//
//    The destructor
//
// Description:
//
//    Terminates the polling thread and cleans up.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    none
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
File_bb_event_poller<T>::~File_bb_event_poller()
{
   pthread_mutex_lock(&die_lock);
   die = true;
   pthread_mutex_unlock(&die_lock);
   pthread_join(poller, 0);
   delete evt_mgr;
   delete core;
   pthread_mutex_destroy(&die_lock);
   pthread_mutex_destroy(&event_id_lock);
   delete event_id;
   delete target;
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::post
//
// Purpose:
//
//    Post a new Entry on the Blackboard.
//
// Description:
//
//    Calls post on the wrapped Blackboard object.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    [any exceptions thrown by post on the wrapped Blackboard]
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
void File_bb_event_poller<T>::post(const Entry* e)
{
   core->post(e);
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::erase
//
// Purpose:
//
//    Erase an Entry on the Blackboard.
//
// Description:
//
//    Calls erase on the wrapped Blackboard object.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    [any exceptions thrown by erase on the wrapped Blackboard]
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
void File_bb_event_poller<T>::erase(const Entry* e)
{
   core->erase(e);
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::replace
//
// Purpose:
//
//    Replace an existing Entry with another on the Blackboard.
//
// Description:
//
//    Calls replace on the wrapped Blackboard object.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    [any exceptions thrown by replace on the wrapped Blackboard]
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
void File_bb_event_poller<T>::replace(const Entry* old_ent, 
                                      const Entry* new_ent)
{
   core->replace(old_ent, new_ent);
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::search
//
// Purpose:
//
//    Search for matching Entries on the Blackboard.
//
// Description:
//
//    Calls search on the wrapped Blackboard object and increments the
//    event ID value. NOTE: Unlike most other OAPI Blackboard search
//    member functions, this member function returns the incremented
//    event ID, not the number of matching Entries from the search.
//
//    This makes it possible for a caller to distinguish between events
//    generated before and after a search of the entire Blackboard (say,
//    to populate a manager display).
//
// Returns:
//
//    The next event ID value.
//
// Exceptions thrown:
//
//    [any exceptions thrown by search on the wrapped Blackboard]
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
int File_bb_event_poller<T>::search(const Entry* e, std::vector<Entry*>& res) 
                                                                         const
{
   core->search(e, res);
   pthread_mutex_lock(&event_id_lock);
   int id = ++*event_id;
   pthread_mutex_unlock(&event_id_lock);
   return id;
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::lock_entry
//
// Purpose:
//
//    Lock an Entry on the Blackboard.
//
// Description:
//
//    Calls lock_entry on the wrapped Blackboard object.
//
// Returns:
//
//    An Opus_lock object for the locked Entry. The caller should delete
//    the returned object when it is no longer needed.
//
// Exceptions thrown:
//
//    [any exceptions thrown by lock_entry on the wrapped Blackboard]
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
Opus_lock* File_bb_event_poller<T>::lock_entry(const Entry* e)
{
   return(core->lock_entry(e));
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::str
//
// Purpose:
//
//    Get the Blackboard name.
//
// Description:
//
//    Returns a string identifying this Blackboard class.
//
// Returns:
//
//    string Blackboard name.
//
// Exceptions thrown:
//
//    none
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
std::string File_bb_event_poller<T>::str() const
{
   return FILE_BB_EVENT_POLLER_NAME+core->str();
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::test_driver
//
// Purpose:
//
//    Run the test driver.
//
// Description:
//
//    Calls test_driver on the wrapped Blackboard object.
//
// Returns:
//
//    true if the tests pass; false otherwise
//
// Exceptions thrown:
//
//    [any exceptions thrown by test_driver on the wrapped Blackboard]
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
bool File_bb_event_poller<T>::test_driver()
{
   return(core->test_driver());
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_bb_event_poller<T>::poller_run
//
// Purpose:
//
//    Static entry point for polling thread.
//
// Description:
//
//    The polling thread sleeps for polling interval seconds, then
//    conducts a search of the entire wrapped Blackboard contents and
//    compares the results with the previous search. Events are generated
//    as appropriate.
//
// Returns:
//
//    0 when signalled to die
//
// Exceptions thrown:
//
//    none
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 01/08/02  44741    WMiller     Initial code
// 03/03/07  57369    Sontag      Use size_t where appropriate
//////////////////////////////////////////////////////////////////////////

template<class T>
void* File_bb_event_poller<T>::poller_run(void* obj)
{
   File_bb_event_poller<T>* thiz = static_cast<File_bb_event_poller<T>*>(obj);
   Blackboard* bb = thiz->core;
   Entry_event_mgr* mgr = thiz->evt_mgr;
   T* target = thiz->target;

   std::vector<Entry*> last_time;
   std::vector<Entry*> current;
   std::vector<Entry*> hold;
   bool first = true;
   while(true) {
      pthread_mutex_lock(&thiz->die_lock);     // check if time to quit
      if (thiz->die) {
         pthread_mutex_unlock(&thiz->die_lock);
         break;
      }
      pthread_mutex_unlock(&thiz->die_lock);

      sleep(thiz->polling_interval);          // sleep for polling interval

      try {
         bb->search(target, current);         // get everything
      }
      catch(...) {
         // empty block
      }
      if (first) {                            // only store search results
         last_time = current;                 // if this is the first time
         current.clear();
         first = false;
         continue;
      }

                                             // compare consecutive search
                                             // results and generate events
      for(size_t i = 0; i < last_time.size(); ++i) {
         std::vector<Entry*>::iterator vi;
         if ((vi = find_if(current.begin(), current.end(), 
                           Entry_equals(last_time[i]))) != current.end()) {
            // MODIFIED...?
            if (!(**vi == last_time[i])) { //  YES
               pthread_mutex_lock(&thiz->event_id_lock);
               int id = ++*(thiz->event_id);
               pthread_mutex_unlock(&thiz->event_id_lock);
               mgr->notify(last_time[i], *vi, Entry_event_mgr::MODIFIED, id);
            }
            hold.push_back(*vi);
            current.erase(vi);
         } else {
            // DELETED
            pthread_mutex_lock(&thiz->event_id_lock);
            int id = ++*(thiz->event_id);
            pthread_mutex_unlock(&thiz->event_id_lock);
            mgr->notify(last_time[i], 0, Entry_event_mgr::DELETED, id);
         }
         delete last_time[i];
      }
      last_time = hold;
      hold.clear();
      for(size_t i = 0; i < current.size(); ++i) { // CREATED
         pthread_mutex_lock(&thiz->event_id_lock);
         int id = ++*(thiz->event_id);
         pthread_mutex_unlock(&thiz->event_id_lock);
         mgr->notify(current[i], 0, Entry_event_mgr::CREATED, id);
         last_time.push_back(current[i]);
      }
      current.clear();
   }

   for(size_t i = 0; i < last_time.size(); ++i) delete last_time[i];

   return 0;
}
