/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 1999-2025 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, see
   <http://www.gnu.org/licenses/>. */

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

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

#include <mailutils/sys/list.h>
#include <mailutils/sys/iterator.h>
#include <mailutils/errno.h>

int
mu_iterator_create (mu_iterator_t *piterator, void *owner)
{
  mu_iterator_t iterator;
  if (piterator == NULL)
    return MU_ERR_OUT_PTR_NULL;
  if (owner == NULL)
    return EINVAL;
  iterator = calloc (1, sizeof (*iterator));
  if (iterator == NULL)
    return ENOMEM;
  iterator->owner = owner;
  *piterator = iterator;
  return 0;
}

int
mu_iterator_set_first (mu_iterator_t itr, int (*first) (void *))
{
  if (!itr)
    return EINVAL;
  itr->first = first;
  return 0;
}

int
mu_iterator_set_next (mu_iterator_t itr, int (*next) (void *))
{
  if (!itr)
    return EINVAL;
  itr->next = next;
  return 0;
}

int
mu_iterator_set_getitem (mu_iterator_t itr,
			 int (*getitem) (void *, void **, const void **))
{
  if (!itr)
    return EINVAL;
  itr->getitem = getitem;
  return 0;
}

int
mu_iterator_set_finished_p (mu_iterator_t itr, int (*finished_p) (void *))
{
  if (!itr)
    return EINVAL;
  itr->finished_p = finished_p;
  return 0;
}

int
mu_iterator_set_delitem (mu_iterator_t itr,
			 int (*delitem) (void *, void *))
{
  if (!itr)
    return EINVAL;
  itr->delitem = delitem;
  return 0;
}

int
mu_iterator_set_itrctl (mu_iterator_t itr,
			int (*itrctl) (void *,
				       enum mu_itrctl_req,
				       void *))
{
  if (!itr)
    return EINVAL;
  itr->itrctl = itrctl;
  return 0;
}

int
mu_iterator_set_dataptr (mu_iterator_t itr, void *(*dataptr) (void *))
{
  if (!itr)
    return EINVAL;
  itr->dataptr = dataptr;
  return 0;
}

int
mu_iterator_set_destroy (mu_iterator_t itr, int (*destroy) (mu_iterator_t, void *))
{
  if (!itr)
    return EINVAL;
  itr->destroy = destroy;
  return 0;
}

int
mu_iterator_set_dup (mu_iterator_t itr, int (*dup) (void **ptr, void *data))
{
  if (!itr)
    return EINVAL;
  itr->dup = dup;
  return 0;
}



int
mu_iterator_dup (mu_iterator_t *piterator, mu_iterator_t orig)
{
  mu_iterator_t iterator;
  int status;

  if (piterator == NULL)
    return MU_ERR_OUT_PTR_NULL;
  if (orig == NULL)
    return EINVAL;

  status = mu_iterator_create (&iterator, orig->owner);
  if (status)
    return status;

  status = orig->dup (&iterator->owner, orig->owner);
  if (status)
    {
      free (iterator);
      return status;
    }
  iterator->is_advanced = orig->is_advanced;
  iterator->dup = orig->dup;
  iterator->destroy = orig->destroy;
  iterator->first = orig->first;
  iterator->next = orig->next;
  iterator->getitem = orig->getitem;
  iterator->delitem = orig->delitem;
  iterator->finished_p = orig->finished_p;
  iterator->itrctl = orig->itrctl;

  *piterator = iterator;
  return 0;
}

void
mu_iterator_destroy (mu_iterator_t *piterator)
{
  if (!piterator || !*piterator)
    return;

  if ((*piterator)->destroy)
    (*piterator)->destroy (*piterator, (*piterator)->owner);

  free (*piterator);
  *piterator = NULL;
}

int
mu_iterator_first (mu_iterator_t iterator)
{
  iterator->is_advanced = 0;
  return iterator->first (iterator->owner);
}

int
mu_iterator_next (mu_iterator_t iterator)
{
  int status = 0;
  if (!iterator->is_advanced)
    status = iterator->next (iterator->owner);
  iterator->is_advanced = 0;
  return status;
}

int
mu_iterator_skip (mu_iterator_t iterator, ssize_t count)
{
  int status;
  if (count < 0)
    return ENOSYS; /* Need prev method */
  while (count--)
    if ((status = mu_iterator_next (iterator)))
      break;
  return status;
}

int
mu_iterator_skip_while (mu_iterator_t iterator,
			int (*pred) (void *, void *),
			void *data)
{
  int status;
  if (!iterator || !pred)
    return EINVAL;
  do
    {
      if ((status = mu_iterator_next (iterator)) == 0)
	{
	  void *item;
	  if ((status = mu_iterator_current  (iterator, &item)) == 0)
	    if (!pred (item, data))
	      break;
	}
    }
  while (status == 0);

  return status;
}

int
mu_iterator_skip_until (mu_iterator_t iterator,
			int (*pred) (void *, void *),
			void *data)
{
  int status;
  if (!iterator || !pred)
    return EINVAL;
  do
    {
      if ((status = mu_iterator_next (iterator)) == 0)
	{
	  void *item;
	  if ((status = mu_iterator_current  (iterator, &item)) == 0)
	    if (pred (item, data))
	      break;
	}
    }
  while (status == 0);

  return status;
}


int
mu_iterator_current (mu_iterator_t iterator, void **pitem)
{
  return mu_iterator_current_kv (iterator, NULL, pitem);
}

int
mu_iterator_current_kv (mu_iterator_t iterator,
			const void **pkey, void **pitem)
{
  void *ptr;
  int rc = iterator->getitem (iterator->owner, &ptr, pkey);
  if (rc == 0)
    {
      if (iterator->dataptr)
	*pitem = iterator->dataptr (ptr);
      else
	*pitem = ptr;
    }
  return rc;
}

int
mu_iterator_is_done (mu_iterator_t iterator)
{
  if (iterator == NULL)
    return 1;
  return iterator->finished_p (iterator->owner);
}

int
iterator_get_owner (mu_iterator_t iterator, void **powner)
{
  if (!iterator)
    return EINVAL;
  if (!powner)
    return MU_ERR_OUT_PTR_NULL;
  *powner = iterator->owner;
  return 0;
}

void
mu_iterator_delitem (mu_iterator_t iterator, void *itm)
{
  for (; iterator; iterator = iterator->next_itr)
    {
      if (iterator->delitem)
	{
	  switch (iterator->delitem (iterator->owner, itm))
	    {
	    case MU_ITR_DELITEM_NEXT:
	      iterator->next (iterator->owner);
	    case MU_ITR_DELITEM_ADVANCE:
	      iterator->is_advanced++;
	    }
	}
    }
}

int
mu_iterator_attach (mu_iterator_t *root, mu_iterator_t iterator)
{
  iterator->next_itr = *root;
  *root = iterator;
  return 0;
}

int
mu_iterator_detach (mu_iterator_t *root, mu_iterator_t iterator)
{
  mu_iterator_t itr, prev;

  for (itr = *root, prev = NULL; itr; prev = itr, itr = itr->next_itr)
    if (iterator == itr)
      break;

  if (itr)
    {
      if (prev)
	prev->next_itr = itr->next_itr;
      else
	*root = itr->next_itr;
    }

  return 0;
}

int
mu_iterator_ctl (mu_iterator_t iterator, enum mu_itrctl_req req, void *arg)
{
  if (!iterator)
    return EINVAL;
  if (!iterator->itrctl)
    return ENOSYS;
  return iterator->itrctl (iterator->owner, req, arg);
}
