MODULE = ZMQ::Raw               PACKAGE = ZMQ::Raw::Socket

INCLUDE: const-xs-socket_options.inc

SV *
new (class, context, type)
	SV *class
	SV *context
	int type

	PREINIT:
		zmq_raw_socket *sock;
		zmq_raw_context *ctx;

	CODE:
		ctx = ZMQ_SV_TO_PTR (Context, context);
		Newxz (sock, 1, zmq_raw_socket);
		sock->type = type;
		sock->socket = zmq_socket (ctx->context, type);
		sock->context = ctx->context;
		if (sock->socket == NULL)
		{
			Safefree (sock);
			zmq_raw_check_error (-1);
		}

		ZMQ_NEW_OBJ_WITH_MAGIC (RETVAL, SvPVbyte_nolen (class), sock,
			SvRV (context));

	OUTPUT: RETVAL

void
bind (self, endpoint)
	SV *self
	const char *endpoint

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);
		rc = zmq_bind (sock->socket, endpoint);
		zmq_raw_check_error (rc);

void
unbind (self, endpoint)
	SV *self
	const char *endpoint

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);
		rc = zmq_unbind (sock->socket, endpoint);
		zmq_raw_check_error (rc);

void
connect (self, endpoint)
	SV *self
	const char *endpoint

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);
		rc = zmq_connect (sock->socket, endpoint);
		zmq_raw_check_error (rc);

void
disconnect (self, endpoint)
	SV *self
	const char *endpoint

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);
		rc = zmq_disconnect (sock->socket, endpoint);
		zmq_raw_check_error (rc);

void
send (self, buffer, flags=0)
	SV *self
	SV *buffer
	int flags

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	PPCODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);
		rc = zmq_send (sock->socket,
			SvPVX (buffer), SvCUR (buffer), flags);
		zmq_raw_check_error (rc);
		XSRETURN_YES;

void
sendmsg (self, ...)
	SV *self

	PREINIT:
		int rc, i;
		int flags = 0, count = 0;
		zmq_raw_socket *sock;

	PPCODE:
		if (items < 2)
			croak_usage ("not enough parameters provided");
		count = items-1;

		if (!sv_isobject (ST (items-1)) && SvIOK (ST (items-1)))
		{
			/* the last parameter looks like 'flags'. if all the preceding
			 * parameters are not ZMQ::Raw::Message objects, it is not
			 * 'flags'
			 */
			int is_flags = items-2 ? 1 : 0;
			for (i = 0; i < items-2; ++i && is_flags)
			{
				SV *item = ST (i+1);
				if (sv_isobject (item) && sv_derived_from (item, "ZMQ::Raw::Message"))
					continue;

				is_flags = 0;
			}

			if (is_flags)
			{
				flags = (int)SvIV (ST (items-1));
				--count;
			}
		}

		sock = ZMQ_SV_TO_PTR (Socket, self);
		for (i = 0; i < items && count; ++i)
		{
			zmq_msg_t msg;
			int extra = 0;
			SV *item = ST (i+1);

			if (--count > 0)
				extra = ZMQ_SNDMORE;

			if (sv_isobject (item) && sv_derived_from (item, "ZMQ::Raw::Message"))
			{
				rc = zmq_msg_init (&msg);
				zmq_raw_check_error (rc);

				rc = zmq_msg_copy (&msg, ZMQ_SV_TO_PTR (Message, item));
				if (rc < 0)
					zmq_msg_close (&msg);
				zmq_raw_check_error (rc);

	SENDMSG:
				rc = zmq_sendmsg (sock->socket, &msg, flags | extra);
				if (rc < 0)
					zmq_msg_close (&msg);
				zmq_raw_check_error (rc);

				rc = zmq_msg_close (&msg);
				zmq_raw_check_error (rc);
			}
			else
			{
				STRLEN size;
				const char *b = SvPV (item, size);

				rc = zmq_msg_init_size (&msg, size);
				zmq_raw_check_error (rc);

				Copy (b, zmq_msg_data (&msg), size, char);
				goto SENDMSG;
			}
		}

		XSRETURN_YES;

