/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*-  */
/*
 * libgfbgraph - GObject library for Facebook Graph API
 * Copyright (C) 2013 Álvaro Peña <alvaropg@gmail.com>
 *
 * GFBGraph 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.
 *
 * GFBGraph 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 GFBGraph.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * SECTION:gfbgraph-connectable
 * @title: GFBGraphConnectable
 * @short_description: Connectable interface for nodes
 * @include: gfbgraph/gfbgraph.h
 *
 * #GFBGraphConnectable interface allow the connection between nodes.
 * You can see the posible (not necesary implemented) connections in
 * the section "Connections" in any node object in the
 * <ulink url="https://developers.facebook.com/docs/reference/api/">Facebook Graph API documentation</ulink>
 **/

#include "gfbgraph-connectable.h"
#include "gfbgraph-node.h"

#include <json-glib/json-glib.h>

G_DEFINE_INTERFACE (GFBGraphConnectable, gfbgraph_connectable, GFBGRAPH_TYPE_NODE)

static void
gfbgraph_connectable_default_init (GFBGraphConnectableInterface *iface)
{
        iface->connections = NULL;

        iface->get_connection_post_params = NULL;
        iface->parse_connected_data = NULL;
}

static GHashTable*
get_connections (GFBGraphConnectableInterface *iface)
{
        /* The GHashTable contains the connections for a node.
         * The key must be the g_type_name() of a GFBGRAPH_TYPE_NODE, and the value
         * must be the function name to call in order to retrieve the nodes connected
         * to the GFBGraphNode indicated by GFBGRAPH_TYPE_NODE.
         */
        GHashTable *connections;

        connections = iface->connections;
        /* If no connections... Why you implement this iface? */
        g_assert (g_hash_table_size (connections) > 0);

        return connections;
}

/**
 * gfbgraph_connectable_get_connection_post_params:
 * @self: a #GFBGraphConnectable.
 * @node_type: a #GType, required a #GFBGRAPH_TYPE_NODE or children.
 *
 * Get the params to be inserted in a request to the Facebook Graph API
 * in order to append the node @self to a node of type @node_type.
 *
 * Returns: (transfer full): A string based #GHashTable with the params and his values or %NULL.
 **/
GHashTable*
gfbgraph_connectable_get_connection_post_params (GFBGraphConnectable *self, GType node_type)
{
        GFBGraphConnectableInterface *iface;

        g_return_val_if_fail (GFBGRAPH_IS_CONNECTABLE (self), NULL);
        g_return_val_if_fail (g_type_is_a (node_type, GFBGRAPH_TYPE_NODE), NULL);
        g_return_val_if_fail (gfbgraph_connectable_is_connectable_to (self, node_type), NULL);

        iface = GFBGRAPH_CONNECTABLE_GET_IFACE (self);
        g_assert (iface->get_connection_post_params != NULL);

        return iface->get_connection_post_params (self, node_type);
}

/**
 * gfbgraph_connectable_parse_connected_data:
 * @self: a #GFBGraphConnectable.
 * @payload: a const #gchar with the response string from the Facebook Graph API.
 * @error: (allow-none): a #GError.
 *
 * Parse the response contained in @payload when a gfbgraph_node_get_connection_nodes() was
 * executed.
 *
 * Returns: (element-type GFBGraphNode) (transfer full): a newly-allocated #GList of #GFBGraphNode created from the @payload or %NULL.
 **/
GList*
gfbgraph_connectable_parse_connected_data (GFBGraphConnectable *self, const gchar *payload, GError **error)
{
        GFBGraphConnectableInterface *iface;

        g_return_val_if_fail (GFBGRAPH_IS_CONNECTABLE (self), NULL);

        iface = GFBGRAPH_CONNECTABLE_GET_IFACE (self);
        g_assert (iface->parse_connected_data != NULL);

        return iface->parse_connected_data (self, payload, error);
}


