/*
 * Copyright 2011, 2013 Canonical Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of either or both of the following licenses:
 *
 * 1) the GNU Lesser General Public License version 3, as published by the
 * Free Software Foundation; and/or
 * 2) the GNU Lesser General Public License version 2.1, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the applicable version of the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of both the GNU Lesser General Public
 * License version 3 and version 2.1 along with this program.  If not, see
 * <http://www.gnu.org/licenses/>
 */
#include "gripinputdevice.h"

#include <geis/geis.h>
#include <glib.h>


G_DEFINE_TYPE (GripInputDevice,
               grip_input_device,
               G_TYPE_OBJECT);

struct GripInputDevicePrivate
{
  GeisDevice         geis_device;
  gchar             *name;
  GeisInputDeviceId  device_id;
  gint               touches;
  gboolean           is_direct;
  gboolean           is_independent;
  GripAxisExtents    x_extents;
  GripAxisExtents    y_extents;
};


enum {
  PROP_0,
  PROP_GEIS_DEVICE,
  PROP_GEIS_DEVICE_ATTRS
};


/*
 * grip_input_device_constructor:
 * @type:     The internal type of the #GripInputDevice class.
 * @n_params: The number of constructor parameters passed.
 * @params:   A collection of constructor parameters.
 *
 * Constructs a new input device.
 */
static GObject *
grip_input_device_constructor (GType                  type,
                               guint                  n_params,
                               GObjectConstructParam *params)
{
  GObjectClass *parent_class = G_OBJECT_CLASS (grip_input_device_parent_class);
  GObject *object = parent_class->constructor (type, n_params, params);
  GripInputDevice *input_device = GRIP_INPUT_DEVICE (object);
  guint i;

  if (input_device->priv->geis_device == NULL)
  {
    for (i = 0; i < n_params; ++i)
    {
      if (0 == g_strcmp0(params[i].pspec->name, "device-attrs"))
      {
	GeisGestureAttr *attrs = (GeisGestureAttr *)g_value_get_pointer (params[i].value);
	GeisGestureAttr *a;

	for (a = attrs; a->name; a++)
	{
	  if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_NAME))
	    input_device->priv->name = g_strdup (a->string_val);
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_ID))
	    input_device->priv->device_id = a->integer_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_TOUCHES))
	    input_device->priv->touches = a->integer_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_DIRECT_TOUCH))
	    input_device->priv->is_direct = a->boolean_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_INDEPENDENT_TOUCH))
	    input_device->priv->is_independent = a->boolean_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_MIN_X))
	    input_device->priv->x_extents.minimum = a->float_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_MAX_X))
	    input_device->priv->x_extents.maximum = a->float_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_RES_X))
	    input_device->priv->x_extents.resolution = a->float_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_MIN_Y))
	    input_device->priv->y_extents.minimum = a->float_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_MAX_Y))
	    input_device->priv->y_extents.maximum = a->float_val;
	  else if (0 == g_strcmp0 (a->name, GEIS_DEVICE_ATTRIBUTE_RES_Y))
	    input_device->priv->y_extents.resolution = a->float_val;
	}
      }
    }
  }
  else
  {
    guint num_attrs = geis_device_attr_count(input_device->priv->geis_device);
    for (i = 0; i < num_attrs; ++i)
    {
       GeisAttr attr = geis_device_attr(input_device->priv->geis_device, i);
        if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_NAME))
          input_device->priv->name = g_strdup (geis_attr_value_to_string(attr));
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_ID))
          input_device->priv->device_id = geis_attr_value_to_integer(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_TOUCHES))
          input_device->priv->touches = geis_attr_value_to_integer(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_DIRECT_TOUCH))
          input_device->priv->is_direct = geis_attr_value_to_boolean(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_INDEPENDENT_TOUCH))
          input_device->priv->is_independent = geis_attr_value_to_boolean(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_MIN_X))
          input_device->priv->x_extents.minimum = geis_attr_value_to_float(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_MAX_X))
          input_device->priv->x_extents.maximum = geis_attr_value_to_float(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_RES_X))
          input_device->priv->x_extents.resolution = geis_attr_value_to_float(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_MIN_Y))
          input_device->priv->y_extents.minimum = geis_attr_value_to_float(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_MAX_Y))
          input_device->priv->y_extents.maximum = geis_attr_value_to_float(attr);
        else if (0 == g_strcmp0 (geis_attr_name(attr), GEIS_DEVICE_ATTRIBUTE_RES_Y))
          input_device->priv->y_extents.resolution = geis_attr_value_to_float(attr);
    }
  }

  return object;
}


/*
 * grip_input_device_finalize:
 * @object:      A #GripInputDevice object
 *
 * Frees all allocated resources of a #GripInputDevice before turing in for the
 * night.
 */
static void
grip_input_device_finalize (GObject *object)
{
  GObjectClass *parent_class = G_OBJECT_CLASS (grip_input_device_parent_class);
  GripInputDevice *input_device = GRIP_INPUT_DEVICE (object);

  g_free (input_device->priv->name);
  parent_class->finalize (object);
}


