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

#pragma once

#include <queue>

#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>


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_queue
{
private:
  mutable boost::mutex m_mut;
  std::queue<T> m_dataQueue;
  boost::condition_variable m_dataCond;
  // sec m_duration;
  bool wait_predicate()
  {
    return !m_dataQueue.empty();
  }
public:
  threadsafe_queue()
  {
    //
    // m_duration=10;
  }
  threadsafe_queue(threadsafe_queue const& _other)
  {
    boost::lock_guard<boost::mutex> lock(_other.m_mut);
    m_dataQueue = _other.m_dataQueue;
  }
  // threadsafe_queue(const threadsafe_queue&);
  ~threadsafe_queue()
  {
    //
  }
  void push(T _newValue)
  {
    boost::lock_guard<boost::mutex> lock(m_mut);
    m_dataQueue.push(boost::move(_newValue));
    m_dataCond.notify_one();
  }

  void wait_and_pop(T& _value)
  {
    boost::unique_lock<boost::mutex> lock(m_mut);
    m_dataCond.wait(lock, boost::bind(&threadsafe_queue::wait_predicate, this));
    _value = boost::move(m_dataQueue.front());
    m_dataQueue.pop();
  }

  boost::shared_ptr<T> wait_and_pop()
  {
    boost::unique_lock<boost::mutex> lock(m_mut);
    m_dataCond.wait(lock, boost::bind(&threadsafe_queue::wait_predicate, this));
    boost::shared_ptr<T> res(boost::make_shared<T>(boost::move(m_dataQueue.front())));
    m_dataQueue.pop();
    return res;
  }

  bool try_wait_and_compare(const T& _inValue, T& _outValue)
  {
    bool res = false;
    // sec waitDuration;
    boost::chrono::duration<long, boost::ratio<1> > waitDuration(14);
    boost::unique_lock<boost::mutex> lock(m_mut);
    // m_dataCond.wait(lock, boost::bind(&threadsafe_queue::wait_predicate, this));
    m_dataCond.wait_for(lock, waitDuration);
    _outValue = boost::move(m_dataQueue.front());
    if (_outValue == _inValue)
      {
        m_dataQueue.pop();
        res = true;
      }
    else
      m_dataCond.notify_one();
    return res;
  }

  bool try_pop(T& _value)
  {
    boost::lock_guard<boost::mutex> lock(m_mut);
    if (m_dataQueue.empty())
      return false;
    _value = boost::move(m_dataQueue.front());
    m_dataQueue.pop();
    return true;
  }

  boost::shared_ptr<T> try_pop()
  {
    boost::lock_guard<boost::mutex> lock(m_mut);
    if (m_dataQueue.empty())
      return boost::shared_ptr<T>();
    boost::shared_ptr<T> res(boost::make_shared<T>(boost::move(m_dataQueue.front())));
    m_dataQueue.pop();
    return res;
  }

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

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

};

typedef boost::shared_ptr<threadsafe_queue<IProperty> > tsQueueIndiPropPtr;

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