/*
 * Copyright (C) 2016 Canonical Ltd.
 *
 * 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 or version 3 of the License.
 * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
 */

#include "snapd-snap.h"
#include "snapd-enum-types.h"

/**
 * SECTION:snapd-snap
 * @short_description: Snap metadata
 * @include: snapd-glib/snapd-glib.h
 *
 * A #SnapdSnap contains the metadata for a given snap. Snap metadata can be
 * retrieved using snapd_client_list_sync(), snapd_client_list_one_sync() or
 * snapd_client_find_sync().
 */

/**
 * SnapdSnap:
 *
 * #SnapdSnap contains Snap metadata.
 *
 * Since: 1.0
 */

struct _SnapdSnap
{
    GObject parent_instance;

    GPtrArray *apps;
    gchar *base;
    gchar *broken;
    gchar *channel;
    GPtrArray *channels;
    GStrv common_ids;
    SnapdConfinement confinement;
    gchar *contact;
    gchar *description;
    gboolean devmode;
    gint64 download_size;
    gchar *icon;
    gchar *id;
    GDateTime *install_date;
    gint64 installed_size;
    gboolean jailmode;
    gchar *license;
    GPtrArray *media;
    gchar *mounted_from;
    gchar *name;
    GPtrArray *prices;
    gboolean private;
    gchar *publisher_display_name;
    gchar *publisher_id;
    gchar *publisher_username;
    SnapdPublisherValidation publisher_validation;
    gchar *revision;
    GPtrArray *screenshots;
    SnapdSnapStatus status;
    gchar *summary;
    gchar *title;
    gchar *tracking_channel;
    GStrv tracks;
    gboolean trymode;
    SnapdSnapType snap_type;
    gchar *version;
    gchar *website;
};

enum
{
    PROP_APPS = 1,
    PROP_CHANNEL,
    PROP_CONFINEMENT,
    PROP_CONTACT,
    PROP_DESCRIPTION,
    PROP_DEVELOPER,
    PROP_DEVMODE,
    PROP_DOWNLOAD_SIZE,
    PROP_ICON,
    PROP_ID,
    PROP_INSTALL_DATE,
    PROP_INSTALLED_SIZE,
    PROP_JAILMODE,
    PROP_NAME,
    PROP_PRICES,
    PROP_PRIVATE,
    PROP_REVISION,
    PROP_SCREENSHOTS,
    PROP_STATUS,
    PROP_SUMMARY,
    PROP_TRYMODE,
    PROP_SNAP_TYPE,
    PROP_VERSION,
    PROP_TRACKING_CHANNEL,
    PROP_TITLE,
    PROP_LICENSE,
    PROP_CHANNELS,
    PROP_TRACKS,
    PROP_BROKEN,
    PROP_COMMON_IDS,
    PROP_PUBLISHER_DISPLAY_NAME,
    PROP_PUBLISHER_ID,
    PROP_PUBLISHER_USERNAME,
    PROP_PUBLISHER_VALIDATION,
    PROP_BASE,
    PROP_MOUNTED_FROM,
    PROP_MEDIA,
    PROP_WEBSITE,
    PROP_LAST
};

G_DEFINE_TYPE (SnapdSnap, snapd_snap, G_TYPE_OBJECT)

/**
 * snapd_snap_get_apps:
 * @snap: a #SnapdSnap.
 *
 * Get the apps this snap provides.
 *
 * Returns: (transfer none) (element-type SnapdApp): an array of #SnapdApp.
 *
 * Since: 1.0
 */
GPtrArray *
snapd_snap_get_apps (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->apps;
}

/**
 * snapd_snap_get_base:
 * @snap: a #SnapdSnap.
 *
 * Get the base snap this snap uses.
 *
 * Returns: (allow-none): a snap name or %NULL if not set.
 *
 * Since: 1.45
 */
const gchar *
snapd_snap_get_base (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->base;
}

/**
 * snapd_snap_get_broken:
 * @snap: a #SnapdSnap.
 *
 * Get the reason this snap is broken.
 *
 * Returns: (allow-none): an error string or %NULL if not broken.
 *
 * Since: 1.25
 */
const gchar *
snapd_snap_get_broken (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->broken;
}

/**
 * snapd_snap_get_channel:
 * @snap: a #SnapdSnap.
 *
 * Get the channel this snap is from, e.g. "stable".
 *
 * Returns: a channel name.
 *
 * Since: 1.0
 */
const gchar *
snapd_snap_get_channel (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->channel;
}