void
recv (self, flags=0)
	SV *self
	int flags

	PREINIT:
		int rc, ctx;
		int count = 0, more = 1;
		zmq_msg_t msg;
		zmq_raw_socket *sock;

	PPCODE:
		ctx = GIMME_V;

		sock = ZMQ_SV_TO_PTR (Socket, self);

		rc = zmq_msg_init (&msg);
		zmq_raw_check_error (rc);

		SV *buffer = sv_2mortal (newSV (16));
		SvPOK_on (buffer);
		SvCUR_set (buffer, 0);

		do
		{
			rc = zmq_recvmsg (sock->socket, &msg,
				flags);

			if (rc < 0)
				zmq_msg_close (&msg);
			zmq_raw_check_error (rc);

			sv_catpvn (buffer, zmq_msg_data (&msg), zmq_msg_size (&msg));

			more = zmq_msg_get (&msg, ZMQ_MORE);

			if (ctx == G_ARRAY)
			{
				++count;
				XPUSHs (buffer);
				buffer = NULL;

				if (more)
				{
					buffer = sv_2mortal (newSV (16));
					SvPOK_on (buffer);
					SvCUR_set (buffer, 0);
				}
			}
		}
		while (more);

		rc = zmq_msg_close (&msg);
		zmq_raw_check_error (rc);

		if (buffer)
		{
			++count;
			XPUSHs (buffer);
		}

		XSRETURN (count);

void
recvmsg (self, flags=0)
	SV *self
	int flags

	PREINIT:
		int rc, ctx;
		int count = 0, more = 1;
		zmq_msg_t msg;
		zmq_raw_socket *sock;

	PPCODE:
		ctx = GIMME_V;

		sock = ZMQ_SV_TO_PTR (Socket, self);

		rc = zmq_msg_init (&msg);
		zmq_raw_check_error (rc);

		while (more)
		{
			rc = zmq_recvmsg (sock->socket, &msg, flags);
			if (rc < 0)
				zmq_msg_close (&msg);

			zmq_raw_check_error (rc);
			++count;

			{
				zmq_msg_t *obj;
				Newxz (obj, 1, zmq_msg_t);
				rc = zmq_msg_init (obj);
				zmq_raw_check_error (rc);

				rc = zmq_msg_copy (obj, &msg);
				zmq_raw_check_error (rc);

				SV *m;
				ZMQ_NEW_OBJ (m, "ZMQ::Raw::Message", obj);
				mXPUSHs (m);
			}

			more = zmq_msg_get (&msg, ZMQ_MORE);

			if (ctx != G_ARRAY)
				more = 0;
		}

		rc = zmq_msg_close (&msg);
		zmq_raw_check_error (rc);

		XSRETURN (count);

