/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008
   Free Software Foundation, Inc.

   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 library; if not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif

#include <property0.h>
#include <mailutils/errno.h>
#include <mailutils/assoc.h>
#include <stdlib.h>

static void
prop_free_value (void *data)
{
  struct property_item *item = data;
  free (item->value);
}

int
mu_property_create (mu_property_t *pp, void *owner)
{
  int rc;
  mu_property_t prop;
  if (pp == NULL)
    return MU_ERR_OUT_PTR_NULL;
  prop = calloc (1, sizeof *prop);
  if (prop == NULL)
    return ENOMEM;
  rc = mu_assoc_create (&prop->assoc, sizeof (struct property_item), 0);
  if (rc)
    {
      free (prop);
      return rc;
    }
  mu_assoc_set_free (prop->assoc, prop_free_value);
  mu_monitor_create (&prop->lock, 0, prop);
  prop->owner = owner;
  *pp = prop;
  return 0;
}

void
mu_property_destroy (mu_property_t *pp, void *owner)
{
  if (pp && *pp)
    {
      mu_property_t prop = *pp;
      if (prop->owner == owner)
	{
	  /* Destroy the list and is properties.  */
	  mu_monitor_wrlock (prop->lock);
	  mu_assoc_destroy (&prop->assoc);
	  mu_monitor_unlock (prop->lock);
	  mu_monitor_destroy (&prop->lock, prop);
	  free (prop);
	}
      *pp = NULL;
    }
}

void *
mu_property_get_owner (mu_property_t prop)
{
  return (prop == NULL) ? NULL : prop->owner;
}

int
mu_property_set_value (mu_property_t prop, const char *key, const char *value,
		       int overwrite)
{
  struct property_item *item;
  int rc;

  if (!prop)
    return EINVAL;
  rc = mu_assoc_ref_install (prop->assoc, key, (void **)&item);
  if (rc == MU_ERR_NOENT)
    {
      item->value = strdup (value);
    }
  else if (overwrite)
    {
      free (item->value);
      item->value = strdup (value);
    }
  else
    return 0;
  
  if (!item->value)
    {
      mu_assoc_remove (prop->assoc, key);
      return ENOMEM;
    }

  return 0;
}
  
int
mu_property_sget_value (mu_property_t prop, const char *key,
			const char **buffer)
{
  struct property_item *item;

  if (!prop)
    return EINVAL;
  item = mu_assoc_ref (prop->assoc, key);
  if (item == NULL)
    return MU_ERR_NOENT;
  if (buffer)
    *buffer = item->value;
  return 0;
}

int
mu_property_aget_value (mu_property_t prop, const char *key,
			char **buffer)
{
  const char *value;
  int rc = mu_property_sget_value (prop, key, &value);
  if (rc == 0)
    {
      if ((*buffer = strdup (value)) == NULL)
	return ENOMEM;
    }
  return rc;
}

int
mu_property_get_value (mu_property_t prop, const char *key, char *buffer,
		       size_t buflen, size_t *n)
{
  size_t len = 0;
  const char *value;
  int rc = mu_property_sget_value (prop, key, &value);
  if (rc == 0)
    {
      len = strlen (value) + 1;
      if (buffer && buflen)
	{
	  if (buflen < len)
	    len = buflen;
	  len--;
	  memcpy (buffer, value, len);
	  buffer[len] = 0;
	}
    }
  if (n)
    *n = len;
  return rc;
}

int
mu_property_is_set (mu_property_t prop, const char *key)
{
  struct property_item *item = mu_assoc_ref (prop->assoc, key);
  return (item == NULL) ? 0 : 1;
}

int
mu_property_set (mu_property_t prop, const char *key)
{
  return mu_property_set_value (prop, key, "", 1);
}

int
mu_property_unset (mu_property_t prop, const char *key)
{
  if (!prop)
    return EINVAL;
  return mu_assoc_remove (prop->assoc, key);
}