#ifndef _VN_RESOURCE_POOL_H
#define _VN_RESOURCE_POOL_H

#include <assert.h>
#include <string>
#include <stdexcept>

#include "ace/ACE.h"
#include "ace/Containers.h"
#include "ace/Synch.h"
#include "ace/Synch_Traits.h"
#include "ace/Bound_Ptr.h"
#include "ace/Condition_T.h"
#include "ace/Refcounted_Auto_Ptr.h"

template <class RESOURCE_T> class VN_Resource_Pool;

template <class RESOURCE_T>
class VN_Resource_Pool_Ptr : public ACE_Refcounted_Auto_Ptr<VN_Resource_Pool<RESOURCE_T>, ACE_Thread_Mutex>
{
public:
    VN_Resource_Pool_Ptr(VN_Resource_Pool<RESOURCE_T> *x = 0)
        : ACE_Refcounted_Auto_Ptr<VN_Resource_Pool<RESOURCE_T>, ACE_Thread_Mutex>(x)
    {
    }
};

class resource_error : public std::runtime_error 
{
   public:
      resource_error(const std::string &what) 
         : runtime_error(what) {} 
};

template <class RESOURCE_T>
class ACE_Export VN_Resource_Check
{
  public:
   bool operator () (RESOURCE_T *res) const 
   { 
      return res != 0;
   };
};

template <class RESOURCE_T>
class ACE_Export VN_Resource_Create
{
  public:
   RESOURCE_T *operator () () const //throw (resource_error)
   { 
      return new RESOURCE_T;
   };
};

template <class RESOURCE_T>
class ACE_Export VN_Resource_Destroy
{
  public:
   void operator () (RESOURCE_T *res) const 
   { 
      delete res;
   };
};


/**
 * Wrapper for any resource object. It incapsulates object reference design. 
 * When the last instance of an VN_Resource_Pool that references a
 * particular resource object is destroyed, it will put resource again in to pool.
 * Note: thread safetely dependeds on resource type.
 */
template <class RESOURCE_T>
class VN_Resource
{   
public:
      VN_Resource(RESOURCE_T *res, VN_Resource_Pool_Ptr<RESOURCE_T> *pool_ptr)
         :  counter_ (COUNTER::create_strong ()), res_(res)
         , pool_ptr_(*pool_ptr)          
      {}

      VN_Resource(const VN_Resource<RESOURCE_T>& r) : counter_(r.counter_), res_(r.res_)
                                                    , pool_ptr_(r.pool_ptr_)
      { 
          COUNTER::attach_strong(this->counter_);
      }

      virtual ~VN_Resource()
      {
#ifdef VN_TRACE_POOL
          fprintf(stderr, "%p ~VN_Resource()\n", this);
#endif

         if (COUNTER::detach_strong(this->counter_) == 0)
         {
             pool_ptr_->put(*this);
         }
      }
         
      RESOURCE_T* operator->() const { return res_; }

      operator RESOURCE_T*() const { return res_; } 


private:
      /**
       * Assignment operator is deprecated. 
       */
      VN_Resource<RESOURCE_T>& operator=(const VN_Resource<RESOURCE_T>& r){return r;}

private:
      typedef ACE_Bound_Ptr_Counter<ACE_Thread_Mutex> COUNTER;

      /// The reference counter.
      COUNTER *counter_;

      /// The underlying resource.
      RESOURCE_T *res_;

      VN_Resource_Pool_Ptr<RESOURCE_T> pool_ptr_;   
};

template <class RESOURCE_T>
class VN_Resource_Pool
{
friend class VN_Resource<RESOURCE_T>;
friend class ACE_Auto_Basic_Ptr<VN_Resource_Pool<RESOURCE_T> >;

public:
    static VN_Resource_Pool<RESOURCE_T> *open(uint32_t limit = ~0)
    {
        VN_Resource_Pool<RESOURCE_T> *pool = new VN_Resource_Pool<RESOURCE_T>(limit);
#ifdef VN_TRACE_POOL
         fprintf(stderr, "%p VN_Resource_Pool::open(), limit=%d\n", pool, limit);
#endif

        return pool;
    }

