/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * Purple 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 "purpleavatar.h"

struct _PurpleAvatar {
	GObject parent;

	char *filename;

	GdkPixbuf *pixbuf;

	gboolean animated;
	GdkPixbufAnimation *animation;

	PurpleTags *tags;
};

enum {
	PROP_0,
	PROP_FILENAME,
	PROP_PIXBUF,
	PROP_ANIMATED,
	PROP_ANIMATION,
	PROP_TAGS,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
purple_avatar_set_filename(PurpleAvatar *avatar, const char *filename) {
	g_return_if_fail(PURPLE_IS_AVATAR(avatar));

	if(g_set_str(&avatar->filename, filename)) {
		g_object_notify_by_pspec(G_OBJECT(avatar), properties[PROP_FILENAME]);
	}
}

static PurpleAvatar *
purple_avatar_new_common(const char *filename, GdkPixbufAnimation *animation) {
	PurpleAvatar *avatar = NULL;

	avatar = g_object_new(PURPLE_TYPE_AVATAR, "filename", filename, NULL);

	if(gdk_pixbuf_animation_is_static_image(animation)) {
		/* If we loaded a static image, grab the static image and set it to our
		 * pixbuf member, clear the animation, and return the new avatar.
		 */

		avatar->pixbuf = gdk_pixbuf_animation_get_static_image(animation);
		g_object_ref(avatar->pixbuf);

		g_clear_object(&animation);
	} else {
		/* If we did load an animation, set the appropriate properties and
		 * return the avatar.
		 */
		avatar->animated = TRUE;
		avatar->animation = animation;
	}

	return avatar;
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
G_DEFINE_FINAL_TYPE(PurpleAvatar, purple_avatar, G_TYPE_OBJECT)

static void
purple_avatar_get_property(GObject *obj, guint param_id, GValue *value,
                           GParamSpec *pspec)
{
	PurpleAvatar *avatar = PURPLE_AVATAR(obj);

	switch(param_id) {
	case PROP_FILENAME:
		g_value_set_string(value, purple_avatar_get_filename(avatar));
		break;
	case PROP_PIXBUF:
		g_value_set_object(value, purple_avatar_get_pixbuf(avatar));
		break;
	case PROP_ANIMATED:
		g_value_set_boolean(value, purple_avatar_get_animated(avatar));
		break;
	case PROP_ANIMATION:
		g_value_set_object(value, purple_avatar_get_animation(avatar));
		break;
	case PROP_TAGS:
		g_value_set_object(value, purple_avatar_get_tags(avatar));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
purple_avatar_set_property(GObject *obj, guint param_id, const GValue *value,
                           GParamSpec *pspec)
{
	PurpleAvatar *avatar = PURPLE_AVATAR(obj);

	switch(param_id) {
	case PROP_FILENAME:
		purple_avatar_set_filename(avatar, g_value_get_string(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
purple_avatar_finalize(GObject *obj) {
	PurpleAvatar *avatar = PURPLE_AVATAR(obj);

	g_clear_pointer(&avatar->filename, g_free);
	g_clear_object(&avatar->pixbuf);
	g_clear_object(&avatar->animation);
	g_clear_object(&avatar->tags);

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

static void
purple_avatar_init(PurpleAvatar *avatar) {
	avatar->tags = purple_tags_new();
}

static void
purple_avatar_class_init(PurpleAvatarClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = purple_avatar_finalize;
	obj_class->get_property = purple_avatar_get_property;
	obj_class->set_property = purple_avatar_set_property;

	/**
	 * PurpleAvatar:filename:
	 *
	 * The filename that this avatar was created from.
	 *
	 * Since: 3.0
	 */
	properties[PROP_FILENAME] = g_param_spec_string(
		"filename", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleAvatar:pixbuf:
	 *
	 * The [class@GdkPixbuf.Pixbuf] of the avatar. If
	 * [property@Purple.Avatar:animated] is %TRUE, this will be a static frame
	 * from the animation.
	 *
	 * Since: 3.0
	 */
	properties[PROP_PIXBUF] = g_param_spec_object(
		"pixbuf", NULL, NULL,
		GDK_TYPE_PIXBUF,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleAvatar:animated:
	 *
	 * Whether or not this avatar is animated.
	 *
	 * Since: 3.0
	 */
	properties[PROP_ANIMATED] = g_param_spec_boolean(
		"animated", NULL, NULL,
		FALSE,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleAvatar:animation:
	 *
	 * The [class@GdkPixbuf.PixbufAnimation] if
	 * [property@Purple.Avatar:animated] is %TRUE.
	 *
	 * Since: 3.0
	 */
	properties[PROP_ANIMATION] = g_param_spec_object(
		"animation", NULL, NULL,
		GDK_TYPE_PIXBUF_ANIMATION,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleAvatar:tags:
	 *
	 * The [class@Purple.Tags] for the avatar.
	 *
	 * Since: 3.0
	 */
	properties[PROP_TAGS] = g_param_spec_object(
		"tags", NULL, NULL,
		PURPLE_TYPE_TAGS,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
PurpleAvatar *
purple_avatar_new_from_filename(const char *filename, GError **error) {
	GdkPixbufAnimation *animation = NULL;
	GError *local_error = NULL;

	g_return_val_if_fail(filename != NULL, NULL);

	animation = gdk_pixbuf_animation_new_from_file(filename, &local_error);
	if(!GDK_IS_PIXBUF_ANIMATION(animation) || local_error != NULL) {
		g_clear_object(&animation);

		g_propagate_error(error, local_error);

		return NULL;
	}

	return purple_avatar_new_common(filename, animation);
}

PurpleAvatar *
purple_avatar_new_from_resource(const char *resource_path, GError **error) {
	GdkPixbufAnimation *animation = NULL;
	GError *local_error = NULL;

	g_return_val_if_fail(resource_path != NULL, NULL);

	animation = gdk_pixbuf_animation_new_from_resource(resource_path,
	                                                   &local_error);
	if(!GDK_IS_PIXBUF_ANIMATION(animation) || local_error != NULL) {
		g_clear_object(&animation);

		g_propagate_error(error, local_error);

		return NULL;
	}

	return purple_avatar_new_common(NULL, animation);
}

const char *
purple_avatar_get_filename(PurpleAvatar *avatar) {
	g_return_val_if_fail(PURPLE_IS_AVATAR(avatar), NULL);

	return avatar->filename;
}

GdkPixbuf *
purple_avatar_get_pixbuf(PurpleAvatar *avatar) {
	g_return_val_if_fail(PURPLE_IS_AVATAR(avatar), NULL);

	if(avatar->animated) {
		return gdk_pixbuf_animation_get_static_image(avatar->animation);
	}

	return avatar->pixbuf;
}

gboolean
purple_avatar_get_animated(PurpleAvatar *avatar) {
	g_return_val_if_fail(PURPLE_IS_AVATAR(avatar), FALSE);

	return avatar->animated;
}

GdkPixbufAnimation *
purple_avatar_get_animation(PurpleAvatar *avatar) {
	g_return_val_if_fail(PURPLE_IS_AVATAR(avatar), NULL);

	return avatar->animation;
}

PurpleTags *
purple_avatar_get_tags(PurpleAvatar *avatar) {
	g_return_val_if_fail(PURPLE_IS_AVATAR(avatar), NULL);

	return avatar->tags;
}
