/*
 * Ibis - IRCv3 Library
 * Copyright (C) 2022-2024 Ibis Developers
 *
 * 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/>.
 */

#include "ibisstandardreply.h"

#include "ibisconstants.h"
#include "ibisstring.h"

struct _IbisStandardReply {
	GObject parent;

	char *reply_type;
	char *command;
	char *code;
	GStrv context;
	char *description;
};

enum {
	PROP_0,
	PROP_REPLY_TYPE,
	PROP_COMMAND,
	PROP_CODE,
	PROP_CONTEXT,
	PROP_DESCRIPTION,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
G_DEFINE_FINAL_TYPE(IbisStandardReply, ibis_standard_reply, G_TYPE_OBJECT)

static void
ibis_standard_reply_finalize(GObject *obj) {
	IbisStandardReply *reply = IBIS_STANDARD_REPLY(obj);

	g_clear_pointer(&reply->reply_type, g_free);
	g_clear_pointer(&reply->command, g_free);
	g_clear_pointer(&reply->code, g_free);
	g_clear_pointer(&reply->context, g_strfreev);
	g_clear_pointer(&reply->description, g_free);

	G_OBJECT_CLASS(ibis_standard_reply_parent_class)->finalize(obj);
}

static void
ibis_standard_reply_get_property(GObject *obj, guint param_id, GValue *value,
                                 GParamSpec *pspec)
{
	IbisStandardReply *reply = IBIS_STANDARD_REPLY(obj);

	switch(param_id) {
	case PROP_REPLY_TYPE:
		g_value_set_string(value, ibis_standard_reply_get_reply_type(reply));
		break;
	case PROP_COMMAND:
		g_value_set_string(value, ibis_standard_reply_get_command(reply));
		break;
	case PROP_CODE:
		g_value_set_string(value, ibis_standard_reply_get_code(reply));
		break;
	case PROP_CONTEXT:
		g_value_set_boxed(value, ibis_standard_reply_get_context(reply));
		break;
	case PROP_DESCRIPTION:
		g_value_set_string(value, ibis_standard_reply_get_description(reply));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
ibis_standard_reply_init(G_GNUC_UNUSED IbisStandardReply *reply) {
}

static void
ibis_standard_reply_class_init(IbisStandardReplyClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = ibis_standard_reply_finalize;
	obj_class->get_property = ibis_standard_reply_get_property;

	/**
	 * IbisStandardReply:reply-type:
	 *
	 * The type of the reply.
	 *
	 * Since: 0.8
	 */
	properties[PROP_REPLY_TYPE] = g_param_spec_string(
		"reply-type", NULL, NULL,
		NULL,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * IbisStandardReply:command:
	 *
	 * The command that this reply is related to.
	 *
	 * Since: 0.8
	 */
	properties[PROP_COMMAND] = g_param_spec_string(
		"command", NULL, NULL,
		NULL,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * IbisStandardReply:code:
	 *
	 * The machine-readable code for this reply.
	 *
	 * Since: 0.8
	 */
	properties[PROP_CODE] = g_param_spec_string(
		"code", NULL, NULL,
		NULL,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * IbisStandardReply:context:
	 *
	 * The context for this reply.
	 *
	 * Since: 0.8
	 */
	properties[PROP_CONTEXT] = g_param_spec_boxed(
		"context", NULL, NULL,
		G_TYPE_STRV,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * IbisStandardReply:description:
	 *
	 * The description of the reply.
	 *
	 * Since: 0.8
	 */
	properties[PROP_DESCRIPTION] = g_param_spec_string(
		"description", NULL, NULL,
		NULL,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
const char *
ibis_standard_reply_get_reply_type(IbisStandardReply *reply) {
	g_return_val_if_fail(IBIS_IS_STANDARD_REPLY(reply), NULL);

	return reply->reply_type;
}

gboolean
ibis_standard_reply_is_type(IbisStandardReply *reply, const char *type) {
	g_return_val_if_fail(IBIS_IS_STANDARD_REPLY(reply), FALSE);

	return ibis_str_equal(reply->reply_type, type);
}

const char *
ibis_standard_reply_get_code(IbisStandardReply *reply) {
	g_return_val_if_fail(IBIS_IS_STANDARD_REPLY(reply), NULL);

	return reply->code;
}

const char *
ibis_standard_reply_get_command(IbisStandardReply *reply) {
	g_return_val_if_fail(IBIS_IS_STANDARD_REPLY(reply), NULL);

	return reply->command;
}

GStrv
ibis_standard_reply_get_context(IbisStandardReply *reply) {
	g_return_val_if_fail(IBIS_IS_STANDARD_REPLY(reply), NULL);

	return reply->context;
}

const char *
ibis_standard_reply_get_description(IbisStandardReply *reply) {
	g_return_val_if_fail(IBIS_IS_STANDARD_REPLY(reply), NULL);

	return reply->description;
}

IbisStandardReply *
ibis_standard_reply_parse(IbisMessage *message) {
	IbisStandardReply *reply = NULL;
	GStrv params = NULL;
	GStrvBuilder *builder = NULL;
	const char *reply_type = NULL;
	guint n_params = 0;

	g_return_val_if_fail(IBIS_IS_MESSAGE(message), NULL);

	if(ibis_message_is_command(message, IBIS_MSG_FAIL)) {
		reply_type = IBIS_STANDARD_REPLY_TYPE_FAIL;
	} else if(ibis_message_is_command(message, IBIS_MSG_NOTE)) {
		reply_type = IBIS_STANDARD_REPLY_TYPE_NOTE;
	} else if(ibis_message_is_command(message, IBIS_MSG_WARN)) {
		reply_type = IBIS_STANDARD_REPLY_TYPE_WARN;
	}

	if(reply_type == NULL) {
		return NULL;
	}

	/* Standard replies have at least 3 parameters. The type is the COMMAND of
	 * the message and not a parameter.
	 */
	params = ibis_message_get_params(message);
	n_params = g_strv_length(params);
	if(n_params < 3) {
		return NULL;
	}

	reply = g_object_new(IBIS_TYPE_STANDARD_REPLY, NULL);

	reply->reply_type = g_strdup(reply_type);
	reply->command = g_strdup(params[0]);
	reply->code = g_strdup(params[1]);
	reply->description = g_strdup(params[n_params - 1]);

	/* If the number of parameters is more than 3, then we have context that
	 * we need to pull out into a new strv.
	 */
	builder = g_strv_builder_new();

	/* Context is everything from parameter 2 to n_params - 1. */
	for(guint i = 2; i < n_params - 1; i++) {
		g_strv_builder_add(builder, params[i]);
	}

	reply->context = g_strv_builder_end(builder);
	g_clear_pointer(&builder, g_strv_builder_unref);

	return reply;
}