    static void close(VN_Resource_Pool<RESOURCE_T> *pool)
    {
#ifdef VN_TRACE_POOL
        fprintf(stderr, "%p VN_Resource_Pool::close()\n", pool);
#endif

        delete pool->internal_ptr_;
    }

private:         
    VN_Resource_Pool(uint32_t limit = ~0) 
        : counter_(0), limit_(limit), pool_lock_(), pool_cond_(pool_lock_)
        , internal_ptr_(new VN_Resource_Pool_Ptr<RESOURCE_T>(this))
    {
    }

    virtual ~VN_Resource_Pool() 
    {
        ACE_Guard<ACE_Thread_Mutex> guard(this->pool_lock_);

        assert(counter_ == 0);

        RESOURCE_T *res;
        while (resource_list_.dequeue_head(res) == 0)
        {
            VN_Resource_Destroy<RESOURCE_T>()(res);                  
        }

#ifdef VN_TRACE_POOL
        fprintf(stderr, "%p ~VN_Resource_Pool()\n", this);
#endif
    }

public:
    /**
     * Get first available resource by its name
     */
    VN_Resource<RESOURCE_T> get() //throw (resource_error)
    {
        ACE_Guard<ACE_Thread_Mutex> guard(this->pool_lock_);

#ifdef VN_TRACE_POOL
        fprintf(stderr, "%p VN_Resource_Pool::get()\n", this);
#endif
        
        RESOURCE_T *resource = 0;
        
        // If we reach limit of available resources - waiting for somehow put resource back in the pool
        while (counter_ == limit_) 
        {
//            LOG_WARN("%s: No more resources available. Waiting for somehow put resource back", name_.c_str());

            printf("No more resources available. Waiting for somehow put resource back (limit=%d, counter=%d)\n",
                   limit_, counter_);

            pool_cond_.wait();
        }

        if (resource_list_.dequeue_head(resource) == -1)
        {
            // There is no such resource in the cache, creating new one
            resource = VN_Resource_Create<RESOURCE_T>()();
        }

        ++counter_;

        return VN_Resource<RESOURCE_T>(resource, internal_ptr_);        
    }

    bool is_full()
    {
        ACE_Guard<ACE_Thread_Mutex> guard(this->pool_lock_);
        return counter_ == limit_;
    }

private:

      /**
       * Put resource back in to pool
       */
      int put(const VN_Resource<RESOURCE_T> &resource)
      {
         ACE_Guard<ACE_Thread_Mutex> guard(this->pool_lock_);
      
#ifdef VN_TRACE_POOL
         fprintf(stderr, "%p VN_Resource_Pool::put()\n", this);
#endif
   
         if (!VN_Resource_Check<RESOURCE_T>()(resource)) // resource is died
         {
//            LOG_WARN("Resource is unusable. Removing from pool");
            VN_Resource_Destroy<RESOURCE_T>()(resource);                  
         }
         else
         {
             if (resource_list_.enqueue_head(resource) == -1)
             {
//                 LOG_ERROR("%s: Could not bind resource", name_.c_str());
                 return -1;
             }         
         }
         
         --counter_;
         
         pool_cond_.broadcast();

         return 0;
      }

 
private:      
    /// Internal counter of resources already created 
    uint32_t counter_;
    
    /// Limit of resources
    uint32_t limit_;

    ACE_Thread_Mutex pool_lock_;
    ACE_Condition<ACE_Thread_Mutex> pool_cond_;
    ACE_Unbounded_Queue<RESOURCE_T*> resource_list_;
    
    VN_Resource_Pool_Ptr<RESOURCE_T> *internal_ptr_;
    
};

#endif



