/* Gamin
 * Copyright (C) 2003 James Willcox, Corey Bowers
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include "server_config.h"
#include <string.h>
#include <glib.h>
#include "gam_event.h"
#include "gam_node.h"
#include "gam_error.h"
#include "gam_server.h"

/**
 * Create a new node
 *
 * @param path the path the node will represent
 * @param sub an initial GamSubscription for the node, could be NULL
 * @param is_dir whether the node is a directory or not
 * @returns the new node
 */
GamNode *
gam_node_new(const char *path, GamSubscription * sub, gboolean is_dir)
{
    GamNode *node;
    node = g_new0(GamNode, 1);

    node->path = g_strdup(path);
    if (sub)
        node->subs = g_list_prepend(NULL, sub);
    else
        node->subs = NULL;

    node->is_dir = is_dir;
    node->flags = 0;
	node->checks = 0;

    node->poll_time = gam_fs_get_poll_timeout (path);
    node->mon_type = gam_fs_get_mon_type (path);
	GAM_DEBUG(DEBUG_INFO, "g_n_n: node for %s using %s with poll timeout of %d\n", path, node->mon_type == GFS_MT_KERNEL ? "kernel" : "poll", node->poll_time);
    return node;
}

/**
 * Frees a node
 *
 * @param node the node
 */
void
gam_node_free(GamNode * node)
{
    g_assert(node != NULL);
    g_assert(node->subs == NULL);

    g_free(node->path);
    g_free(node);
}

/**
 * Retrieves the parent of a given node
 *
 * @param node the node
 * @returns the parent, or NULL if node has no parent
 */
GamNode *
gam_node_parent(GamNode * node)
{
    GamNode *ret = NULL;

    g_assert(node);

    if (node->node && node->node->parent)
        ret = (GamNode *) node->node->parent->data;

    return ret;
}

/**
 * Returns whether a node is a directory or not
 *
 * @param node the node
 * @returns TRUE if the node is a directory, FALSE otherwise
 */
gboolean
gam_node_is_dir(GamNode * node)
{
    g_assert(node);
    return(node->is_dir);
}

/**
 * Sets whether a node is a directory
 *
 * @param node the node
 * @param is_dir whether the node is a directory
 */
void
gam_node_set_is_dir(GamNode * node, gboolean is_dir)
{
    g_assert(node);
    node->is_dir = is_dir;
}

/**
 * Returns the path associated with a node
 *
 * @param node the node
 * @returns the path.  The caller must keep a reference to node until
 * it has finished with the string.  If it must keep it longer, it
 * should makes its own copy.  The returned string must not be freed.
 */
const char *
gam_node_get_path(GamNode * node)
{
    g_assert(node);
    return node->path;
}

/**
 * Returns the list of subscriptions to a node
 *
 * @param node the node
 * @returns the list of #GamSubscription to the node
 */
GList *
gam_node_get_subscriptions(GamNode * node)
{
    g_assert(node);
    return node->subs;
}

/**
 * Returns whether a node has any directory subscriptions
 *
 * @param node the node
 * @returns TRUE if the node has directory subscriptions, FALSE otherwise
 */
gboolean
gam_node_has_dir_subscriptions(GamNode * node)
{
    GList *s;

    g_assert (node);
    if (!(node->is_dir))
        return(FALSE);
    for (s = node->subs;s != NULL;s = s->next) {
        if (gam_subscription_is_dir((GamSubscription *) s->data))
	    return(TRUE);
    }
    return(FALSE);
}

/**
 * Adds a subscription to a node
 *
 * @param node the node
 * @param sub the subscription
 * @returns TRUE on success, FALSE otherwise
 */
gboolean
gam_node_add_subscription(GamNode * node, GamSubscription * sub)
{
    g_assert(node);
    g_assert(sub);
    g_assert(!g_list_find(node->subs, sub));

    node->subs = g_list_prepend(node->subs, sub);

    return TRUE;
}

/**
 * Removes a subscription from a node
 *
 * @param node the node
 * @param sub the subscription to remove
 * @returns TRUE if the subscription was removed, FALSE otherwise
 */
