// $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/util/threadsafe_vector.h 3471 2020-09-21 14:18:40Z depa $
// based on class definition for threadsafe queue from C++ concurrency in action (listing 6.2)

#pragma once

#include <vector>

#include <boost/chrono/duration.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/bind.hpp>
#include <boost/move/move.hpp>
#include <boost/typeof/typeof.hpp>

typedef boost::chrono::duration<int, boost::ratio<1> > sec;
// namespaces from CppClient.cpp provided by P.Grenz
using indi::IBase;
using indi::IMessage;
using indi::IOut;
using indi::IProperty;
using indi::IIn;
using indi::IElement;
using indi::IXmlParser;
using pcf::Socket;
using pcf::System;

template<typename T>
class threadsafe_vector
{
private:
  mutable boost::mutex m_mut;
  std::vector<T> m_dataVector;
  boost::condition_variable m_dataCond;
  // sec m_duration;
  bool wait_predicate()
  {
    return !m_dataVector.empty();
  }
public:
  threadsafe_vector()
  {
    //
    // m_duration=10;
  }
  threadsafe_vector(threadsafe_vector const& _other)
  {
    boost::lock_guard<boost::mutex> lock(_other.m_mut);
    m_dataVector = _other.m_dataVector;
  }
  // threadsafe_vector(const threadsafe_vector&);
  ~threadsafe_vector()
  {
    //
  }
  void push_back(T _newValue)
  {
    boost::lock_guard<boost::mutex> lock(m_mut);
    m_dataVector.push_back(boost::move(_newValue));
    m_dataCond.notify_one();
  }

  void wait_and_pop_back(T& _value)
  {
    boost::unique_lock<boost::mutex> lock(m_mut);
    m_dataCond.wait(lock, boost::bind(&threadsafe_vector::wait_predicate, this));
    _value = boost::move(m_dataVector.back());
    m_dataVector.pop_back();
  }

  boost::shared_ptr<T> wait_and_pop_back()
  {
    boost::unique_lock<boost::mutex> lock(m_mut);
    m_dataCond.wait(lock, boost::bind(&threadsafe_vector::wait_predicate, this));
    boost::shared_ptr<T> res(boost::make_shared<T>(boost::move(m_dataVector.back())));
    m_dataVector.pop_back();
    return res;
  }

  bool try_wait_and_compare(const T& _inValue, T& _outValue)
  {
    bool res = false;
    boost::chrono::duration<long, boost::ratio<1> > waitDuration(14);
    boost::unique_lock<boost::mutex> lock(m_mut);
    m_dataCond.wait_for(lock, waitDuration);
    _outValue = boost::move(m_dataVector.back());
    if (_outValue == _inValue)
      {
        m_dataVector.pop_back();
        res = true;
      }
    else
      {
        for (BOOST_AUTO(it, m_dataVector.begin()); it < (m_dataVector.end()-1); ++it)
          {
            _outValue = boost::move(*it);
            if (_outValue == _inValue)
              {
                m_dataVector.erase(it);
                res = true;
                break;
              }
          }
        if (!res)
          {
            _outValue.clear();
            m_dataCond.notify_one();
          }
      }
    return res;
  }

  bool try_pop_back(T& _value)
  {
    boost::lock_guard<boost::mutex> lock(m_mut);
    if (m_dataVector.empty())
      return false;
    _value = boost::move(m_dataVector.back());
    m_dataVector.pop_back();
    return true;
  }

  boost::shared_ptr<T> try_pop_back()
  {
    boost::lock_guard<boost::mutex> lock(m_mut);
    if (m_dataVector.empty())
      return boost::shared_ptr<T>();
    boost::shared_ptr<T> res(boost::make_shared<T>(boost::move(m_dataVector.back())));
    m_dataVector.pop_back();
    return res;
  }

  bool empty() const
  {
    boost::lock_guard<boost::mutex> lock(m_mut);
    return m_dataVector.empty();
  }

  int size() const
  {
    boost::lock_guard<boost::mutex> lock(m_mut);
    return m_dataVector.size();
  }

};

typedef boost::shared_ptr<threadsafe_vector<IProperty> > tsVectorIndiPropPtr;

// template <typename T> class threadsafe_vector<IndiProperty>