/**
 * snapd_snap_get_channels:
 * @snap: a #SnapdSnap.
 *
 * Gets the available channels for this snap.
 *
 * Returns: (transfer none) (element-type SnapdChannel): an array of #SnapdChannel.
 *
 * Since: 1.22
 */
GPtrArray *
snapd_snap_get_channels (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->channels;
}

static int
parse_risk (const gchar *risk)
{
    if (g_strcmp0 (risk, "stable") == 0)
        return 0;
    else if (g_strcmp0 (risk, "candidate") == 0)
        return 1;
    else if (g_strcmp0 (risk, "beta") == 0)
        return 2;
    else if (g_strcmp0 (risk, "edge") == 0)
        return 3;
    else
        return -1;
}

/**
 * snapd_snap_match_channel:
 * @snap: a #SnapdSnap.
 * @name: a channel name.
 *
 * Finds the available channel that best matches the given name.
 * If none matches %NULL is returned.
 *
 * Returns: (transfer none) (allow-none): an #SnapdChannel or %NULL.
 *
 * Since: 1.22
 */
SnapdChannel *
snapd_snap_match_channel (SnapdSnap *self, const gchar *name)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    g_return_val_if_fail (name != NULL, NULL);

    g_autoptr(SnapdChannel) c = g_object_new (SNAPD_TYPE_CHANNEL,
                                              "name", name,
                                              NULL);
    SnapdChannel *matched_channel = NULL;
    int matched_risk = -1;
    for (guint i = 0; i < self->channels->len; i++) {
        SnapdChannel *channel = self->channels->pdata[i];

        /* Must be same track and branch */
        if (g_strcmp0 (snapd_channel_get_track (channel), snapd_channel_get_track (c)) != 0 ||
            g_strcmp0 (snapd_channel_get_branch (channel), snapd_channel_get_branch (c)) != 0)
            continue;

        /* Must be no riskier than requested */
        int r = parse_risk (snapd_channel_get_risk (channel));
        if (r > parse_risk (snapd_channel_get_risk (c)))
            continue;

        /* Use this if unmatched or a better risk match */
        if (matched_channel == NULL || r > matched_risk) {
            matched_channel = channel;
            matched_risk = r;
        }
    }

    return matched_channel;
}

/**
 * snapd_snap_get_common_ids:
 * @snap: a #SnapdSnap.
 *
 * Get common IDs associated with this snap.
 *
 * Returns: (transfer none) (array zero-terminated=1): an array of common ids.
 *
 * Since: 1.41
 */
GStrv
snapd_snap_get_common_ids (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->common_ids;
}

/**
 * snapd_snap_get_confinement:
 * @snap: a #SnapdSnap.
 *
 * Get the confinement this snap is using, e.g. %SNAPD_CONFINEMENT_STRICT.
 *
 * Returns: a #SnapdConfinement.
 *
 * Since: 1.0
 */
SnapdConfinement
snapd_snap_get_confinement (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), SNAPD_CONFINEMENT_UNKNOWN);
    return self->confinement;
}

/**
 * snapd_snap_get_contact:
 * @snap: a #SnapdSnap.
 *
 * Get the means of contacting the snap developer, e.g. "mailto:developer@example.com".
 *
 * Returns: a contact URL.
 *
 * Since: 1.13
 */
const gchar *
snapd_snap_get_contact (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->contact;
}

/**
 * snapd_snap_get_description:
 * @snap: a #SnapdSnap.
 *
 * Get a multi-line description of this snap. The description is formatted using
 * a subset of Markdown. To parse this use a #SnapdMarkdownParser.
 *
 * Returns: description text.
 *
 * Since: 1.0
 */
const gchar *
snapd_snap_get_description (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->description;
}

/**
 * snapd_snap_get_developer:
 * @snap: a #SnapdSnap.
 *
 * Get the developer who created this snap.
 *
 * Returns: a developer name.
 *
 * Since: 1.0
 * Deprecated: 1.42: Use snapd_snap_get_publisher_username()
 */
const gchar *
snapd_snap_get_developer (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->publisher_username;
}

/**
 * snapd_snap_get_devmode:
 * @snap: a #SnapdSnap.
 *
 * Get if this snap is running in developer mode.
 *
 * Returns: %TRUE if this snap is running in devmode.
 *
 * Since: 1.0
 */
gboolean
snapd_snap_get_devmode (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), FALSE);
    return self->devmode;
}

/**
 * snapd_snap_get_download_size:
 * @snap: a #SnapdSnap.
 *
 * Get the download size of this snap or 0 if unknown.
 *
 * Returns: a byte count.
 *
 * Since: 1.0
 */
