#ifndef REUSABLE_ACCEPTOR_INCLUDED
#define REUSABLE_ACCEPTOR_INCLUDED

#include <string>
#include <boost/function.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
namespace sockets {
    
    class reusable_acceptor {
    public:
        reusable_acceptor(
            boost::asio::io_service& io_service
        );
        template<
            typename handling
        >
        void async_connect(
            boost::asio::ip::tcp::endpoint const& endpoint,
            handling handler
        );
        template<
            typename handling
        >
        void async_accept(
            boost::asio::ip::tcp::socket& connected,
            handling handler
        );
        boost::asio::io_service& get_io_service(
        );
    protected:
        static std::string sharing_socket_name(
            boost::asio::ip::tcp::endpoint const& shared
        );
        void handle_provider_connect(
            boost::system::error_code const& error,
            boost::asio::local::stream_protocol::socket* connecting,
            boost::asio::local::stream_protocol::endpoint const& sharing_endpoin,
            boost::asio::ip::tcp::endpoint const& requested_endpoint,
            boost::function<
                void (
                    boost::system::error_code const& error
                )
            > handler
        );
        void handle_sharing_accept(
            boost::system::error_code const& error,
            boost::asio::local::stream_protocol::socket* accepted
        );
    private:
        boost::asio::io_service& io_service;
        boost::asio::ip::tcp::acceptor* reusable;
        boost::asio::local::stream_protocol::acceptor* sharing;
    };
    
    template<
        typename handling
    >
    void reusable_acceptor::async_connect(
        boost::asio::ip::tcp::endpoint const& endpoint,
        handling handler
    ) {
        boost::asio::local::stream_protocol::endpoint provider(
            sharing_socket_name(
                endpoint
            )
        );
        boost::asio::local::stream_protocol::socket* connecting = new boost::asio::local::stream_protocol::socket(
            io_service
        );
        boost::function<
            void (
                boost::system::error_code const& error
            )
        > stored(
            handler
        );
        connecting->async_connect(
            provider,
            boost::bind(
                &reusable_acceptor::handle_provider_connect,
                this,
                boost::asio::placeholders::error,
                connecting,
                provider,
                endpoint,
                stored
            )
        );
    }
    
    template<
        typename handling
    >
    void reusable_acceptor::async_accept(
        boost::asio::ip::tcp::socket& connected,
        handling handler
    ) {
        reusable->async_accept(
            connected,
            handler
        );
    }
}

#endif
