#include "ui/base/touch/multi_touch_device_x11.h"

#include "ui/base/touch/multi_touch_device.h"

#include "ui/base/x/x11_util.h"

#include <xorg/xserver-properties.h>

#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XIproto.h>

namespace {

const char * AxisNameForType(ui::Axis::Type type) {

  // Defines are taken from xorg/xserver-properties.h
  static const char* names[ui::Axis::AXIS_TYPE_LAST+1] = {
    AXIS_LABEL_PROP_ABS_MT_POSITION_X,
    AXIS_LABEL_PROP_ABS_MT_POSITION_Y,
    AXIS_LABEL_PROP_ABS_MT_TOUCH_MAJOR,
    AXIS_LABEL_PROP_ABS_MT_TOUCH_MINOR,
    AXIS_LABEL_PROP_ABS_MT_WIDTH_MAJOR,
    AXIS_LABEL_PROP_ABS_MT_WIDTH_MINOR,
    AXIS_LABEL_PROP_ABS_MT_ORIENTATION,
    AXIS_LABEL_PROP_ABS_MT_TOOL_TYPE,
    AXIS_LABEL_PROP_ABS_MT_BLOB_ID,
    AXIS_LABEL_PROP_ABS_MT_TRACKING_ID,
    AXIS_LABEL_PROP_ABS_MT_PRESSURE,
    AXIS_LABEL_PROP_ABS_MISC,
    "Unknown axis type",
    "ui::Axis::AXIS_TYPE_LAST"
  };

  return names[type];
}

ui::Axis::Type AxisTypeForLabel(Display* display, Atom label) {
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_TOUCH_MAJOR,
                           True))
    return ui::Axis::AXIS_TYPE_TOUCH_MAJOR;
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_TOUCH_MINOR,
                           True))
    return ui::Axis::AXIS_TYPE_TOUCH_MINOR;
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_WIDTH_MAJOR,
                           True))
    return ui::Axis::AXIS_TYPE_WIDTH_MAJOR;
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_WIDTH_MINOR,
                           True))
    return ui::Axis::AXIS_TYPE_WIDTH_MINOR;
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_ORIENTATION,
                           True))
    return ui::Axis::AXIS_TYPE_ORIENTATION;
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_TOOL_TYPE,
                           True))
    return ui::Axis::AXIS_TYPE_TOOL;
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_BLOB_ID,
                           True))
    return ui::Axis::AXIS_TYPE_BLOB_ID;
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_TRACKING_ID,
                           True))
    return ui::Axis::AXIS_TYPE_TRACKING_ID;
  if (label == XInternAtom(display, AXIS_LABEL_PROP_ABS_MT_PRESSURE,
                           True))
    return ui::Axis::AXIS_TYPE_PRESSURE;

  return ui::Axis::AXIS_TYPE_UNKNOWN;
}

} // namespace

namespace ui {
bool xi_device_info_to_mt_device(XIDeviceInfo * info,
                                 MultiTouchDevice & out) {

  if(info == NULL)
    return false;
  
  out.set_id(info->deviceid);
  out.set_name(info->name);
  MultiTouchDevice::Axes axes;

  for (int i = 0; i < info->num_classes; ++i) {
    switch (info->classes[i]->type) {
      case XITouchClass: {
        XITouchClassInfo* touch_info =
          reinterpret_cast<XITouchClassInfo*>(info->classes[i]);

        switch(touch_info->mode) {
          case XIDirectTouch:
            out.set_type(MultiTouchDevice::DIRECT_TOUCH_DEVICE_TYPE);
            break;
          case XIDependentTouch:
            out.set_type(MultiTouchDevice::DEPENDENT_TOUCH_DEVICE_TYPE);
            break;
        }

        out.set_max_num_touches(touch_info->num_touches);
        break;
      }

      case XIValuatorClass: {
        XIValuatorClassInfo* valuator_info =
          reinterpret_cast<XIValuatorClassInfo*>(info->classes[i]);

        Axis::Type type;

        /* X and Y axes are always 0 and 1 in X11. Due to historical reasons,
         * the labels of these axes may not be consistent across input modules.
         * Hard code them here instead. */
        if (valuator_info->number == 0)
          type = Axis::AXIS_TYPE_X;
        else if (valuator_info->number == 1)
          type = Axis::AXIS_TYPE_Y;
        else {
          type = AxisTypeForLabel(ui::GetXDisplay(), valuator_info->label);
        }

        if(type==Axis::AXIS_TYPE_UNKNOWN)
          continue;

        Axis axis;
        axis.set_name(AxisNameForType(type));
        axis.set_type(type);
        axis.set_min(valuator_info->min);
        axis.set_max(valuator_info->max);
        axis.set_resolution(valuator_info->resolution);

        axes[type] = axis;

        break;
      }

      default:
        break;
    }
  }

  out.set_axes(axes);

  out.set_window_resolution_x(0.f);
  out.set_window_resolution_y(0.f);

  return true;
}

}