gint64
snapd_snap_get_download_size (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), 0);
    return self->download_size;
}

/**
 * snapd_snap_get_icon:
 * @snap: a #SnapdSnap.
 *
 * Get the icon for this Snap, either a URL or an absolute path to retrieve it
 * from snapd directly.
 *
 * Returns: a URL or path.
 *
 * Since: 1.0
 */
const gchar *
snapd_snap_get_icon (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->icon;
}

/**
 * snapd_snap_get_id:
 * @snap: a #SnapdSnap.
 *
 * Gets the unique ID for this snap.
 *
 * Returns: an ID.
 *
 * Since: 1.0
 */
const gchar *
snapd_snap_get_id (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->id;
}

/**
 * snapd_snap_get_install_date:
 * @snap: a #SnapdSnap.
 *
 * Get the date this snap was installed or %NULL if unknown.
 *
 * Returns: (transfer none) (allow-none): a #GDateTime or %NULL.
 *
 * Since: 1.0
 */
GDateTime *
snapd_snap_get_install_date (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->install_date;
}

/**
 * snapd_snap_get_installed_size:
 * @snap: a #SnapdSnap.
 *
 * Get the installed size of this snap or 0 if unknown.
 *
 * Returns: a byte count.
 *
 * Since: 1.0
 */
gint64
snapd_snap_get_installed_size (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), 0);
    return self->installed_size;
}

/**
 * snapd_snap_get_jailmode:
 * @snap: a #SnapdSnap.
 *
 * Get if this snap is running in enforced confinement (jail) mode.
 *
 * Returns: %TRUE if this snap is running in jailmode.
 *
 * Since: 1.8
 */
gboolean
snapd_snap_get_jailmode (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), FALSE);
    return self->jailmode;
}

/**
 * snapd_snap_get_license:
 * @snap: a #SnapdSnap.
 *
 * Gets the SPDX license expression for this snap, e.g. "GPL-3.0+".
 *
 * Returns: (allow-none): an SPDX license expression or %NULL.
 *
 * Since: 1.19
 */
const gchar *
snapd_snap_get_license (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->license;
}

/**
 * snapd_snap_get_media:
 * @snap: a #SnapdSnap.
 *
 * Get media that is associated with this snap.
 *
 * Returns: (transfer none) (element-type SnapdMedia): an array of #SnapdMedia.
 *
 * Since: 1.45
 */
GPtrArray *
snapd_snap_get_media (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->media;
}

/**
 * snapd_snap_get_mounted_from:
 * @snap: a #SnapdSnap.
 *
 * Gets the path this snap is mounted from, which is a .snap file for installed
 * snaps and a directory for snaps in try mode.
 *
 * Returns: (allow-none): a file path or %NULL.
 *
 * Since: 1.45
 */
const gchar *
snapd_snap_get_mounted_from (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->mounted_from;
}

/**
 * snapd_snap_get_title:
 * @snap: a #SnapdSnap.
 *
 * Get the title for this snap. If not available use the snap name instead.
 *
 * Returns: (allow-none): a title or %NULL.
 *
 * Since: 1.14
 */
const gchar *
snapd_snap_get_title (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->title;
}

/**
 * snapd_snap_get_name:
 * @snap: a #SnapdSnap.
 *
 * Get the name of this snap. This is used to reference this snap, e.g. for
 * installing / removing.
 *
 * Returns: a name.
 *
 * Since: 1.0
 */
const gchar *
snapd_snap_get_name (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->name;
}

/**
 * snapd_snap_get_prices:
 * @snap: a #SnapdSnap.
 *
 * Get the prices that this snap can be purchased at.
 *
 * Returns: (transfer none) (element-type SnapdPrice): an array of #SnapdPrice.
 *
 * Since: 1.0
 */
GPtrArray *
snapd_snap_get_prices (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->prices;
}

/**
 * snapd_snap_get_private:
 * @snap: a #SnapdSnap.
 *
 * Get if this snap is only available to the developer.
 *
 * Returns: %TRUE if this is a private snap.
 *
 * Since: 1.0
 */
gboolean
snapd_snap_get_private (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), FALSE);
    return self->private;
}

/**
 * snapd_snap_get_publisher_display_name:
 * @snap: a #SnapdSnap.
 *
 * Get the display name of the publisher who created this snap.
 *
 * Returns: a publisher display name.
 *
 * Since: 1.42
 */
