/* riemann/event.c -- Riemann C client library
 * Copyright (C) 2013  Gergely Nagy <algernon@madhouse-project.org>
 *
 * 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 3 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 program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <riemann/attribute.h>
#include <riemann/event.h>

#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

riemann_event_t *
riemann_event_new (void)
{
  riemann_event_t *event;

  event = (riemann_event_t *)malloc (sizeof (riemann_event_t));
  event__init (event);
  return event;
}

void
riemann_event_free (riemann_event_t *event)
{
  if (!event)
    {
      errno = EINVAL;
      return;
    }

  event__free_unpacked (event, NULL);
}

static void
_riemann_event_set_string (char **str, char *value)
{
  if (*str)
    free (*str);
  *str = strdup (value);
}

static int
_riemann_event_set_va (riemann_event_t *event,
                       riemann_event_field_t first_field, va_list aq)
{
  va_list ap;
  riemann_event_field_t field;

  if (!event)
    return -EINVAL;

  va_copy (ap, aq);
  field = first_field;
  do
    {
      switch (field)
        {
        case RIEMANN_EVENT_FIELD_NONE:
          break;

        case RIEMANN_EVENT_FIELD_TIME:
          event->time = (int64_t) va_arg (ap, int64_t);
          event->has_time = 1;
          break;

        case RIEMANN_EVENT_FIELD_STATE:
          _riemann_event_set_string (&event->state, va_arg (ap, char *));
          break;

        case RIEMANN_EVENT_FIELD_SERVICE:
          _riemann_event_set_string (&event->service, va_arg (ap, char *));
          break;

        case RIEMANN_EVENT_FIELD_HOST:
          _riemann_event_set_string (&event->host, va_arg (ap, char *));
          break;

        case RIEMANN_EVENT_FIELD_DESCRIPTION:
          _riemann_event_set_string (&event->description, va_arg (ap, char *));
          break;

        case RIEMANN_EVENT_FIELD_TAGS:
          {
            char *tag;
            size_t n;

            for (n = 0; n < event->n_tags; n++)
              free (event->tags[n]);
            if (event->tags)
              free (event->tags);
            event->tags = NULL;
            event->n_tags = 0;

            while ((tag = va_arg (ap, char *)) != NULL)
              {
                event->tags =
                  realloc (event->tags, sizeof (char *) * (event->n_tags + 1));
                event->tags[event->n_tags] = strdup (tag);
                event->n_tags++;
              }

            break;
          }

        case RIEMANN_EVENT_FIELD_TTL:
          event->ttl = (float) va_arg (ap, double);
          event->has_ttl = 1;
          break;

        case RIEMANN_EVENT_FIELD_ATTRIBUTES:
          {
            riemann_attribute_t *attrib;
            size_t n;

            for (n = 0; n < event->n_attributes; n++)
              riemann_attribute_free (event->attributes[n]);
            if (event->attributes)
              free (event->attributes);
            event->attributes = NULL;
            event->n_attributes = 0;

            while ((attrib = va_arg (ap, riemann_attribute_t *)) != NULL)
              {
                event->attributes =
                  realloc (event->attributes,
                           sizeof (riemann_attribute_t *) * (event->n_attributes + 1));
                event->attributes[event->n_attributes] = attrib;
                event->n_attributes++;
              }

            break;
          }

        case RIEMANN_EVENT_FIELD_METRIC_S64:
          event->metric_sint64 = va_arg (ap, int64_t);
          event->has_metric_sint64 = 1;
          break;

        case RIEMANN_EVENT_FIELD_METRIC_D:
          event->metric_d = va_arg (ap, double);
          event->has_metric_d = 1;
          break;

        case RIEMANN_EVENT_FIELD_METRIC_F:
          event->metric_f = (float) va_arg (ap, double);
          event->has_metric_f = 1;
          break;

        default:
          va_end (ap);
          return -EPROTO;
        }

      if (field != RIEMANN_EVENT_FIELD_NONE)
        field = va_arg (ap, riemann_event_field_t);
    }
  while (field != RIEMANN_EVENT_FIELD_NONE);
  va_end (ap);

  return 0;
}

int
riemann_event_set (riemann_event_t *event, ...)
{
  va_list ap;
  int r;
  riemann_event_field_t first_field;

  va_start (ap, event);
  first_field = va_arg (ap, riemann_event_field_t);
  r = _riemann_event_set_va (event, first_field, ap);
  va_end (ap);
  return r;
}

int
riemann_event_tag_add (riemann_event_t *event, const char *tag)
{
  if (!event || !tag)
    return -EINVAL;

  event->tags =
    realloc (event->tags, sizeof (char *) * (event->n_tags + 1));
  event->tags[event->n_tags] = strdup (tag);
  event->n_tags++;

  return 0;
}

int
riemann_event_attribute_add (riemann_event_t *event,
                             riemann_attribute_t *attrib)
{
  if (!event || !attrib)
    return -EINVAL;

  event->attributes =
    realloc (event->attributes,
             sizeof (riemann_attribute_t *) * (event->n_attributes + 1));
  event->attributes[event->n_attributes] = attrib;
  event->n_attributes++;

  return 0;
}

riemann_event_t *
riemann_event_create (riemann_event_field_t field, ...)
{
  riemann_event_t *event;
  va_list ap;

  event = riemann_event_new ();
  if (!event)
    return NULL;

  va_start (ap, field);
  if (_riemann_event_set_va (event, field, ap) != 0)
    {
      int e = errno;

      va_end (ap);
      riemann_event_free (event);
      errno = e;
      return NULL;
    }
  va_end (ap);

  return event;
}