gboolean
gam_node_remove_subscription(GamNode * node, GamSubscription * sub)
{
    g_assert(node);
    g_assert(g_list_find (node->subs, sub));

    node->subs = g_list_remove_all(node->subs, sub);

    return TRUE;
}

/**
 * Sets the GNode associated with a node.  Should only be used by MdTree
 *
 * @param node the node
 * @param gnode the GNode
 */
void
gam_node_set_node(GamNode * node, GNode * gnode)
{
    g_assert(node);
    node->node = gnode;
}

/**
 * Gets the GNode associated with a node.  Should only be used by MdTree
 *
 * @param node the node
 * @returns the GNode
 */
GNode *
gam_node_get_node(GamNode * node)
{
    g_assert(node);
    return node->node;
}

/**
 * Set a flag on a node
 *
 * @param node the node
 * @param flag the flag to set
 */
void
gam_node_set_flag(GamNode * node, int flag)
{
    g_assert(node);
    /* Make sure we set exactly one flag.  */
    g_assert((flag & (flag - 1)) == 0);
    node->flags |= flag;
}

/**
 * Clears a flag from a node
 *
 * @param node the node
 * @param flag the flag
 */
void
gam_node_unset_flag(GamNode * node, int flag)
{
    g_assert(node);
    /* Make sure we clear exactly one flag.  */
    g_assert((flag & (flag - 1)) == 0);
    node->flags &= ~flag;
}

/**
 * Checks whether a flag is set on a node
 *
 * @param node the node
 * @param flag the flag
 * @returns TRUE if the flag is set, FALSE otherwise
 */
gboolean
gam_node_has_flag(GamNode * node, int flag)
{
    g_assert(node);
    /* Check exactly one flag.  */
    g_assert((flag & (flag - 1)) == 0);
    return node->flags & flag;
}

/**
 * Set a pflag on a node
 *
 * @param node the node
 * @param flag the flag to set
 */
void
gam_node_set_pflag(GamNode * node, int flag)
{
    g_assert(node);
    /* Make sure we set exactly one flag.  */
    g_assert((flag & (flag - 1)) == 0);
    node->pflags |= flag;
}

/**
 * Clears a pflag from a node
 *
 * @param node the node
 * @param flag the flag
 */
void
gam_node_unset_pflag(GamNode * node, int flag)
{
    g_assert(node);
    /* Make sure we clear exactly one flag.  */
    g_assert((flag & (flag - 1)) == 0);
    node->pflags &= ~flag;
}

/**
 * Checks whether a pflag is set on a node
 *
 * @param node the node
 * @param flag the flag
 * @returns TRUE if the flag is set, FALSE otherwise
 */
gboolean
gam_node_has_pflag(GamNode * node, int flag)
{
    g_assert(node);
    /* Check exactly one flag.  */
    g_assert((flag & (flag - 1)) == 0);
    return node->pflags & flag;
}

/**
 * Set the pflags on a node
 *
 * @param node the node
 * @param flags the flags
 */
void
gam_node_set_pflags(GamNode * node, int flags)
{
    g_assert(node);
    node->pflags = flags;
}


/**
 * Checks whether some pflags are set on a node
 *
 * @param node the node
 * @param flags the flags
 * @returns TRUE if the flag is set, FALSE otherwise
 */
gboolean
gam_node_has_pflags(GamNode * node, int flags)
{
    g_assert(node);
    return node->pflags & flags;
}

void
gam_node_emit_event (GamNode *node, GaminEventType event)
{
	GList *l;
	GamNode *parent;
	GList *subs;
	int is_dir_node = gam_node_is_dir(node);

#ifdef VERBOSE_POLL
	GAM_DEBUG(DEBUG_INFO, "Poll: emit events %d for %s\n", event, gam_node_get_path(node));
#endif
	subs = gam_node_get_subscriptions(node);

	if (subs)
		subs = g_list_copy(subs);

	parent = gam_node_parent(node);
	if (parent) {
		GList *parent_subs = gam_node_get_subscriptions(parent);

		for (l = parent_subs; l; l = l->next) {
			if (!g_list_find(subs, l->data))
				subs = g_list_prepend(subs, l->data);
		}
	}

	gam_server_emit_event(gam_node_get_path(node), is_dir_node, event, subs, 0);

	g_list_free(subs);
}

/** @} */
