/* libinfinity - a GObject-based infinote implementation
 * Copyright (C) 2007-2015 Armin Burgmeier <armin@arbur.net>
 *
 * 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 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, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

/**
 * SECTION:inf-communication-hosted-group
 * @title: InfCommunicationHostedGroup
 * @short_description: Communication group opened by the local host
 * @include: libinfinity/communication/inf-communication-hosted-group.h
 * @stability: Unstable
 * @see_also: #InfCommunicationGroup, #InfCommunicationManager
 *
 * #InfCommunicationHostedGroup is a #InfCommunicationHostedGroup opened on
 * the local host. It allows adding other hosts to the group via
 * inf_communication_hosted_group_add_member(), and to remove hosts via
 * inf_communication_hosted_group_remove_member().
 **/

#include <libinfinity/communication/inf-communication-hosted-group.h>
#include <libinfinity/communication/inf-communication-group-private.h>

typedef struct _InfCommunicationHostedGroupPrivate
  InfCommunicationHostedGroupPrivate;
struct _InfCommunicationHostedGroupPrivate {
  GPtrArray* methods;
};

#define INF_COMMUNICATION_HOSTED_GROUP_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), INF_COMMUNICATION_TYPE_HOSTED_GROUP, InfCommunicationHostedGroupPrivate))

G_DEFINE_TYPE_WITH_CODE(InfCommunicationHostedGroup, inf_communication_hosted_group, INF_COMMUNICATION_TYPE_GROUP,
  G_ADD_PRIVATE(InfCommunicationHostedGroup))

static const gchar*
inf_communication_hosted_group_get_method(InfCommunicationGroup* group,
                                          unsigned int index)
{
  InfCommunicationHostedGroup* hosted_group;
  InfCommunicationHostedGroupPrivate* priv;

  hosted_group = INF_COMMUNICATION_HOSTED_GROUP(group);
  priv = INF_COMMUNICATION_HOSTED_GROUP_PRIVATE(hosted_group);

  if(index < priv->methods->len)
    return g_ptr_array_index(priv->methods, index);

  /* fallback to central method */
  if(index == priv->methods->len)
    return "central";

  return NULL;
}

static gchar*
inf_communication_hosted_group_get_publisher_id(InfCommunicationGroup* group,
                                                InfXmlConnection* for_conn)
{
  gchar* local_id;
  g_object_get(G_OBJECT(for_conn), "local-id", &local_id, NULL);
  return local_id;
}

/*
 * GObject overrides.
 */

static void
inf_communication_hosted_group_init(InfCommunicationHostedGroup* group)
{
  InfCommunicationHostedGroupPrivate* priv;
  priv = INF_COMMUNICATION_HOSTED_GROUP_PRIVATE(group);

  priv->methods = g_ptr_array_new();
}

static void
inf_communication_hosted_group_finalize(GObject* object)
{
  InfCommunicationHostedGroup* group;
  InfCommunicationHostedGroupPrivate* priv;
  guint i;

  group = INF_COMMUNICATION_HOSTED_GROUP(object);
  priv = INF_COMMUNICATION_HOSTED_GROUP_PRIVATE(group);

  for(i = 0; i < priv->methods->len; ++ i)
    g_free(g_ptr_array_index(priv->methods, i));
  g_ptr_array_free(priv->methods, TRUE);

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

/*
 * GType registration.
 */

static void
inf_communication_hosted_group_class_init(
  InfCommunicationHostedGroupClass* hosted_group_class)
{
  GObjectClass* object_class;
  InfCommunicationGroupClass* group_class;

  object_class = G_OBJECT_CLASS(hosted_group_class);
  group_class = INF_COMMUNICATION_GROUP_CLASS(hosted_group_class);

  object_class->finalize = inf_communication_hosted_group_finalize;

  group_class->get_method = inf_communication_hosted_group_get_method;
  group_class->get_publisher_id =
    inf_communication_hosted_group_get_publisher_id;
}

/*
 * Public API.
 */

/**
 * inf_communication_hosted_group_add_method:
 * @group: A #InfCommunicationHostedGroup.
 * @method: The method name to add.
 *
 * Adds a method to the hosted group. When a connection from a given network
 * is added to the group the first time, a #InfCommunicationMethod is
 * instantiated to handle messaging for the group within this network.
 * The first method added will be tried first. If the communication manager
 * does support it (meaning inf_communication_manager_get_factory_for() for
 * the connection's network and the chosen method returns non-%NULL), then it
 * will be used, otherwise the next method will be tried, etc. If no method
 * is supported, or no methods are added to the group, then the "central"
 * method will be used as a fallback.
 */
void
inf_communication_hosted_group_add_method(InfCommunicationHostedGroup* group,
                                          const gchar* method)
{
  InfCommunicationHostedGroupPrivate* priv;

  g_return_if_fail(INF_COMMUNICATION_IS_HOSTED_GROUP(group));
  g_return_if_fail(method != NULL);

  priv = INF_COMMUNICATION_HOSTED_GROUP_PRIVATE(group);
  g_ptr_array_add(priv->methods, g_strdup(method));
}

/**
 * inf_communication_hosted_group_add_member:
 * @group: A #InfCommunicationGroup.
 * @connection: A #InfXmlConnection to add to group.
 *
 * Adds @connection as a member to @group. On the remote site, a
 * #InfCommunicationJoinedGroup with the same name and method used for
 * @connection (see inf_communication_group_get_method_for_connection())
 * needs to be created for successful communication.
 */
void
inf_communication_hosted_group_add_member(InfCommunicationHostedGroup* group,
                                          InfXmlConnection* connection)
{
  g_return_if_fail(INF_COMMUNICATION_IS_HOSTED_GROUP(group));
  g_return_if_fail(INF_IS_XML_CONNECTION(connection));

  g_return_if_fail(
    !inf_communication_group_is_member(
      INF_COMMUNICATION_GROUP(group),
      connection
    )
  );

  _inf_communication_group_add_member(
    INF_COMMUNICATION_GROUP(group),
    connection
  );
}

/**
 * inf_communication_hosted_group_remove_member:
 * @grp: A #InfCommunicationGroup.
 * @connection: The @InfXmlConnection to remove from the group.
 *
 * Removes @connection's membership from @group. On the remote site, the
 * corresponding #InfCommunicationJoinedGroup needs to be freed.
 */
void
inf_communication_hosted_group_remove_member(InfCommunicationHostedGroup* grp,
                                             InfXmlConnection* connection)
{
  g_return_if_fail(INF_COMMUNICATION_IS_HOSTED_GROUP(grp));
  g_return_if_fail(INF_IS_XML_CONNECTION(connection));

  g_return_if_fail(
    inf_communication_group_is_member(
      INF_COMMUNICATION_GROUP(grp),
      connection
    )
  );

  _inf_communication_group_remove_member(
    INF_COMMUNICATION_GROUP(grp),
    connection
  );
}

/* vim:set et sw=2 ts=2: */
