/*
 * Copyright © 2019 Benjamin Otte
 *
 * 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 2.1 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/>.
 *
 * Authors: Benjamin Otte <otte@gnome.org>
 */

#include "config.h"

#include "gtkcustomfilter.h"

#include "gtkintl.h"
#include "gtktypebuiltins.h"

/**
 * SECTION:gtkcustomfilter
 * @Title: GtkCustomFilter
 * @Short_description: Filtering with callbacks
 *
 * #GtkCustomFilter is a #GtkFilter that uses a callback to determine
 * whether to include an item or not.
 */
struct _GtkCustomFilter
{
  GtkFilter parent_instance;

  GtkCustomFilterFunc match_func;
  gpointer user_data;
  GDestroyNotify user_destroy;
};

G_DEFINE_TYPE (GtkCustomFilter, gtk_custom_filter, GTK_TYPE_FILTER)

static gboolean
gtk_custom_filter_match (GtkFilter *filter,
                         gpointer   item)
{
  GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);

  if (!self->match_func)
    return TRUE;

  return self->match_func (item, self->user_data);
}

static GtkFilterMatch
gtk_custom_filter_get_strictness (GtkFilter *filter)
{
  GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);

  if (!self->match_func)
    return GTK_FILTER_MATCH_ALL;

  return GTK_FILTER_MATCH_SOME;
}

static void
gtk_custom_filter_dispose (GObject *object)
{
  GtkCustomFilter *self = GTK_CUSTOM_FILTER (object);

  if (self->user_destroy)
    self->user_destroy (self->user_data);

  G_OBJECT_CLASS (gtk_custom_filter_parent_class)->dispose (object);
}

static void
gtk_custom_filter_class_init (GtkCustomFilterClass *class)
{
  GtkFilterClass *filter_class = GTK_FILTER_CLASS (class);
  GObjectClass *object_class = G_OBJECT_CLASS (class);

  filter_class->match = gtk_custom_filter_match;
  filter_class->get_strictness = gtk_custom_filter_get_strictness;

  object_class->dispose = gtk_custom_filter_dispose;
}

static void
gtk_custom_filter_init (GtkCustomFilter *self)
{
}

/**
 * gtk_custom_filter_new:
 * @match_func: (nullable): function to filter items
 * @user_data: (nullable): user data to pass to @match_func
 * @user_destroy: destroy notify for @user_data
 *
 * Creates a new filter using the given @match_func to filter
 * items.
 *
 * If @match_func is %NULL, the filter matches all items.
 *
 * If the filter func changes its filtering behavior,
 * gtk_filter_changed() needs to be called.
 *
 * Returns: a new #GtkCustomFilter
 **/
GtkCustomFilter *
gtk_custom_filter_new (GtkCustomFilterFunc match_func,
                       gpointer            user_data,
                       GDestroyNotify      user_destroy)
{
  GtkCustomFilter *result;

  result = g_object_new (GTK_TYPE_CUSTOM_FILTER, NULL);

  gtk_custom_filter_set_filter_func (result, match_func, user_data, user_destroy);

  return result;
}

/**
 * gtk_custom_filter_set_filter_func:
 * @self: a #GtkCustomFilter
 * @match_func: (nullable): function to filter items
 * @user_data: (nullable): user data to pass to @match_func
 * @user_destroy: destroy notify for @user_data
 *
 * Sets (or unsets) the function used for filtering items.
 *
 * If @match_func is %NULL, the filter matches all items.
 *
 * If the filter func changes its filtering behavior,
 * gtk_filter_changed() needs to be called.
 *
 * If a previous function was set, its @user_destroy will be
 * called now.
 **/
void
gtk_custom_filter_set_filter_func (GtkCustomFilter     *self,
                                   GtkCustomFilterFunc  match_func,
                                   gpointer             user_data,
                                   GDestroyNotify       user_destroy)
{
  g_return_if_fail (GTK_IS_CUSTOM_FILTER (self));
  g_return_if_fail (match_func || (user_data == NULL && !user_destroy));

  if (self->user_destroy)
    self->user_destroy (self->user_data);

  self->match_func = match_func;
  self->user_data = user_data;
  self->user_destroy = user_destroy;

  gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_DIFFERENT);
}