/*
 * Ibis - IRCv3 Library
 * Copyright (C) 2022-2024 Ibis Developers <devel@pidgin.im>
 *
 * Ibis is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This library 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#if !defined(IBIS_GLOBAL_HEADER_INSIDE) && !defined(IBIS_COMPILATION)
# error "only <ibis.h> may be included directly"
#endif

#ifndef IBIS_CLIENT_H
#define IBIS_CLIENT_H

#include <glib.h>
#include <glib-object.h>

#include <gio/gio.h>

#include <hasl.h>

#include "ibisfeatures.h"
#include "ibismessage.h"
#include "ibisversion.h"

G_BEGIN_DECLS

/**
 * IBIS_CLIENT_ERROR:
 *
 * A #GError domain for client errors.
 *
 * Since: 0.1
 */
#define IBIS_CLIENT_ERROR \
	(g_quark_from_static_string("ibis-client")) \
	IBIS_AVAILABLE_MACRO_IN_0_1

#define IBIS_TYPE_CLIENT (ibis_client_get_type())

/**
 * IbisClient:
 *
 * A representation of an IRCv3 client.
 *
 * The client has a bit of functionality built-in but there are some cases
 * where you may have to let the default handler run if you are listening to
 * the generic [signal@Client::message] signal.
 *
 * The client uses the [const@Ibis.RPL_WELCOME] message to update
 * [property@Client:active-nick]. This means your handler will need to return
 * %FALSE to let the default handler implement this feature.
 *
 * Likewise, the client also uses [const@Ibis.MSG_NICK] to update the
 * [property@Client:active-nick] property if the nick name of the user has been
 * changed via a bouncer or just via a `NICK` command. So again, your handler
 * will need to return %FALSE to let the default handler implement this
 * feature.
 *
 * Since: 0.1
 */

IBIS_AVAILABLE_IN_0_1
G_DECLARE_FINAL_TYPE(IbisClient, ibis_client, IBIS, CLIENT, GObject)

#include "ibiscapabilities.h"

/**
 * ibis_client_connect:
 * @hostname: the hostname or IP address to connect to
 * @port: the port to connect to
 * @password: (nullable): the optional server password to use
 * @tls: if true use a TLS connection
 * @cancellable: (transfer none) (nullable): a [class@Gio.Cancellable]
 * @proxy_resolver: (transfer none) (nullable): a [iface@Gio.ProxyResolver]
 *
 * Attempts to connect to @hostname on @port.
 *
 * If @password is provided, it will be deleted immediately after use.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_connect(IbisClient *client, const char *hostname, guint16 port, const char *password, gboolean tls, GCancellable *cancellable, GProxyResolver *proxy_resolver);

/**
 * ibis_client_disconnect:
 * @error: (transfer full) (nullable): a #GError
 * @reason: (nullable): an optional disconnect reason
 *
 * Disconnects @client.
 *
 * This will send [const@MSG_QUIT] with @message and then call
 * [method@Client.stop].
 *
 * If @error is not %NULL, it will be assigned to [property@Client:error].
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_disconnect(IbisClient *client, GError *error, const char *reason);

/**
 * ibis_client_get_active_nick:
 *
 * Gets the active nick for the client.
 *
 * The active nick is the nick that the server has accepted.
 *
 * Returns: (nullable): The active nick if it is known.
 *
 * Since: 0.11
 */
IBIS_AVAILABLE_IN_0_11
const char *ibis_client_get_active_nick(IbisClient *client);

/**
 * ibis_client_get_alt_nick:
 *
 * Gets the alternative nick to use in the event that [property@Client:nick] is
 * already in use.
 *
 * Returns: (nullable): The alternative nick.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
const char *ibis_client_get_alt_nick(IbisClient *client);

/**
 * ibis_client_set_alt_nick:
 * @alt_nick: (nullable): the new alternative nick
 *
 * Sets the alternative nick of @client to @alt_nick.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_set_alt_nick(IbisClient *client, const char *alt_nick);

/**
 * ibis_client_get_cancellable:
 *
 * Gets the [class@Gio.Cancellable] for @client.
 *
 * Returns: (transfer none): The cancellable.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
GCancellable *ibis_client_get_cancellable(IbisClient *client);

/**
 * ibis_client_get_capabilities:
 *
 * Gets the [class@Capabilities] from @client.
 *
 * Returns: (transfer none): The capabilities.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
IbisCapabilities *ibis_client_get_capabilities(IbisClient *client);

/**
 * ibis_client_get_connected:
 *
 * Gets whether or not @client is currently connected.
 *
 * Returns: true if connected; false otherwise.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
gboolean ibis_client_get_connected(IbisClient *client);

/**
 * ibis_client_get_error:
 *
 * Gets the #GError that client encountered.
 *
 * Returns: (transfer none) (nullable): The error if one has been encountered.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
GError *ibis_client_get_error(IbisClient *client);

/**
 * ibis_client_get_features:
 *
 * Gets the [class@Ibis.Features] from @client.
 *
 * Returns: (transfer none) (nullable): The features object if @client is
 *          connected.
 *
 * Since: 0.4
 */
IBIS_AVAILABLE_IN_0_4
IbisFeatures *ibis_client_get_features(IbisClient *client);

/**
 * ibis_client_get_hasl_context:
 *
 * Gets the [class@Hasl.Context] from @client.
 *
 * Returns: (nullable) (transfer none): The Hasl context.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
HaslContext *ibis_client_get_hasl_context(IbisClient *client);

/**
 * ibis_client_set_hasl_context:
 * @hasl_context: (nullable) (transfer none): the new context to set
 *
 * Sets the [class@Hasl.Context] for @client to use.
 *
 * You should avoid switching this out during registration. If you want to
 * remove this from memory you should connect to the `notify::connected` signal
 * and set it to %NULL from that handler.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_set_hasl_context(IbisClient *client, HaslContext *hasl_context);

/**
 * ibis_client_get_network:
 *
 * Gets the network that the client is connected to.
 *
 * Returns: (nullable): The network name if set.
 *
 * Since: 0.4
 */
