/*
  Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
  This file is part of GlusterFS.

  This file is licensed to you under your choice of the GNU Lesser
  General Public License, version 3 or any later version (LGPLv3 or
  later), or the GNU General Public License, version 2 (GPLv2), in all
  cases as published by the Free Software Foundation.
*/

#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif

#include <sys/uio.h>

#include "common-utils.h"
#include "xlator.h"
#include "graph-utils.h"



struct gf_printer {
        ssize_t (*write) (struct gf_printer *gp, char *buf, size_t len);
        void *priv;
        int  len;
};

static ssize_t
gp_write_file (struct gf_printer *gp, char *buf, size_t len)
{
        FILE *f = gp->priv;

        if (fwrite (buf, len, 1, f) != 1) {
                gf_log ("graph-print", GF_LOG_ERROR, "fwrite failed (%s)",
                        strerror (errno));

                return -1;
        }

        return len;
}

static ssize_t
gp_write_buf (struct gf_printer *gp, char *buf, size_t len)
{
        struct iovec *iov = gp->priv;

        if (iov->iov_len < len) {
                gf_log ("graph-print", GF_LOG_ERROR, "buffer full");

                return -1;
        }

        memcpy (iov->iov_base, buf, len);
        iov->iov_base += len;
        iov->iov_len  -= len;

        return len;
}

static int
gpprintf (struct gf_printer *gp, const char *format, ...)
{
        va_list arg;
        char *str = NULL;
        int ret = 0;

        va_start (arg, format);
        ret = gf_vasprintf (&str, format, arg);
        va_end (arg);

        if (ret < 0)
                return ret;

        ret = gp->write (gp, str, ret);

        GF_FREE (str);

        return ret;
}

#define GPPRINTF(gp, fmt, ...) do {                             \
                ret = gpprintf (gp, fmt, ## __VA_ARGS__);       \
                if (ret == -1)                                  \
                        goto out;                               \
                else                                            \
                        gp->len += ret;                             \
        } while (0)

static int
_print_volume_options (dict_t *d, char *k, data_t *v,
                           void *tmp)
{
        struct gf_printer *gp  = tmp;
        int                ret = 0;
        GPPRINTF (gp, "    option %s %s\n", k, v->data);
        return 0;
out:
        /* means, it is a failure */
        return -1;
}

static int
glusterfs_graph_print (struct gf_printer *gp, glusterfs_graph_t *graph)
{
        xlator_t      *trav = NULL;
        xlator_list_t *xch = NULL;
        int            ret = 0;
        ssize_t        len = 0;

        if (!graph->first)
                return 0;

        for (trav = graph->first; trav->next; trav = trav->next);
        for (; trav; trav = trav->prev) {
                GPPRINTF (gp, "volume %s\n    type %s\n", trav->name,
                          trav->type);

                ret = dict_foreach (trav->options, _print_volume_options, gp);
                if (ret)
                        goto out;

                if (trav->children) {
                        GPPRINTF (gp, "    subvolumes");

                        for (xch = trav->children; xch; xch = xch->next)
                                GPPRINTF (gp, " %s", xch->xlator->name);

                        GPPRINTF (gp, "\n");
                }

                GPPRINTF (gp, "end-volume\n");
                if (trav != graph->first)
                        GPPRINTF (gp, "\n");
        }

out:
        len = gp->len;
        if (ret == -1) {
                gf_log ("graph-print", GF_LOG_ERROR, "printing failed");

                return -1;
        }

        return len;

#undef GPPRINTF
}

int
glusterfs_graph_print_file (FILE *file, glusterfs_graph_t *graph)
{
        struct gf_printer gp = { .write = gp_write_file,
                                 .priv  = file
        };

        return glusterfs_graph_print (&gp, graph);
}

char *
glusterfs_graph_print_buf (glusterfs_graph_t *graph)
{
        FILE *f = NULL;
        struct iovec iov = {0,};
        int len = 0;
        char *buf = NULL;
        struct gf_printer gp = { .write = gp_write_buf,
                                 .priv  = &iov
        };

        f = fopen ("/dev/null", "a");
        if (!f) {
                gf_log ("graph-print", GF_LOG_ERROR,
                        "cannot open /dev/null (%s)", strerror (errno));

                return NULL;
        }
        len = glusterfs_graph_print_file (f, graph);
        fclose (f);
        if (len == -1)
                return NULL;

        buf = GF_CALLOC (1, len + 1, gf_common_mt_graph_buf);
        if (!buf) {
                return NULL;
        }
        iov.iov_base = buf;
        iov.iov_len  = len;

        len = glusterfs_graph_print (&gp, graph);
        if (len == -1) {
                GF_FREE (buf);

                return NULL;
        }

        return buf;
}