/**
 * gfbgraph_connectable_is_connectable_to:
 * @self: a #GFBGraphConnectable.
 * @node_type: a #GType, required a #GFBGRAPH_TYPE_NODE or children.
 *
 * Check if @self object, normally a #GFBGraphNode implementing the #GFBGraphConnectable interface,
 * has the possibility to be connected to another node of type @node_type.
 *
 * Returns: %TRUE in case that the @self object can be connected to a node of type @node_type,
 * %FALSE otherwise.
 **/
gboolean
gfbgraph_connectable_is_connectable_to (GFBGraphConnectable *self, GType node_type)
{
        GFBGraphConnectableInterface *iface;
        GHashTable *connections;

        g_return_val_if_fail (GFBGRAPH_IS_CONNECTABLE (self), FALSE);
        g_return_val_if_fail (g_type_is_a (node_type, GFBGRAPH_TYPE_NODE), FALSE);

        iface = GFBGRAPH_CONNECTABLE_GET_IFACE (self);

        connections = get_connections (iface);
        return g_hash_table_contains (connections, g_type_name (node_type));
}

/**
 * gfbgraph_connectable_get_connection_path:
 * @self: a #GFBGraphConnectable.
 * @node_type: a #GType, required a #GFBGRAPH_TYPE_NODE or children.
 *
 * Get the Facebook Graph API function path to retrieve the nodes connected with @node_type
 * managed by the #GFBGraphConnectable object.
 *
 * Returns: (transfer none): a const #gchar with the function path or %NULL.
 **/
const gchar*
gfbgraph_connectable_get_connection_path (GFBGraphConnectable *self, GType node_type)
{
        GFBGraphConnectableInterface *iface;
        GHashTable *connections;

        g_return_val_if_fail (GFBGRAPH_IS_CONNECTABLE (self), NULL);
        g_return_val_if_fail (g_type_is_a (node_type, GFBGRAPH_TYPE_NODE), NULL);
        g_return_val_if_fail (gfbgraph_connectable_is_connectable_to (self, node_type), NULL);

        iface = GFBGRAPH_CONNECTABLE_GET_IFACE (self);

        connections = get_connections (iface);
        return (const gchar *) g_hash_table_lookup (connections, g_type_name (node_type));
}

/**
 * gfbgraph_connectable_default_parse_connected_data:
 * @self: a #GFBGraphConnectable.
 * @payload: a const #gchar with the response string from the Facebook Graph API.
 * @error: (allow-none): a #GError or %NULL.
 *
 * In most cases, #GFBGraphConnectable implementers can use this function in order to parse
 * the response when a gfbgraph_node_get_connection_nodes() is executed and the
 * gfbgraph_connectable_parse_connected_data() was called.
 *
 * Normally, Facebook Graph API returns the connections in the same way, using JSON objects,
 * with a root object called "data".
 *
 * Returns: (element-type GFBGraphNode) (transfer full): a newly-allocated #GList of #GFBGraphNode with the same #GType as @self.
 **/
GList*
gfbgraph_connectable_default_parse_connected_data (GFBGraphConnectable *self, const gchar *payload, GError **error)
{
        GList *nodes_list = NULL;
        JsonParser *jparser;
        GType node_type;

        node_type = G_OBJECT_TYPE (self);

        jparser = json_parser_new ();
        if (json_parser_load_from_data (jparser, payload, -1, error)) {
                JsonNode *root_jnode;
                JsonObject *main_jobject;
                JsonArray *nodes_jarray;
                int i = 0;

                root_jnode = json_parser_get_root (jparser);
                main_jobject = json_node_get_object (root_jnode);
                nodes_jarray = json_object_get_array_member (main_jobject, "data");
                for (i = 0; i < json_array_get_length (nodes_jarray); i++) {
                        JsonNode *jnode;
                        GFBGraphNode *node;

                        jnode = json_array_get_element (nodes_jarray, i);
                        node = GFBGRAPH_NODE (json_gobject_deserialize (node_type, jnode));
                        nodes_list = g_list_append (nodes_list, node);
                }
        }

        g_clear_object (&jparser);

        return nodes_list;
}