IBIS_AVAILABLE_IN_0_4
const char *ibis_client_get_network(IbisClient *client);

/**
 * ibis_client_get_nick:
 *
 * Gets the nick from @client.
 *
 * Returns: The nick.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
const char *ibis_client_get_nick(IbisClient *client);

/**
 * ibis_client_set_nick:
 * @nick: the new nick
 *
 * Sets the nick of @client to @nick.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_set_nick(IbisClient *client, const char *nick);

/**
 * ibis_client_get_realname:
 *
 * Gets the realname that is being used when sending the `USER` command.
 *
 * Before version 0.11.1 this used to fall back to [property@Client:nick] if it
 * was unset. Since then it only returns the actual value of
 * [property@Client:realname].
 *
 * Returns: The realname.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
const char *ibis_client_get_realname(IbisClient *client);

/**
 * ibis_client_set_realname:
 * @realname: (nullable): the new realname
 *
 * Sets the realname to use when sending the `USER` command.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_set_realname(IbisClient *client, const char *realname);

/**
 * ibis_client_get_registered:
 *
 * Checks whether the client has completed user registration.
 *
 * User registration is the first thing that happens while connecting and other
 * commands like JOIN, PRIVMSG, etc can not be sent until registration is
 * completed.
 *
 * Returns: true if registration has been completed; false otherwise.
 *
 * Since: 0.10
 */
IBIS_AVAILABLE_IN_0_10
gboolean ibis_client_get_registered(IbisClient *client);

/**
 * ibis_client_get_source_prefix:
 * @source: the source
 *
 * Gets the Channel Membership Prefix from @source according to the prefixes
 * that the server told us in [const@RPL_ISUPPORT].
 *
 * Returns: (transfer full) (nullable): The prefix if found.
 *
 * Since: 0.8
 */
IBIS_AVAILABLE_IN_0_8
char *ibis_client_get_source_prefix(IbisClient *client, const char *source);

/**
 * ibis_client_get_username:
 *
 * Gets the username that is being used when sending the `USER` command.
 *
 * Before version 0.11.1 this used to fall back to [property@Client:nick] if it
 * was unset. Since then it only returns the actual value of
 * [property@Client:username].
 *
 * Returns: The username.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
const char *ibis_client_get_username(IbisClient *client);

/**
 * ibis_client_set_username:
 * @username: (nullable): the new username
 *
 * Sets the username to use when sending the `USER` command.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_set_username(IbisClient *client, const char *username);

/**
 * ibis_client_is_channel:
 * @target: the target
 *
 * Checks if @target is a channel.
 *
 * This uses [const@FEATURE_CHANTYPES] from the result of [const@RPL_ISUPPORT]
 * to determine if target starts with a valid channel character.
 *
 * Returns: true if @target starts with a valid channel character; false
 *          otherwise.
 *
 * Since: 0.4
 */
IBIS_AVAILABLE_IN_0_4
gboolean ibis_client_is_channel(IbisClient *client, const char *target);

/**
 * ibis_client_new:
 *
 * Creates a new client.
 *
 * Returns: (transfer full): The new client.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
IbisClient *ibis_client_new(void);

/**
 * ibis_client_normalize:
 * @input: (nullable): the input to normalize
 *
 * Normalizes @input depending on the [const@FEATURE_CASEMAPPING] feature.
 *
 * Available casemappings: *ascii*, *rfc1459*, and *rfc1459-strict* since 0.7.
 *
 * Returns: (transfer full): A normalized copy of @input.
 *
 * Since: 0.7
 */
IBIS_AVAILABLE_IN_0_7
char *ibis_client_normalize(IbisClient *client, const char *input);

/**
 * ibis_client_start:
 * @stream: the [class@Gio.IOStream] to process
 * @password: (nullable): the optional server password to use
 * @cancellable: (transfer none) (nullable): a [class@Gio.Cancellable] to use
 *
 * Starts @client.
 *
 * Starting the @client means that it should start processing @stream.
 *
 * If @password is provided, it will be used during registration and then
 * immediately cleared.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_start(IbisClient *client, GIOStream *stream, const char *password, GCancellable *cancellable);

/**
 * ibis_client_stop:
 * @error: (transfer full) (nullable): a #GError
 *
 * Stops @client.
 *
 * This will close all of the streams and reset everything to its pre-connected
 * state.
 *
 * If @error is not %NULL, it will be assigned to [property@Client:error].
 *
 * Since: 0.10
 */
IBIS_AVAILABLE_IN_0_10
void ibis_client_stop(IbisClient *client, GError *error);

/**
 * ibis_client_strip_source_prefix:
 * @source: the source
 *
 * Strips the Channel Membership Prefix from @source according to the prefixes
 * that the server told us in [const@RPL_ISUPPORT].
 *
 * Returns: (transfer full) (nullable): The source with any prefixes removed.
 *
 * Since: 0.8
 */
IBIS_AVAILABLE_IN_0_8
char *ibis_client_strip_source_prefix(IbisClient *client, const char *source);

/**
 * ibis_client_write:
 * @message: (transfer full): the message to write
 *
 * Writes @message to the server.
 *
 * Since: 0.1
 */
IBIS_AVAILABLE_IN_0_1
void ibis_client_write(IbisClient *client, IbisMessage *message);

G_END_DECLS

#endif /* IBIS_CLIENT_H */