/*
 * grip_input_device_set_property:
 * @object:      A #GripInputDevice object
 * @property_id: Identifies the property.
 * @value:       The value of the property.
 * @pspec:       The specification of the property.
 *
 * Sets properties on a #GripInputDevice.
 */
static void
grip_input_device_set_property (GObject      *object,
                                guint         property_id,
                                const GValue *value,
                                GParamSpec   *pspec)
{
  GripInputDevice *input_device = GRIP_INPUT_DEVICE (object);

  switch (property_id)
  {
    case PROP_GEIS_DEVICE:
      input_device->priv->geis_device = g_value_get_pointer (value);
      break;
    case PROP_GEIS_DEVICE_ATTRS:
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}


/**
 * grip_input_device_get_id:
 * @input_device: (in): A #GripInputDevice
 *
 * Gets the internal identifier of the input device.
 *
 * Returns: the internal identifier of the input device
 */
guint
grip_input_device_get_id(GripInputDevice *input_device)
{
  return input_device->priv->device_id;
}


/**
 * grip_input_device_get_name:
 * @input_device: (in): A #GripInputDevice
 *
 * Gets the name of the input device, as retrieved from the device itself.
 *
 * Returns: the seld-described name of the input device.
 */
const gchar *
grip_input_device_get_name (GripInputDevice *input_device)
{
  return input_device->priv->name;
}


/**
 * grip_input_device_is_direct:
 * @input_device: (in): A #GripInputDevice
 *
 * Indicates if the input device is a direct input device (eg. touchscreen) or
 * not (eg. touchpad).
 *
 * Returns: %TRUE if the input device is an direct device, %FALSE
 * otherwsie.
 */
gboolean
grip_input_device_is_direct(GripInputDevice *input_device)
{
  return input_device->priv->is_direct;
}


/**
 * grip_input_device_is_independent:
 * @input_device: (in): A #GripInputDevice
 *
 * Indicates if the input device is an independent input device (for example, an
 * Apple MagicMouse).
 *
 * Returns: %TRUE if the input device is an independent device, %FALSE
 * otherwsie.
 */
gboolean
grip_input_device_is_independent(GripInputDevice *input_device)
{
  return input_device->priv->is_independent;
}


/**
 * grip_input_device_get_x_extents:
 * @input_device: (in): A #GripInputDevice
 *
 * Returns: the X-axis extents of the device.
 */
const GripAxisExtents *
grip_input_device_get_x_extents(GripInputDevice *input_device)
{
  return &input_device->priv->x_extents;
}


/**
 * grip_input_device_get_y_extents:
 * @input_device: (in): A #GripInputDevice
 *
 * Returns: the Y-axis extents of the device.
 */
const GripAxisExtents *
grip_input_device_get_y_extents(GripInputDevice *input_device)
{
  return &input_device->priv->y_extents;
}


/*
 * grip_input_device_init:
 * @self: A #GripInputDevice
 *
 * Initializes a #GripInputDevice object before construction.
 */
static void
grip_input_device_init (GripInputDevice *self)
{
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
                                            GRIP_TYPE_INPUT_DEVICE,
                                            GripInputDevicePrivate);
}


/*
 * grip_input_device_class_init:
 * @klass: the class object.
 *
 * Initializes the #GripInputDeviceClass.
 */
static void
grip_input_device_class_init (GripInputDeviceClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  g_type_class_add_private (object_class, sizeof (GripInputDevicePrivate));
  object_class->constructor  = grip_input_device_constructor;
  object_class->set_property = grip_input_device_set_property;
  object_class->finalize     = grip_input_device_finalize;

  /**
   * GripInputDevice:device-attrs:
   *
   * A list of GEIS v1 device attrs (passed into the device callback) used to
   * construct a #GripInputDevice.
   */
  g_object_class_install_property (object_class, PROP_GEIS_DEVICE_ATTRS,
      g_param_spec_pointer ("device-attrs", "GEIS1 device attributes",
                            "a list of GEIS v1 device attributes",
          G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));

  /**
   * GripInputDevice:geis-device:
   *
   * A GEIS v2 device object used to construct a #GripInputDevice.
   */
  g_object_class_install_property (object_class, PROP_GEIS_DEVICE,
      g_param_spec_pointer ("geis-device", "GEIS device", "a GEIS device",
          G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
}

GripDeviceType
grip_get_device_type (GripInputDevice *input_device) {
  gboolean is_direct = grip_input_device_is_direct (input_device);
  gboolean is_independent = grip_input_device_is_independent (input_device);
  if (is_direct && !is_independent)
    return GRIP_DEVICE_TOUCHSCREEN;
  if (!is_direct && !is_independent)
    return GRIP_DEVICE_TOUCHPAD;
  if (!is_direct && is_independent)
    return GRIP_DEVICE_INDEPENDENT;

  g_critical("Unknown touch device type.");
  return GRIP_DEVICE_TOUCHSCREEN;
}
