/************************************************************\
 * Copyright (C) 2004 The Regents of the University of California.
 * (c.f. DISCLAIMER, COPYING)
 *
 * This file is part of PowerMan, a remote power management program.
 * For details, see https://github.com/chaos/powerman.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
\************************************************************/

/* Args used to be stored in a List, but gprof showed that very large
 * configurations spent a lot of time doing linear search of arg list for
 * each arg->state update.  The List was traded for a hash, but as we still
 * want to iterate through args in the client in order, we keep the hostlist
 * representation of the nodes in the ArgList, and use it to implement an
 * 'iterator' interface.
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include "list.h"
#include "xmalloc.h"
#include "hostlist.h"
#include "hash.h"
#include "arglist.h"

struct arglist_iterator {
    hostlist_iterator_t itr;
    ArgList arglist;
};

struct arglist {
    hash_t args;
    hostlist_t hl;
    int refcount;               /* free when refcount == 0 */
};

static void _destroy_arg(Arg * arg)
{
    assert(arg != NULL);
    assert(arg->node != NULL);
    xfree(arg->node);
    if (arg->val)
        xfree(arg->val);
    xfree(arg);
}

static Arg *_create_arg(char *node)
{
    Arg *arg = (Arg *) xmalloc(sizeof(Arg));

    arg->node = xstrdup(node);
    arg->state = ST_UNKNOWN;
    arg->val = NULL;

    return arg;
}

ArgList arglist_create(hostlist_t hl)
{
    ArgList new = (ArgList) xmalloc(sizeof(struct arglist));
    hostlist_iterator_t itr;
    char *node;
    int hash_size;

    new->refcount = 1;
    hash_size = hostlist_count(hl); /* reasonable? */
    new->args = hash_create(hash_size, (hash_key_f)hash_key_string,
            (hash_cmp_f)strcmp, (hash_del_f)_destroy_arg);

    if ((itr = hostlist_iterator_create(hl)) == NULL) {
        arglist_unlink(new);
        return NULL;
    }
    while ((node = hostlist_next(itr)) != NULL) {
        Arg *arg = _create_arg(node);

        hash_insert(new->args, arg->node, arg);
        free(node); /* hostlist_next strdups returned string */
    }
    hostlist_iterator_destroy(itr);
    new->hl = hostlist_copy(hl);

    return new;
}

void arglist_unlink(ArgList arglist)
{
    if (--arglist->refcount == 0) {
        hash_destroy(arglist->args);
        hostlist_destroy(arglist->hl);
        xfree(arglist);
    }
}

ArgList arglist_link(ArgList arglist)
{
    arglist->refcount++;

    return arglist;
}

Arg *arglist_find(ArgList arglist, char *node)
{
    Arg *arg = NULL;

    if (node != NULL)
        arg = hash_find(arglist->args, node);

    return arg;
}

ArgListIterator arglist_iterator_create(ArgList arglist)
{
    ArgListIterator itr = (ArgListIterator)xmalloc(sizeof(struct arglist_iterator));

    itr->arglist = arglist;
    itr->itr = hostlist_iterator_create(arglist->hl);

    return itr;
}

void arglist_iterator_destroy(ArgListIterator itr)
{
    hostlist_iterator_destroy(itr->itr);
    xfree(itr);
}

Arg *arglist_next(ArgListIterator itr)
{
    Arg *arg = NULL;
    char *node;

    node = hostlist_next(itr->itr);
    if (node != NULL) {
        arg = hash_find(itr->arglist->args, node);
        free(node); /* hostlist_next strdups returned string */
    }

    return arg;
}

/*
 * vi:tabstop=4 shiftwidth=4 expandtab
 */
