/* {{{ Mosquitto\Client::connect() */
PHP_METHOD(Mosquitto_Client, connect)
{
	mosquitto_client_object *object;
	char *host = NULL, *interface = NULL;
	int host_len, interface_len, retval;
	long port = 1883;
	long keepalive = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lls!",
				&host, &host_len, &port, &keepalive,
				&interface, &interface_len)  == FAILURE) {

		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);

	if (interface == NULL) {
		retval = mosquitto_connect(object->client, host, port, keepalive);
	} else {
		retval = mosquitto_connect_bind(object->client, host, port, keepalive, interface);
	}

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
	RETURN_LONG(retval);
}
/* {{{ Mosquitto\Client::setTlsOptions() */
PHP_METHOD(Mosquitto_Client, setTlsOptions)
{
	mosquitto_client_object *object;
	char *tls_version = NULL, *ciphers = NULL;
	int tls_version_len = 0, ciphers_len = 0, retval = 0;
	int verify_peer = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s!s!",
				&verify_peer,
				&tls_version, &tls_version_len,
				&ciphers, &ciphers_len
				) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC);

	retval = mosquitto_tls_opts_set(object->client, verify_peer, tls_version, ciphers);

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
	RETURN_LONG(retval);
}
/* {{{ Mosquitto\Client::onPublish() */
PHP_METHOD(Mosquitto_Client, onPublish)
{
	mosquitto_client_object *object;
	zend_fcall_info publish_callback = empty_fcall_info;
	zend_fcall_info_cache publish_callback_cache = empty_fcall_info_cache;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!",
				&publish_callback, &publish_callback_cache)  == FAILURE) {

		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);

	if (!ZEND_FCI_INITIALIZED(publish_callback)) {
		zend_throw_exception(mosquitto_ce_exception, "Need a valid callback", 0 TSRMLS_CC);
	}

	object->publish_callback = publish_callback;
	object->publish_callback_cache = publish_callback_cache;
	Z_ADDREF_P(publish_callback.function_name);

	if (publish_callback.object_ptr != NULL) {
		Z_ADDREF_P(publish_callback.object_ptr);
	}

	mosquitto_publish_callback_set(object->client, php_mosquitto_publish_callback);
}
/* {{{ Mosquitto\Message::tokeniseTopic() */
PHP_METHOD(Mosquitto_Message, tokeniseTopic)
{
	char *topic = NULL, **topics = NULL;
	int topic_len = 0, retval = 0, count = 0, i = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &topic, &topic_len) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	retval = mosquitto_sub_topic_tokenise(topic, &topics, &count);

	if (retval == MOSQ_ERR_NOMEM) {
		zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to tokenise topic");
		return;
	}

	array_init(return_value);
	for (i = 0; i < count; i++) {
		if (topics[i] == NULL) {
			add_next_index_null(return_value);
		} else {
			add_next_index_string(return_value, topics[i], 1);
		}
	}

	mosquitto_sub_topic_tokens_free(&topics, count);
}
/* {{{ Mosquitto\Client::loopForever() */
PHP_METHOD(Mosquitto_Client, loopForever)
{
	mosquitto_client_object *object;
	long timeout = 1000, max_packets = 1, retval = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll",
				&timeout, &max_packets) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	object->looping = 1;

	while (object->looping) {
		retval = mosquitto_loop(object->client, timeout, max_packets);
		php_mosquitto_handle_errno(retval, errno TSRMLS_CC);

		if (EG(exception)) {
			break;
		}
	}
}
/* {{{ Mosquitto\Client::__construct() */
PHP_METHOD(Mosquitto_Client, __construct)
{
	mosquitto_client_object *object;
	char *id = NULL;
	int id_len = 0;
	zend_bool clean_session = 1;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!b", &id, &id_len, &clean_session) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
	object->client = mosquitto_new(id, clean_session, object);

	if (!object->client) {
		char *message = php_mosquitto_strerror_wrapper(errno);
		zend_throw_exception(mosquitto_ce_exception, message, 1 TSRMLS_CC);
#ifndef STRERROR_R_CHAR_P
		if (message != NULL) {
			efree(message);
		}
#endif
	}
}
PHP_METHOD(Mosquitto_Message, __construct)
{
	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters_none() == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();
}
PHP_METHOD(Mosquitto_Message, __construct)
{
	mosquitto_message_object *object;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters_none() == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_message_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
}
/* {{{ Mosquitto\Client::exitLoop() */
PHP_METHOD(Mosquitto_Client, exitLoop)
{
	mosquitto_client_object *object;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters_none() == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	php_mosquitto_exit_loop(object);
}
/* {{{ Mosquitto\Client::getSocket() */
PHP_METHOD(Mosquitto_Client, getSocket)
{
	mosquitto_client_object *object;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters_none()  == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	RETURN_LONG(mosquitto_socket(object->client));
}
/* {{{ Mosquitto\Client::setTlsCertificates() */
PHP_METHOD(Mosquitto_Client, setTlsCertificates)
{
	mosquitto_client_object *object;
	char *ca_path = NULL, *cert_path = NULL, *key_path = NULL, *key_pw = NULL;
	int ca_path_len = 0, cert_path_len = 0, key_path_len = 0, key_pw_len, retval = 0;
	zval stat;
	zend_bool is_dir = 0;
	int (*pw_callback)(char *, int, int, void *) = NULL;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!|s!s!s!",
				&ca_path, &ca_path_len,
				&cert_path, &cert_path_len,
				&key_path, &key_path_len,
				&key_pw, &key_pw_len) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}

	if ((php_check_open_basedir(ca_path TSRMLS_CC) < 0) ||
		(php_check_open_basedir(cert_path TSRMLS_CC) < 0) ||
		(php_check_open_basedir(key_path TSRMLS_CC) < 0))
	{
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}

	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC);

	php_stat(ca_path, ca_path_len, FS_IS_DIR, &stat TSRMLS_CC);
	is_dir = Z_BVAL(stat);

	if (key_pw != NULL) {
		pw_callback = php_mosquitto_pw_callback;
		MQTTG(client_key) = estrdup(key_pw);
		MQTTG(client_key_len) = key_pw_len;
	}

	if (is_dir) {
		retval = mosquitto_tls_set(object->client, NULL, ca_path, cert_path, key_path, pw_callback);
	} else {
		retval = mosquitto_tls_set(object->client, ca_path, NULL, cert_path, key_path, pw_callback);
	}

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
	RETURN_LONG(retval);
}
/* {{{ Mosquitto\Client::setMessageRetry() */
PHP_METHOD(Mosquitto_Client, setMessageRetry)
{
	mosquitto_client_object *object;
	long retry = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &retry)  == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	mosquitto_message_retry_set(object->client, retry);
}
/* {{{ Mosquitto\Client::clearWill() */
PHP_METHOD(Mosquitto_Client, clearWill)
{
	mosquitto_client_object *object;
	int retval;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters_none() == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	retval = mosquitto_will_clear(object->client);

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
}
/* {{{ Mosquitto\Message::topicMatchesSub() */
PHP_METHOD(Mosquitto_Message, topicMatchesSub)
{
	char *topic = NULL, *subscription = NULL;
	int topic_len, subscription_len;
	zend_bool result;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
				&topic, &topic_len, &subscription, &subscription_len) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	mosquitto_topic_matches_sub(subscription, topic, (bool *) &result);
	RETURN_BOOL(result);
}
/* {{{ Mosquitto\Client::setCredentials() */
PHP_METHOD(Mosquitto_Client, setCredentials)
{
	mosquitto_client_object *object;
	char *username = NULL, *password = NULL;
	int username_len, password_len, retval;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &username, &username_len, &password, &password_len) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	retval = mosquitto_username_pw_set(object->client, username, password);
	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
}
/* {{{ Mosquitto\Client::setMaxInFlightMessages() */
PHP_METHOD(Mosquitto_Client, setMaxInFlightMessages)
{
	mosquitto_client_object *object;
	int retval;
	long max = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &max)  == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	retval = mosquitto_max_inflight_messages_set(object->client, max);

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
}
/* {{{ Mosquitto\Client::setTlsInsecure() */
PHP_METHOD(Mosquitto_Client, setTlsInsecure)
{
	mosquitto_client_object *object;
	zend_bool value = 0;
	int retval = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &value) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC);

	retval = mosquitto_tls_insecure_set(object->client, value);

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
}
/* {{{ Mosquitto\Client::setReconnectDelay() */
PHP_METHOD(Mosquitto_Client, setReconnectDelay)
{
	mosquitto_client_object *object;
	int retval;
	long reconnect_delay = 0, reconnect_delay_max = 0;
	zend_bool exponential_backoff = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|lb",
				&reconnect_delay, &reconnect_delay_max, &exponential_backoff)  == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	retval = mosquitto_reconnect_delay_set(object->client, reconnect_delay, reconnect_delay_max, exponential_backoff);

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
}
/* {{{ Mosquitto\Client::unsubscribe() */
PHP_METHOD(Mosquitto_Client, unsubscribe)
{
	mosquitto_client_object *object;
	char *sub;
	int sub_len, retval, mid;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
				&sub, &sub_len) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	retval = mosquitto_unsubscribe(object->client, &mid, sub);

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);

	RETURN_LONG(mid);
}
/* {{{ Mosquitto\Client::setWill() */
PHP_METHOD(Mosquitto_Client, setWill)
{
	mosquitto_client_object *object;
	int topic_len, payload_len, retval;
	long qos;
	zend_bool retain;
	char *topic, *payload;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sslb",
				&topic, &topic_len, &payload, &payload_len, &qos, &retain) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) mosquitto_client_object_get(getThis() TSRMLS_CC);
	retval = mosquitto_will_set(object->client, topic, payload_len, (void *) payload, qos, retain);

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
}
/* {{{ Mosquitto\Client::setTlsPSK() */
PHP_METHOD(Mosquitto_Client, setTlsPSK)
{
	mosquitto_client_object *object;
	char *psk = NULL, *identity = NULL, *ciphers = NULL;
	int psk_len = 0, identity_len = 0, ciphers_len = 0, retval = 0;

	PHP_MOSQUITTO_ERROR_HANDLING();
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!s!|s!",
				&psk, &psk_len, &identity, &identity_len, &ciphers, &ciphers_len
				) == FAILURE) {
		PHP_MOSQUITTO_RESTORE_ERRORS();
		return;
	}
	PHP_MOSQUITTO_RESTORE_ERRORS();

	object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC);

	retval = mosquitto_tls_psk_set(object->client, psk, identity, ciphers);

	php_mosquitto_handle_errno(retval, errno TSRMLS_CC);
	RETURN_LONG(retval);
}