const gchar *
snapd_snap_get_publisher_display_name (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->publisher_display_name;
}

/**
 * snapd_snap_get_publisher_id:
 * @snap: a #SnapdSnap.
 *
 * Get the ID of the publisher who created this snap.
 *
 * Returns: a publisher ID.
 *
 * Since: 1.42
 */
const gchar *
snapd_snap_get_publisher_id (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->publisher_id;
}

/**
 * snapd_snap_get_publisher_username:
 * @snap: a #SnapdSnap.
 *
 * Get the username of the publisher who created this snap.
 *
 * Returns: a publisher username.
 *
 * Since: 1.42
 */
const gchar *
snapd_snap_get_publisher_username (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->publisher_username;
}

/**
 * snapd_snap_get_publisher_validation:
 * @snap: a #SnapdSnap.
 *
 * Get the validation for the snap publisher, e.g. %SNAPD_PUBLISHER_VALIDATION_VERIFIED
 *
 * Returns: a #SnapdPublisherValidation.
 *
 * Since: 1.42
 */
SnapdPublisherValidation
snapd_snap_get_publisher_validation (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), SNAPD_PUBLISHER_VALIDATION_UNKNOWN);
    return self->publisher_validation;
}

/**
 * snapd_snap_get_revision:
 * @snap: a #SnapdSnap.
 *
 * Get the revision for this snap. The format of the string is undefined.
 * See also snapd_snap_get_version().
 *
 * Returns: a revision string.
 *
 * Since: 1.0
 */
const gchar *
snapd_snap_get_revision (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->revision;
}

/**
 * snapd_snap_get_screenshots:
 * @snap: a #SnapdSnap.
 *
 * Get the screenshots that are available for this snap.
 *
 * Returns: (transfer none) (element-type SnapdScreenshot): an array of #SnapdScreenshot.
 *
 * Since: 1.0
 * Deprecated: 1.45: Use snapd_snap_get_media()
 */
GPtrArray *
snapd_snap_get_screenshots (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->screenshots;
}

/**
 * snapd_snap_get_snap_type:
 * @snap: a #SnapdSnap.
 *
 * Get the type of snap, e.g. %SNAPD_SNAP_TYPE_APP
 *
 * Returns: a #SnapdSnapType.
 *
 * Since: 1.0
 */
SnapdSnapType
snapd_snap_get_snap_type (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), SNAPD_SNAP_TYPE_UNKNOWN);
    return self->snap_type;
}

/**
 * snapd_snap_get_status:
 * @snap: a #SnapdSnap.
 *
 * Get the current status of this snap, e.g. SNAPD_SNAP_STATUS_INSTALLED.
 *
 * Returns: a #SnapdSnapStatus.
 *
 * Since: 1.0
 */
SnapdSnapStatus
snapd_snap_get_status (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), SNAPD_SNAP_STATUS_UNKNOWN);
    return self->status;
}

/**
 * snapd_snap_get_summary:
 * @snap: a #SnapdSnap.
 *
 * Get a single line summary for this snap, e.g. "Best app ever!".
 *
 * Returns: a summary string.
 *
 * Since: 1.0
 */
const gchar *
snapd_snap_get_summary (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->summary;
}

/**
 * snapd_snap_get_tracking_channel:
 * @snap: a #SnapdSnap.
 *
 * Get the channel that updates will be installed from, e.g. "stable".
 *
 * Returns: a channel name.
 *
 * Since: 1.7
 */
const gchar *
snapd_snap_get_tracking_channel (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->tracking_channel;
}

/**
 * snapd_snap_get_tracks:
 * @snap: a #SnapdSnap.
 *
 * Get the tracks that are available.
 *
 * Returns: (transfer none) (array zero-terminated=1): an ordered array of track names.
 *
 * Since: 1.22
 */
GStrv
snapd_snap_get_tracks (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->tracks;
}

/**
 * snapd_snap_get_trymode:
 * @snap: a #SnapdSnap.
 *
 * Get if this snap is running in try mode (installed locally and able to be
 * directly modified).
 *
 * Returns: %TRUE if using trymode.
 *
 * Since: 1.0
 */
gboolean
snapd_snap_get_trymode (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), FALSE);
    return self->trymode;
}

/**
 * snapd_snap_get_version:
 * @snap: a #SnapdSnap.
 *
 * Get the version for this snap. The format of the string is undefined.
 * See also snapd_snap_get_revision().
 *
 * Returns: a version string.
 *
 * Since: 1.0
 */
const gchar *
snapd_snap_get_version (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->version;
}

