/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

/**
 * SECTION:as-agreement
 * @short_description: Object representing a privacy policy
 * @include: appstream-glib.h
 * @stability: Unstable
 *
 * Agreements can be used by components to specify GDPR, EULA or other warnings.
 *
 * See also: #AsAgreementDetail, #AsAgreementSection
 */

#include "config.h"

#include "as-node-private.h"
#include "as-agreement-private.h"
#include "as-agreement-section-private.h"
#include "as-ref-string.h"
#include "as-tag.h"

typedef struct {
	AsAgreementKind		 kind;
	AsRefString		*version_id;
	GPtrArray		*sections;
} AsAgreementPrivate;

G_DEFINE_TYPE_WITH_PRIVATE (AsAgreement, as_agreement, G_TYPE_OBJECT)

#define GET_PRIVATE(o) (as_agreement_get_instance_private (o))

static void
as_agreement_finalize (GObject *object)
{
	AsAgreement *agreement = AS_AGREEMENT (object);
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);

	if (priv->version_id != NULL)
		as_ref_string_unref (priv->version_id);
	g_ptr_array_unref (priv->sections);

	G_OBJECT_CLASS (as_agreement_parent_class)->finalize (object);
}

static void
as_agreement_init (AsAgreement *agreement)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	priv->sections = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
}

static void
as_agreement_class_init (AsAgreementClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	object_class->finalize = as_agreement_finalize;
}

/**
 * as_agreement_kind_to_string:
 * @value: the #AsAgreementKind.
 *
 * Converts the enumerated value to an text representation.
 *
 * Returns: string version of @value
 *
 * Since: 0.7.8
 **/
const gchar *
as_agreement_kind_to_string (AsAgreementKind value)
{
	if (value == AS_AGREEMENT_KIND_GENERIC)
		return "generic";
	if (value == AS_AGREEMENT_KIND_EULA)
		return "eula";
	if (value == AS_AGREEMENT_KIND_PRIVACY)
		return "privacy";
	return "unknown";
}

/**
 * as_agreement_kind_from_string:
 * @value: the string.
 *
 * Converts the text representation to an enumerated value.
 *
 * Returns: a #AsAgreementKind or %AS_AGREEMENT_KIND_UNKNOWN for unknown
 *
 * Since: 0.7.8
 **/
AsAgreementKind
as_agreement_kind_from_string (const gchar *value)
{
	if (g_strcmp0 (value, "generic") == 0)
		return AS_AGREEMENT_KIND_GENERIC;
	if (g_strcmp0 (value, "eula") == 0)
		return AS_AGREEMENT_KIND_EULA;
	if (g_strcmp0 (value, "privacy") == 0)
		return AS_AGREEMENT_KIND_PRIVACY;
	return AS_AGREEMENT_KIND_UNKNOWN;
}

/**
 * as_agreement_get_kind:
 * @agreement: a #AsAgreement instance.
 *
 * Gets the agreement kind.
 *
 * Returns: a string, e.g. %AS_AGREEMENT_KIND_EULA
 *
 * Since: 0.7.8
 **/
AsAgreementKind
as_agreement_get_kind (AsAgreement *agreement)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	g_return_val_if_fail (AS_IS_AGREEMENT (agreement), AS_AGREEMENT_KIND_UNKNOWN);
	return priv->kind;
}

/**
 * as_agreement_set_kind:
 * @agreement: a #AsAgreement instance.
 * @kind: the agreement kind, e.g. %AS_AGREEMENT_KIND_EULA
 *
 * Sets the agreement kind.
 *
 * Since: 0.7.8
 **/
void
as_agreement_set_kind (AsAgreement *agreement, AsAgreementKind kind)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	g_return_if_fail (AS_IS_AGREEMENT (agreement));
	priv->kind = kind;
}

/**
 * as_agreement_get_version_id:
 * @agreement: a #AsAgreement instance.
 *
 * Gets the agreement version_id.
 *
 * Returns: a string, e.g. "1.4a", or NULL
 *
 * Since: 0.7.8
 **/
const gchar *
as_agreement_get_version_id (AsAgreement *agreement)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	g_return_val_if_fail (AS_IS_AGREEMENT (agreement), NULL);
	return priv->version_id;
}

/**
 * as_agreement_set_version_id:
 * @agreement: a #AsAgreement instance.
 * @version_id: the agreement version ID, e.g. "1.4a"
 *
 * Sets the agreement version identifier.
 *
 * Since: 0.7.8
 **/
