//
// handler_queue.hpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef ASIO_DETAIL_HANDLER_QUEUE_HPP
#define ASIO_DETAIL_HANDLER_QUEUE_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/push_options.hpp"

#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/noncopyable.hpp"

namespace asio {
namespace detail {

class handler_queue
  : private noncopyable
{
public:
  // Base class for handlers in the queue.
  class handler
    : private noncopyable
  {
  public:
    void invoke()
    {
      invoke_func_(this);
    }

    void destroy()
    {
      destroy_func_(this);
    }

  protected:
    typedef void (*invoke_func_type)(handler*);
    typedef void (*destroy_func_type)(handler*);

    handler(invoke_func_type invoke_func,
        destroy_func_type destroy_func)
      : next_(0),
        invoke_func_(invoke_func),
        destroy_func_(destroy_func)
    {
    }

    ~handler()
    {
    }

  private:
    friend class handler_queue;
    handler* next_;
    invoke_func_type invoke_func_;
    destroy_func_type destroy_func_;
  };

  // Smart point to manager handler lifetimes.
  class scoped_ptr
    : private noncopyable
  {
  public:
    explicit scoped_ptr(handler* h)
      : handler_(h)
    {
    }

    ~scoped_ptr()
    {
      if (handler_)
        handler_->destroy();
    }

    handler* get() const
    {
      return handler_;
    }

    handler* release()
    {
      handler* tmp = handler_;
      handler_ = 0;
      return tmp;
    }

  private:
    handler* handler_;
  };

  // Constructor.
  handler_queue()
    : front_(0),
      back_(0)
  {
  }

  // Wrap a handler to be pushed into the queue.
  template <typename Handler>
  static handler* wrap(Handler h)
  {
    // Allocate and construct an object to wrap the handler.
    typedef handler_wrapper<Handler> value_type;
    typedef handler_alloc_traits<Handler, value_type> alloc_traits;
    raw_handler_ptr<alloc_traits> raw_ptr(h);
    handler_ptr<alloc_traits> ptr(raw_ptr, h);
    return ptr.release();
  }

  // Get the handler at the front of the queue.
  handler* front()
  {
    return front_;
  }

  // Pop a handler from the front of the queue.
  void pop()
  {
    if (front_)
    {
      handler* tmp = front_;
      front_ = front_->next_;
      if (front_ == 0)
        back_ = 0;
      tmp->next_= 0;
    }
  }

  // Push a handler on to the back of the queue.
  void push(handler* h)
  {
    h->next_ = 0;
    if (back_)
    {
      back_->next_ = h;
      back_ = h;
    }
    else
    {
      front_ = back_ = h;
    }
  }

  // Whether the queue is empty.
  bool empty() const
  {
    return front_ == 0;
  }

private:
  // Template wrapper for handlers.
  template <typename Handler>
  class handler_wrapper
    : public handler
  {
  public:
    handler_wrapper(Handler h)
      : handler(
          &handler_wrapper<Handler>::do_call,
          &handler_wrapper<Handler>::do_destroy),
        handler_(h)
    {
    }

    static void do_call(handler* base)
    {
      // Take ownership of the handler object.
      typedef handler_wrapper<Handler> this_type;
      this_type* h(static_cast<this_type*>(base));
      typedef handler_alloc_traits<Handler, this_type> alloc_traits;
      handler_ptr<alloc_traits> ptr(h->handler_, h);

      // Make a copy of the handler so that the memory can be deallocated before
      // the upcall is made.
      Handler handler(h->handler_);

      // Free the memory associated with the handler.
      ptr.reset();

      // Make the upcall.
      asio_handler_invoke_helpers::invoke(handler, &handler);
    }

    static void do_destroy(handler* base)
    {
      // Take ownership of the handler object.
      typedef handler_wrapper<Handler> this_type;
      this_type* h(static_cast<this_type*>(base));
      typedef handler_alloc_traits<Handler, this_type> alloc_traits;
      handler_ptr<alloc_traits> ptr(h->handler_, h);
    }

  private:
    Handler handler_;
  };

  // The front of the queue.
  handler* front_;

  // The back of the queue.
  handler* back_;
};

} // namespace detail
} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_DETAIL_HANDLER_QUEUE_HPP
