/*
 Copyright (c) 2007-2010 iMatix Corporation
 
 This file is part of 0MQ.
 
 
 0MQ is free software; you can redistribute it and/or modify it under
 the terms of the Lesser GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.
 
 0MQ is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 Lesser GNU General Public License for more details.
 
 You should have received a copy of the Lesser GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __ZMQ_HPP__NOTHROW_INCLUDED__
#define __ZMQ_HPP__NOTHROW_INCLUDED__

#include "zmq.h"

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

#ifdef	__GNUC__
#  define WARN_UNUSED  __attribute__((warn_unused_result))
#else
#  define WARN_UNUSED
#endif//__GNUC__

namespace zmq {
	namespace nothrow {
		
		typedef zmq_free_fn free_fn;
		typedef zmq_pollitem_t pollitem_t;

		inline int poll (zmq_pollitem_t *items_, int nitems_, long timeout_) WARN_UNUSED;
		inline int device (int device_, void * insocket_, void* outsocket_) WARN_UNUSED;
		
		inline int poll (zmq_pollitem_t *items_, int nitems_, long timeout_ = -1)
		{
			return zmq_poll (items_, nitems_, timeout_);
		}
		
		inline int device (int device_, void * insocket_, void* outsocket_)
		{
			return zmq_device (device_, insocket_, outsocket_);
		}
		
		class message_t : private zmq_msg_t
			{
				friend class socket_t;
				// really want this to be embedded in zmq_msg_t
				int error_;
				
			public:
				
				int error() const 
				{
					return error_;
				}
				
				inline message_t ()	
				: error_(zmq_msg_init(this))
				{
				}
				
				inline message_t(size_t size_) 
				: error_(zmq_msg_init_size (this, size_)) 
				{
				}
				
				inline message_t (void *data_, size_t size_, free_fn *ffn_,
								  void *hint_ = NULL) 
				: error_(zmq_msg_init_data (this, data_, size_, ffn_, hint_))
				{
				}
				
				inline ~message_t ()
				{
					// ignoring error
					zmq_msg_close (this);
				}
				
				inline int rebuild() WARN_UNUSED
				{
					if (! error_) {
						int rc = zmq_msg_close (this);
						if (rc != 0)
							return rc;
					}
					error_ = zmq_msg_init (this);
					return error_;
				}
				
				inline int rebuild (size_t size_) WARN_UNUSED
				{
					if (! error_) {
						int rc = zmq_msg_close (this);
						if (rc != 0)
							return rc;
					}
					error_ = zmq_msg_init_size (this, size_);
					return error_;
				}
				
				inline int rebuild (void *data_, size_t size_, free_fn *ffn_,
									void *hint_ = NULL) WARN_UNUSED
				{
					if (! error_) {
						int rc = zmq_msg_close (this);
						if (rc != 0)
							return rc;
					}
					error_ = zmq_msg_init_data (this, data_, size_, ffn_, hint_);
					return error_;
				}
				
				inline int move (message_t *msg_) WARN_UNUSED
				{
					if (! error_)
						error_ = zmq_msg_move (this, (zmq_msg_t*) msg_);
					return error_;
				}
				
				inline int copy (message_t *msg_) WARN_UNUSED
				{
					if (! error_)
						error_ = zmq_msg_copy (this, (zmq_msg_t*) msg_);
					return error_;
				}
				
				inline void *data () WARN_UNUSED
				{
					if (error_)
						return NULL;
					else
						return zmq_msg_data (this);
				}
				
				inline size_t size () WARN_UNUSED
				{
					if (error_)
						return 0;
					else
						return zmq_msg_size (this);
				}
				
			private:
				
				//  Disable implicit message copying, so that users won't use shared
				//  messages (less efficient) without being aware of the fact.
				message_t (const message_t&);
				void operator = (const message_t&);
			};
		
		class context_t
			{
				friend class socket_t;
				
			public:
				
				inline context_t (int io_threads_)
				: ptr(zmq_init (io_threads_))
				{
				}
				
				bool is_open() const WARN_UNUSED
				{
					return ptr != NULL;
				}
				
				inline ~context_t ()
				{
					if (ptr)
						assert(zmq_term (ptr) == 0);
				}
				
			private:
				
				void *ptr;
				
				context_t (const context_t&);
				void operator = (const context_t&);
			};
		
		class socket_t
			{
			public:
				
				inline socket_t (context_t &context_, int type_)
				: ptr(zmq_socket (context_.ptr, type_))
				{
				}
				
				inline ~socket_t ()
				{
					if (ptr)
						assert(zmq_close (ptr) == 0);
				}
				
				// user must check if ptr is non-null
				inline operator void* () WARN_UNUSED
				{
					return ptr;
				}
				
				inline int setsockopt (int option_, const void *optval_,
										size_t optvallen_) __attribute__((warn_unused_result)) WARN_UNUSED
				{
					assert(ptr != NULL);
					return zmq_setsockopt (ptr, option_, optval_, optvallen_);
				}
				
				inline int getsockopt (int option_, void *optval_,
										size_t *optvallen_) WARN_UNUSED
				{
					assert(ptr != NULL);
					return zmq_getsockopt (ptr, option_, optval_, optvallen_);
				}
				
				inline int bind (const char *addr_) WARN_UNUSED
				{
					return zmq_bind (ptr, addr_);
				}
				
				inline int connect (const char *addr_) WARN_UNUSED
				{
					return zmq_connect (ptr, addr_);
				}
				
				inline int send (message_t &msg_, int flags_ = 0) WARN_UNUSED
				{
					return zmq_send (ptr, &msg_, flags_);
				}
				
				inline int recv (message_t *msg_, int flags_ = 0) WARN_UNUSED
				{
					return zmq_recv (ptr, msg_, flags_);
				}
				
			private:
				
				void *ptr;
				
				socket_t (const socket_t&);
				void operator = (const socket_t&);
			};
		
	}
}

#endif//__ZMQ_HPP__NOTHROW_INCLUDED__
