/* LIBPIKA - The PIKA Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * pikacontroller.c
 * Copyright (C) 2004 Michael Natterer <mitch@gimp.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 library.  If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <gegl.h>
#include <gtk/gtk.h>

#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikaconfig/pikaconfig.h"

#include "pikawidgetstypes.h"

#include "pikawidgetsmarshal.h"

#define PIKA_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
#include "pikacontroller.h"
#include "pikaicons.h"


/**
 * SECTION: pikacontroller
 * @title: PikaController
 * @short_description: Pluggable PIKA input controller modules.
 *
 * An abstract interface for implementing arbitrary input controllers.
 **/


enum
{
  PROP_0,
  PROP_NAME,
  PROP_STATE
};

enum
{
  EVENT,
  LAST_SIGNAL
};


static void   pika_controller_finalize     (GObject      *object);
static void   pika_controller_set_property (GObject      *object,
                                            guint         property_id,
                                            const GValue *value,
                                            GParamSpec   *pspec);
static void   pika_controller_get_property (GObject      *object,
                                            guint         property_id,
                                            GValue       *value,
                                            GParamSpec   *pspec);


G_DEFINE_TYPE_WITH_CODE (PikaController, pika_controller, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONFIG, NULL))

#define parent_class pika_controller_parent_class

static guint controller_signals[LAST_SIGNAL] = { 0 };


static void
pika_controller_class_init (PikaControllerClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize     = pika_controller_finalize;
  object_class->set_property = pika_controller_set_property;
  object_class->get_property = pika_controller_get_property;

  klass->name                = "Unnamed";
  klass->help_domain         = NULL;
  klass->help_id             = NULL;
  klass->icon_name           = PIKA_ICON_CONTROLLER;

  klass->get_n_events        = NULL;
  klass->get_event_name      = NULL;
  klass->event               = NULL;

  g_object_class_install_property (object_class, PROP_NAME,
                                   g_param_spec_string ("name",
                                                        "Name",
                                                        "The controller's name",
                                                        "Unnamed Controller",
                                                        PIKA_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));

  g_object_class_install_property (object_class, PROP_STATE,
                                   g_param_spec_string ("state",
                                                        "State",
                                                        "The controller's state, as human-readable string",
                                                        "Unknown",
                                                        PIKA_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));

  controller_signals[EVENT] =
    g_signal_new ("event",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (PikaControllerClass, event),
                  g_signal_accumulator_true_handled, NULL,
                  _pika_widgets_marshal_BOOLEAN__POINTER,
                  G_TYPE_BOOLEAN, 1,
                  G_TYPE_POINTER);
}

static void
pika_controller_init (PikaController *controller)
{
}

static void
pika_controller_finalize (GObject *object)
{
  PikaController *controller = PIKA_CONTROLLER (object);

  g_clear_pointer (&controller->name,  g_free);
  g_clear_pointer (&controller->state, g_free);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
pika_controller_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  PikaController *controller = PIKA_CONTROLLER (object);

  switch (property_id)
    {
    case PROP_NAME:
      if (controller->name)
        g_free (controller->name);
      controller->name = g_value_dup_string (value);
      break;
    case PROP_STATE:
      if (controller->state)
        g_free (controller->state);
      controller->state = g_value_dup_string (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
pika_controller_get_property (GObject    *object,
                              guint       property_id,
                              GValue     *value,
                              GParamSpec *pspec)
{
  PikaController *controller = PIKA_CONTROLLER (object);

  switch (property_id)
    {
    case PROP_NAME:
      g_value_set_string (value, controller->name);
      break;
    case PROP_STATE:
      g_value_set_string (value, controller->state);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

PikaController *
pika_controller_new (GType controller_type)
{
  PikaController *controller;

  g_return_val_if_fail (g_type_is_a (controller_type, PIKA_TYPE_CONTROLLER),
                        NULL);

  controller = g_object_new (controller_type, NULL);

  return controller;
}

gint
pika_controller_get_n_events (PikaController *controller)
{
  g_return_val_if_fail (PIKA_IS_CONTROLLER (controller), 0);

  if (PIKA_CONTROLLER_GET_CLASS (controller)->get_n_events)
    return PIKA_CONTROLLER_GET_CLASS (controller)->get_n_events (controller);

  return 0;
}

const gchar *
pika_controller_get_event_name (PikaController *controller,
                                gint            event_id)
{
  const gchar *name = NULL;

  g_return_val_if_fail (PIKA_IS_CONTROLLER (controller), NULL);

  if (PIKA_CONTROLLER_GET_CLASS (controller)->get_event_name)
    name = PIKA_CONTROLLER_GET_CLASS (controller)->get_event_name (controller,
                                                                   event_id);

  if (! name)
    name = "<invalid event id>";

  return name;
}

const gchar *
pika_controller_get_event_blurb (PikaController *controller,
                                 gint            event_id)
{
  const gchar *blurb = NULL;

  g_return_val_if_fail (PIKA_IS_CONTROLLER (controller), NULL);

  if (PIKA_CONTROLLER_GET_CLASS (controller)->get_event_blurb)
    blurb =  PIKA_CONTROLLER_GET_CLASS (controller)->get_event_blurb (controller,
                                                                      event_id);

  if (! blurb)
    blurb = "<invalid event id>";

  return blurb;
}

gboolean
pika_controller_event (PikaController            *controller,
                       const PikaControllerEvent *event)
{
  gboolean retval = FALSE;

  g_return_val_if_fail (PIKA_IS_CONTROLLER (controller), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  g_signal_emit (controller, controller_signals[EVENT], 0,
                 event, &retval);

  return retval;
}