/**
 * snapd_snap_get_website:
 * @snap: a #SnapdSnap.
 *
 * Get the website of the snap developer, e.g. "http://example.com".
 *
 * Returns: a website URL.
 *
 * Since: 1.50
 */
const gchar *
snapd_snap_get_website (SnapdSnap *self)
{
    g_return_val_if_fail (SNAPD_IS_SNAP (self), NULL);
    return self->website;
}

static void
snapd_snap_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
    SnapdSnap *self = SNAPD_SNAP (object);

    switch (prop_id) {
    case PROP_APPS:
        g_clear_pointer (&self->apps, g_ptr_array_unref);
        if (g_value_get_boxed (value) != NULL)
            self->apps = g_ptr_array_ref (g_value_get_boxed (value));
        break;
    case PROP_BASE:
        g_free (self->base);
        self->base = g_strdup (g_value_get_string (value));
        break;
    case PROP_BROKEN:
        g_free (self->broken);
        self->broken = g_strdup (g_value_get_string (value));
        break;
    case PROP_CHANNEL:
        g_free (self->channel);
        self->channel = g_strdup (g_value_get_string (value));
        break;
    case PROP_CHANNELS:
        g_clear_pointer (&self->channels, g_ptr_array_unref);
        if (g_value_get_boxed (value) != NULL)
            self->channels = g_ptr_array_ref (g_value_get_boxed (value));
        break;
    case PROP_CONFINEMENT:
        self->confinement = g_value_get_enum (value);
        break;
    case PROP_CONTACT:
        g_free (self->contact);
        self->contact = g_strdup (g_value_get_string (value));
        break;
    case PROP_DESCRIPTION:
        g_free (self->description);
        self->description = g_strdup (g_value_get_string (value));
        break;
    case PROP_DEVMODE:
        self->devmode = g_value_get_boolean (value);
        break;
    case PROP_DOWNLOAD_SIZE:
        self->download_size = g_value_get_int64 (value);
        break;
    case PROP_ICON:
        g_free (self->icon);
        self->icon = g_strdup (g_value_get_string (value));
        break;
    case PROP_ID:
        g_free (self->id);
        self->id = g_strdup (g_value_get_string (value));
        break;
    case PROP_INSTALL_DATE:
        g_clear_pointer (&self->install_date, g_date_time_unref);
        if (g_value_get_boxed (value) != NULL)
            self->install_date = g_date_time_ref (g_value_get_boxed (value));
        break;
    case PROP_INSTALLED_SIZE:
        self->installed_size = g_value_get_int64 (value);
        break;
    case PROP_JAILMODE:
        self->jailmode = g_value_get_boolean (value);
        break;
    case PROP_MOUNTED_FROM:
        g_free (self->mounted_from);
        self->mounted_from = g_strdup (g_value_get_string (value));
        break;
    case PROP_MEDIA:
        g_clear_pointer (&self->media, g_ptr_array_unref);
        if (g_value_get_boxed (value) != NULL)
            self->media = g_ptr_array_ref (g_value_get_boxed (value));
        break;
    case PROP_NAME:
        g_free (self->name);
        self->name = g_strdup (g_value_get_string (value));
        break;
    case PROP_PRICES:
        g_clear_pointer (&self->prices, g_ptr_array_unref);
        if (g_value_get_boxed (value) != NULL)
            self->prices = g_ptr_array_ref (g_value_get_boxed (value));
        break;
    case PROP_PRIVATE:
        self->private = g_value_get_boolean (value);
        break;
    case PROP_PUBLISHER_DISPLAY_NAME:
        g_free (self->publisher_display_name);
        self->publisher_display_name = g_strdup (g_value_get_string (value));
        break;
    case PROP_PUBLISHER_ID:
        g_free (self->publisher_id);
        self->publisher_id = g_strdup (g_value_get_string (value));
        break;
    case PROP_PUBLISHER_USERNAME:
    case PROP_DEVELOPER:
        g_free (self->publisher_username);
        self->publisher_username = g_strdup (g_value_get_string (value));
        break;
    case PROP_PUBLISHER_VALIDATION:
        self->publisher_validation = g_value_get_enum (value);
        break;
    case PROP_REVISION:
        g_free (self->revision);
        self->revision = g_strdup (g_value_get_string (value));
        break;
    case PROP_SCREENSHOTS:
        g_clear_pointer (&self->screenshots, g_ptr_array_unref);
        if (g_value_get_boxed (value) != NULL)
            self->screenshots = g_ptr_array_ref (g_value_get_boxed (value));
        break;
    case PROP_SNAP_TYPE:
        self->snap_type = g_value_get_enum (value);
        break;
    case PROP_STATUS:
        self->status = g_value_get_enum (value);
        break;
    case PROP_SUMMARY:
        g_free (self->summary);
        self->summary = g_strdup (g_value_get_string (value));
        break;
    case PROP_TITLE:
        g_free (self->title);
        self->title = g_strdup (g_value_get_string (value));
        break;
    case PROP_TRACKING_CHANNEL:
        g_free (self->tracking_channel);
        self->tracking_channel = g_strdup (g_value_get_string (value));
        break;
    case PROP_TRACKS:
        g_strfreev (self->tracks);
        self->tracks = g_strdupv (g_value_get_boxed (value));
        break;
    case PROP_TRYMODE:
        self->trymode = g_value_get_boolean (value);
        break;
    case PROP_VERSION:
        g_free (self->version);
        self->version = g_strdup (g_value_get_string (value));
        break;
    case PROP_LICENSE:
        g_free (self->license);
        self->license = g_strdup (g_value_get_string (value));
        break;
    case PROP_COMMON_IDS:
        g_strfreev (self->common_ids);
        self->common_ids = g_strdupv (g_value_get_boxed (value));
        break;
    case PROP_WEBSITE:
        g_free (self->website);
        self->website = g_strdup (g_value_get_string (value));
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
snapd_snap_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
    SnapdSnap *self = SNAPD_SNAP (object);

    switch (prop_id) {
    case PROP_APPS:
        g_value_set_boxed (value, self->apps);
        break;
    case PROP_BASE:
        g_value_set_string (value, self->base);
        break;
    case PROP_BROKEN:
        g_value_set_string (value, self->broken);
        break;
    case PROP_CHANNEL:
        g_value_set_string (value, self->channel);
        break;
    case PROP_CHANNELS:
        g_value_set_boxed (value, self->channels);
        break;
    case PROP_CONFINEMENT:
        g_value_set_enum (value, self->confinement);
        break;
    case PROP_CONTACT:
        g_value_set_string (value, self->contact);
        break;
    case PROP_DESCRIPTION:
        g_value_set_string (value, self->description);
        break;
    case PROP_DEVMODE:
        g_value_set_boolean (value, self->devmode);
        break;
    case PROP_DOWNLOAD_SIZE:
        g_value_set_int64 (value, self->download_size);
        break;
    case PROP_ICON:
        g_value_set_string (value, self->icon);
        break;
    case PROP_ID:
        g_value_set_string (value, self->id);
        break;
    case PROP_INSTALL_DATE:
        g_value_set_boxed (value, self->install_date);
        break;
    case PROP_INSTALLED_SIZE:
        g_value_set_int64 (value, self->installed_size);
        break;
    case PROP_JAILMODE:
        g_value_set_boolean (value, self->jailmode);
        break;
    case PROP_MEDIA:
        g_value_set_boxed (value, self->media);
        break;
    case PROP_MOUNTED_FROM:
        g_value_set_string (value, self->mounted_from);
        break;
    case PROP_NAME:
        g_value_set_string (value, self->name);
        break;
    case PROP_PRICES:
        g_value_set_boxed (value, self->prices);
        break;
    case PROP_PRIVATE:
        g_value_set_boolean (value, self->private);
        break;
    case PROP_PUBLISHER_DISPLAY_NAME:
        g_value_set_string (value, self->publisher_display_name);
        break;
    case PROP_PUBLISHER_ID:
        g_value_set_string (value, self->publisher_id);
        break;
    case PROP_PUBLISHER_USERNAME:
    case PROP_DEVELOPER:
        g_value_set_string (value, self->publisher_username);
        break;
    case PROP_PUBLISHER_VALIDATION:
        g_value_set_enum (value, self->publisher_validation);
        break;
    case PROP_REVISION:
        g_value_set_string (value, self->revision);
        break;
    case PROP_SCREENSHOTS:
        g_value_set_boxed (value, self->screenshots);
        break;
    case PROP_SNAP_TYPE:
        g_value_set_enum (value, self->snap_type);
        break;
    case PROP_STATUS:
        g_value_set_enum (value, self->status);
        break;
    case PROP_SUMMARY:
        g_value_set_string (value, self->summary);
        break;
    case PROP_TITLE:
        g_value_set_string (value, self->title);
        break;
    case PROP_TRACKING_CHANNEL:
        g_value_set_string (value, self->tracking_channel);
        break;
    case PROP_TRACKS:
        g_value_set_boxed (value, self->tracks);
        break;
    case PROP_TRYMODE:
        g_value_set_boolean (value, self->trymode);
        break;
    case PROP_VERSION:
        g_value_set_string (value, self->version);
        break;
    case PROP_LICENSE:
        g_value_set_string (value, self->license);
        break;
    case PROP_COMMON_IDS:
        g_value_set_boxed (value, self->common_ids);
        break;
    case PROP_WEBSITE:
        g_value_set_string (value, self->website);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
snapd_snap_finalize (GObject *object)
{
    SnapdSnap *self = SNAPD_SNAP (object);

    g_clear_pointer (&self->apps, g_ptr_array_unref);
    g_clear_pointer (&self->base, g_free);
    g_clear_pointer (&self->broken, g_free);
    g_clear_pointer (&self->channel, g_free);
    g_clear_pointer (&self->channels, g_ptr_array_unref);
    g_clear_pointer (&self->common_ids, g_strfreev);
    g_clear_pointer (&self->contact, g_free);
    g_clear_pointer (&self->description, g_free);
    g_clear_pointer (&self->icon, g_free);
    g_clear_pointer (&self->id, g_free);
    g_clear_pointer (&self->install_date, g_date_time_unref);
    g_clear_pointer (&self->name, g_free);
    g_clear_pointer (&self->license, g_free);
    g_clear_pointer (&self->media, g_ptr_array_unref);
    g_clear_pointer (&self->mounted_from, g_free);
    g_clear_pointer (&self->prices, g_ptr_array_unref);
    g_clear_pointer (&self->publisher_display_name, g_free);
    g_clear_pointer (&self->publisher_id, g_free);
    g_clear_pointer (&self->publisher_username, g_free);
    g_clear_pointer (&self->revision, g_free);
    g_clear_pointer (&self->screenshots, g_ptr_array_unref);
    g_clear_pointer (&self->summary, g_free);
    g_clear_pointer (&self->title, g_free);
    g_clear_pointer (&self->tracking_channel, g_free);
    g_clear_pointer (&self->tracks, g_strfreev);
    g_clear_pointer (&self->version, g_free);
    g_clear_pointer (&self->website, g_free);

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

static void
snapd_snap_class_init (SnapdSnapClass *klass)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

    gobject_class->set_property = snapd_snap_set_property;
    gobject_class->get_property = snapd_snap_get_property;
    gobject_class->finalize = snapd_snap_finalize;

    g_object_class_install_property (gobject_class,
                                     PROP_APPS,
                                     g_param_spec_boxed ("apps",
                                                         "apps",
                                                         "Apps this snap contains",
                                                         G_TYPE_PTR_ARRAY,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_BASE,
                                     g_param_spec_string ("base",
                                                          "base",
                                                          "Base snap this snap uses",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_BROKEN,
                                     g_param_spec_string ("broken",
                                                          "broken",
                                                          "Error string if snap is broken",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_CHANNEL,
                                     g_param_spec_string ("channel",
                                                          "channel",
                                                          "Channel the snap is from",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_CHANNELS,
                                     g_param_spec_boxed ("channels",
                                                         "channels",
                                                         "Channels this snap is available on",
                                                         G_TYPE_PTR_ARRAY,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_COMMON_IDS,
                                     g_param_spec_boxed ("common-ids",
                                                         "common-ids",
                                                         "Common IDs",
                                                         G_TYPE_STRV,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_CONFINEMENT,
                                     g_param_spec_enum ("confinement",
                                                        "confinement",
                                                        "Confinement requested by the snap",
                                                        SNAPD_TYPE_CONFINEMENT, SNAPD_CONFINEMENT_UNKNOWN,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_CONTACT,
                                     g_param_spec_string ("contact",
                                                          "contact",
                                                          "Method of contacting developer",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_DESCRIPTION,
                                     g_param_spec_string ("description",
                                                          "description",
                                                          "Description of the snap",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_DEVELOPER,
                                     g_param_spec_string ("developer",
                                                          "developer",
                                                          "Developer who created the snap",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_DEPRECATED));
    g_object_class_install_property (gobject_class,
                                     PROP_DEVMODE,
                                     g_param_spec_boolean ("devmode",
                                                           "devmode",
                                                           "TRUE if the snap is currently installed in devmode",
                                                           FALSE,
                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_DOWNLOAD_SIZE,
                                     g_param_spec_int64 ("download-size",
                                                         "download-size",
                                                         "Download size in bytes",
                                                         G_MININT64, G_MAXINT64, 0,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_ICON,
                                     g_param_spec_string ("icon",
                                                          "icon",
                                                          "URL to the snap icon",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_ID,
                                     g_param_spec_string ("id",
                                                          "id",
                                                          "Unique ID for this snap",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_INSTALL_DATE,
                                     g_param_spec_boxed ("install-date",
                                                         "install-date",
                                                         "Date this snap was installed",
                                                         G_TYPE_DATE_TIME,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_INSTALLED_SIZE,
                                     g_param_spec_int64 ("installed-size",
                                                         "installed-size",
                                                         "Installed size in bytes",
                                                         G_MININT64, G_MAXINT64, 0,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_JAILMODE,
                                     g_param_spec_boolean ("jailmode",
                                                           "jailmode",
                                                           "TRUE if the snap is currently installed in jailmode",
                                                           FALSE,
                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_LICENSE,
                                     g_param_spec_string ("license",
                                                          "license",
                                                          "The snap license as an SPDX expression",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_MEDIA,
                                     g_param_spec_boxed ("media",
                                                         "media",
                                                         "Media associated with this snap",
                                                         G_TYPE_PTR_ARRAY,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_MOUNTED_FROM,
                                     g_param_spec_string ("mounted-from",
                                                          "mounted-from",
                                                          "Path snap is mounted from",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_NAME,
                                     g_param_spec_string ("name",
                                                          "name",
                                                          "The snap name",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_PRICES,
                                     g_param_spec_boxed ("prices",
                                                         "prices",
                                                         "Prices this snap can be purchased for",
                                                         G_TYPE_PTR_ARRAY,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_PRIVATE,
                                     g_param_spec_boolean ("private",
                                                           "private",
                                                           "TRUE if this snap is only available to its author",
                                                           FALSE,
                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_PUBLISHER_DISPLAY_NAME,
                                     g_param_spec_string ("publisher-display-name",
                                                          "publisher-display-name",
                                                          "Display name for snap publisher",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_PUBLISHER_ID,
                                     g_param_spec_string ("publisher-id",
                                                          "publisher-id",
                                                          "ID for snap publisher",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_PUBLISHER_USERNAME,
                                     g_param_spec_string ("publisher-username",
                                                          "publisher-username",
                                                          "Username for snap publisher",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_PUBLISHER_VALIDATION,
                                     g_param_spec_enum ("publisher-validation",
                                                        "publisher-validation",
                                                        "Validation for snap publisher",
                                                        SNAPD_TYPE_PUBLISHER_VALIDATION, SNAPD_PUBLISHER_VALIDATION_UNKNOWN,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_REVISION,
                                     g_param_spec_string ("revision",
                                                          "revision",
                                                          "Revision of this snap",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_SCREENSHOTS,
                                     g_param_spec_boxed ("screenshots",
                                                         "screenshots",
                                                         "Screenshots of this snap",
                                                         G_TYPE_PTR_ARRAY,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_STATUS,
                                     g_param_spec_enum ("status",
                                                        "status",
                                                        "State of this snap",
                                                        SNAPD_TYPE_SNAP_STATUS, SNAPD_SNAP_STATUS_UNKNOWN,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_SUMMARY,
                                     g_param_spec_string ("summary",
                                                          "summary",
                                                          "One line description",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_TITLE,
                                     g_param_spec_string ("title",
                                                          "title",
                                                          "The snap title",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_TRACKING_CHANNEL,
                                     g_param_spec_string ("tracking-channel",
                                                          "tracking-channel",
                                                          "Channel the snap is currently tracking",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_TRACKS,
                                     g_param_spec_boxed ("tracks",
                                                         "tracks",
                                                         "Track names",
                                                         G_TYPE_STRV,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_TRYMODE,
                                     g_param_spec_boolean ("trymode",
                                                           "trymode",
                                                           "TRUE if this snap is installed in try mode",
                                                           FALSE,
                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_SNAP_TYPE,
                                     g_param_spec_enum ("snap-type",
                                                        "snap-type",
                                                        "Snap type",
                                                        SNAPD_TYPE_SNAP_TYPE, SNAPD_SNAP_TYPE_UNKNOWN,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_VERSION,
                                     g_param_spec_string ("version",
                                                          "version",
                                                          "Snap version",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property (gobject_class,
                                     PROP_WEBSITE,
                                     g_param_spec_string ("website",
                                                          "website",
                                                          "Website of developer",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}

static void
snapd_snap_init (SnapdSnap *self)
{
}