void
as_agreement_set_version_id (AsAgreement *agreement, const gchar *version_id)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	g_return_if_fail (AS_IS_AGREEMENT (agreement));
	as_ref_string_assign_safe (&priv->version_id, version_id);
}

/**
 * as_agreement_get_sections:
 * @agreement: a #AsAgreement instance.
 *
 * Gets all the sections in the agreement.
 *
 * Returns: (transfer container) (element-type AsAgreementSection): array
 *
 * Since: 0.7.8
 **/
GPtrArray *
as_agreement_get_sections (AsAgreement *agreement)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	g_return_val_if_fail (AS_IS_AGREEMENT (agreement), NULL);
	return priv->sections;
}

/**
 * as_agreement_get_section_default:
 * @agreement: a #AsAgreement instance.
 *
 * Gets the first section in the agreement.
 *
 * Returns: (transfer none): agreement section, or %NULL
 *
 * Since: 0.7.8
 **/
AsAgreementSection *
as_agreement_get_section_default (AsAgreement *agreement)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	g_return_val_if_fail (AS_IS_AGREEMENT (agreement), NULL);
	if (priv->sections->len == 0)
		return NULL;
	return AS_AGREEMENT_SECTION (g_ptr_array_index (priv->sections, 0));
}

/**
 * as_agreement_add_detail:
 * @agreement: a #AsAgreement instance.
 * @agreement_section: a #AsAgreementSection instance.
 *
 * Adds a section to the agreement.
 *
 * Since: 0.7.8
 **/
void
as_agreement_add_section (AsAgreement *agreement, AsAgreementSection *agreement_section)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	g_return_if_fail (AS_IS_AGREEMENT (agreement));
	g_ptr_array_add (priv->sections, g_object_ref (agreement_section));
}

/**
 * as_agreement_node_insert: (skip)
 * @agreement: a #AsAgreement instance.
 * @parent: the parent #GNode to use..
 * @ctx: the #AsNodeContext
 *
 * Inserts the agreement into the DOM tree.
 *
 * Returns: (transfer none): A populated #GNode, or %NULL
 *
 * Since: 0.7.8
 **/
GNode *
as_agreement_node_insert (AsAgreement *agreement,
			       GNode *parent,
			       AsNodeContext *ctx)
{
	AsAgreementPrivate *priv = GET_PRIVATE (agreement);
	g_return_val_if_fail (AS_IS_AGREEMENT (agreement), NULL);
	GNode *n = as_node_insert (parent, "agreement", NULL,
				   AS_NODE_INSERT_FLAG_NONE,
				   NULL);
	if (priv->kind != AS_AGREEMENT_KIND_UNKNOWN) {
		as_node_add_attribute (n, "type",
				       as_agreement_kind_to_string (priv->kind));
	}
	if (priv->version_id != NULL)
		as_node_add_attribute (n, "version_id", priv->version_id);
	for (guint i = 0; i < priv->sections->len; i++) {
		AsAgreementSection *ps = g_ptr_array_index (priv->sections, i);
		as_agreement_section_node_insert (ps, n, ctx);
	}

	return n;
}

/**
 * as_agreement_node_parse:
 * @agreement: a #AsAgreement instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.7.8
 **/
gboolean
as_agreement_node_parse (AsAgreement *agreement, GNode *node,
			      AsNodeContext *ctx, GError **error)
{
	const gchar *tmp;

	/* get ID */
	tmp = as_node_get_attribute (node, "type");
	if (tmp != NULL)
		as_agreement_set_kind (agreement, as_agreement_kind_from_string (tmp));
	tmp = as_node_get_attribute (node, "version_id");
	if (tmp != NULL)
		as_agreement_set_version_id (agreement, tmp);

	/* get sections and details */
	for (GNode *c = node->children; c != NULL; c = c->next) {
		if (as_node_get_tag (c) == AS_TAG_AGREEMENT_SECTION) {
			g_autoptr(AsAgreementSection) ps = as_agreement_section_new ();
			if (!as_agreement_section_node_parse (ps, c, ctx, error))
				return FALSE;
			as_agreement_add_section (agreement, ps);
			continue;
		}
	}
	return TRUE;
}

/**
 * as_agreement_new:
 *
 * Creates a new #AsAgreement.
 *
 * Returns: (transfer full): a #AsAgreement
 *
 * Since: 0.7.8
 **/
AsAgreement *
as_agreement_new (void)
{
	AsAgreement *agreement;
	agreement = g_object_new (AS_TYPE_AGREEMENT, NULL);
	return AS_AGREEMENT (agreement);
}
