/*
 * ministream - minimal (subset) appstream metadata parser
 * Copyright (c) 2026  Florian Leander Singer <sp1rit@disroot.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "ministream-priv.h"

G_DEFINE_QUARK (ms-error-quark, ms_error)
G_DEFINE_ENUM_TYPE (MsError, ms_error,
	G_DEFINE_ENUM_VALUE (MS_ERROR_NO_COMPONENT, "no-component"),
	G_DEFINE_ENUM_VALUE (MS_ERROR_NO_IDENTIFIER, "no-identifier")
)

G_DEFINE_BOXED_TYPE (MsComponent, ms_component, ms_component_ref, ms_component_unref)

void ms_release_free(MsRelease* self) {
	if (self->version)
		g_free(self->version);
	if (self->description)
		g_free(self->description);
	g_free(self);
}

/**
 * ms_release_get_version:
 * @self: the release
 *
 * Gets the version attached to the release, or %NULL if not set.
 *
 * Returns: (nullable): the version of the release
 */
const gchar* ms_release_get_version(const MsRelease* self) {
	return self->version;
}

/**
 * ms_release_get_description:
 * @self: the release
 *
 * Gets the description attached to the releease, or %NULL if not set.
 *
 * The description is encoded as markup, containg paragraphs, ordered
 * and unordered lists, aswell as list items within them.
 *
 * Returns: (nullable): the description of the release
 */
const gchar* ms_release_get_description(const MsRelease* self) {
	return self->description;
}

/**
 * ms_component_ref:
 * @self: the component
 *
 * Increment the reference count of the component.
 *
 * This is an atomic operation and as such, threadsafe.
 *
 * Returns: (transfer full): the new reference of @self
 */
MsComponent* ms_component_ref(MsComponent* self) {
	g_atomic_ref_count_inc(&self->rc);
	return self;
}

/**
 * ms_component_unref:
 * @self: (transfer full): the component
 *
 * Decrements the reference count of the component. Once the count
 * reaches zero, @self will be freed.
 *
 * As such, do not use @self after calling this method.
 */
void ms_component_unref(MsComponent* self) {
	if (g_atomic_ref_count_dec(&self->rc)) {
		g_free(self->id);
		if (self->name)
			g_free(self->name);
		if (self->homepage_uri)
			g_free(self->homepage_uri);
		if (self->bugtracker_uri)
			g_free(self->bugtracker_uri);
		if (self->help_uri)
			g_free(self->help_uri);
		if (self->desktop_launchables)
			g_ptr_array_unref(self->desktop_launchables);
		if (self->releases)
			g_ptr_array_unref(self->releases);
		if (self->project_license)
			g_free(self->project_license);
		if (self->developer_name)
			g_free(self->developer_name);

		g_free(self);
	}
}

/**
 * ms_component_get_id:
 * @self: the component
 *
 * Get the application identifier of this component. This should be in
 * the big-endian domain format.
 *
 * Returns: the application identifier
 */
const gchar* ms_component_get_id(const MsComponent* self) {
	return self->id;
}

/**
 * ms_component_get_name:
 * @self: the component
 *
 * Get the application name of this component in the best locale
 * available.
 *
 * Returns: (nullable): the localized application name
 */
const gchar* ms_component_get_name(const MsComponent* self) {
	return self->name;
}

/**
 * ms_component_get_homepage_uri:
 * @self: the component
 *
 * Get the homepage URI of this component.
 *
 * Returns: (nullable): the hompage URI
 */
const gchar* ms_component_get_homepage_uri(const MsComponent* self) {
	return self->homepage_uri;
}

/**
 * ms_component_get_bugtracker_uri:
 * @self: the component
 *
 * Get the bugtracker URI of this component.
 *
 * Returns: (nullable): the bugtracker URI
 */
const gchar* ms_component_get_bugtracker_uri(const MsComponent* self) {
	return self->bugtracker_uri;
}

/**
 * ms_component_get_help_uri:
 * @self: the component
 *
 * Get the help URI of this component.
 *
 * Returns: (nullable): the help URI
 */
const gchar* ms_component_get_help_uri(const MsComponent* self) {
	return self->help_uri;
}

/**
 * ms_component_get_desktop_launchables:
 * @self: the component
 *
 * Gets the desktop launchables this component provides.
 *
 * Returns: (nullable) (transfer none) (array zero-terminated=1): the desktop launchables
 */
const gchar** ms_component_get_desktop_launchables(const MsComponent* self) {
	if (!self->desktop_launchables)
		return NULL;

	g_assert(g_ptr_array_index(self->desktop_launchables, self->desktop_launchables->len - 1) == NULL);
	return (const gchar**)self->desktop_launchables->pdata;
}

/**
 * ms_component_get_releases:
 * @self: the component
 * @len: (out) (not optional): the number of releases of this component
 *
 * Gets the releases information of this component.
 *
 * Returns: (nullable) (transfer none) (array length=len): the releases of this component
 */
const MsRelease** ms_component_get_releases(const MsComponent* self, guint* len) {
	if (!self->releases) {
		*len = 0;
		return NULL;
	}

	*len = self->releases->len;
	return (const MsRelease**)self->releases->pdata;
}

/**
 * ms_component_get_project_license:
 * @self: the component
 *
 * Get the license of the project this component is part of. This should
 * be an identifier part of the SPDX license list.
 *
 * Returns: (nullable): the project license identifier
 */
const gchar* ms_component_get_project_license(const MsComponent* self) {
	return self->project_license;
}

/**
 * ms_component_get_developer_name:
 * @self: the component
 *
 * Gets the name of the developer that has written this component in the
 * best locale available.
 *
 * Returns: (nullable): the localized developer name
 */
const gchar* ms_component_get_developer_name(const MsComponent* self) {
	return self->developer_name;
}