void
setsockopt (self, option, value)
	SV *self
	int option
	SV *value

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);

		switch (option)
		{
			// int
			case ZMQ_BACKLOG:
			case ZMQ_CONFLATE:
			case ZMQ_CONNECT_TIMEOUT:
			case ZMQ_HANDSHAKE_IVL:
			case ZMQ_HEARTBEAT_IVL:
			case ZMQ_HEARTBEAT_TIMEOUT:
			case ZMQ_HEARTBEAT_TTL:
			case ZMQ_IMMEDIATE:
			case ZMQ_INVERT_MATCHING:
			case ZMQ_IPV6:
			case ZMQ_LINGER:
			case ZMQ_MULTICAST_HOPS:
			case ZMQ_MULTICAST_MAXTPDU:
			case ZMQ_PLAIN_SERVER:
			case ZMQ_USE_FD:
			case ZMQ_PROBE_ROUTER:
			case ZMQ_RATE:
			case ZMQ_RCVBUF:
			case ZMQ_RCVHWM:
			case ZMQ_RCVTIMEO:
			case ZMQ_RECONNECT_IVL:
			case ZMQ_RECONNECT_IVL_MAX:
			case ZMQ_RECOVERY_IVL:
			case ZMQ_REQ_CORRELATE:
			case ZMQ_REQ_RELAXED:
			case ZMQ_ROUTER_HANDOVER:
			case ZMQ_ROUTER_MANDATORY:
			case ZMQ_ROUTER_RAW:
			case ZMQ_ROUTER_NOTIFY:
			case ZMQ_SNDBUF:
			case ZMQ_SNDHWM:
			case ZMQ_SNDTIMEO:
			case ZMQ_STREAM_NOTIFY:
			case ZMQ_TCP_KEEPALIVE:
			case ZMQ_TCP_KEEPALIVE_CNT:
			case ZMQ_TCP_KEEPALIVE_IDLE:
			case ZMQ_TCP_KEEPALIVE_INTVL:
			case ZMQ_TCP_MAXRT:
			case ZMQ_TOS:
			case ZMQ_XPUB_VERBOSE:
			case ZMQ_XPUB_VERBOSER:
			case ZMQ_XPUB_MANUAL:
			case ZMQ_XPUB_NODROP:
			case ZMQ_CURVE_SERVER:
				{
					int v;
					if (!SvIOK (value))
						croak_usage ("Value is not an int");

					v = (int)SvIV (value);
					rc = zmq_setsockopt (sock->socket, option,
						&v, sizeof (v));
					zmq_raw_check_error (rc);
				}
				break;

			// int64
			case ZMQ_MAXMSGSIZE:
				{
					int64_t v;
					if (!SvIOK (value))
						croak_usage ("Value is not an int");

					v = SvIV (value);
					rc = zmq_setsockopt (sock->socket, option,
						&v, sizeof (v));
					zmq_raw_check_error (rc);
				}
				break;

			// binary
			case ZMQ_CONNECT_RID:
			case ZMQ_ROUTING_ID:
			case ZMQ_PLAIN_PASSWORD:
			case ZMQ_PLAIN_USERNAME:
			case ZMQ_SOCKS_PROXY:
			case ZMQ_SUBSCRIBE:
			case ZMQ_UNSUBSCRIBE:
			case ZMQ_XPUB_WELCOME_MSG:
			case ZMQ_ZAP_DOMAIN:
			case ZMQ_TCP_ACCEPT_FILTER:
			case ZMQ_CURVE_SECRETKEY:
			case ZMQ_CURVE_PUBLICKEY:
			case ZMQ_CURVE_SERVERKEY:
				{
					STRLEN len;
					char *buf;

					if (!SvPOK (value))
						croak_usage ("Value is not a string");

					buf = SvPV (value, len);
					rc = zmq_setsockopt (sock->socket, option,
						buf, len);
					zmq_raw_check_error (rc);
				}
				break;

			default:
				croak_usage ("Unsupported option");
		}

void
close (self)
	SV *self

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);

		if (sock->context == NULL)
			croak_usage ("close() cannot be called on this socket");

		rc = zmq_close (sock->socket);
		zmq_raw_check_error (rc);

		sock->socket = zmq_socket (sock->context, sock->type);
		if (sock->socket == NULL)
			zmq_raw_check_error (-1);

void
monitor (self, endpoint, events)
	SV *self
	const char *endpoint
	int events

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);

		rc = zmq_socket_monitor (sock->socket, endpoint, events);
		zmq_raw_check_error (rc);

void
join (self, group)
	SV *self
	const char *group

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);

		rc = zmq_join (sock->socket, group);
		zmq_raw_check_error (rc);

void
leave (self, group)
	SV *self
	const char *group

	PREINIT:
		int rc;
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);

		rc = zmq_leave (sock->socket, group);
		zmq_raw_check_error (rc);

void
DESTROY(self)
	SV *self

	PREINIT:
		zmq_raw_socket *sock;

	CODE:
		sock = ZMQ_SV_TO_PTR (Socket, self);
		if (sock->socket)
			zmq_close (sock->socket);
		Safefree (sock);
		SvREFCNT_dec (ZMQ_SV_TO_MAGIC (self));