#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "swis.h"
#include "Global/RISCOS.h"


#include "debugrep.h"
#include "usbjoystick.h"
#include "usb.h"
#include "joydev.h"
#include "device.h"
#include "led.h"
#include "rumble.h"


static const uint8_t uhid_xb360gp_report_descr[] = {UHID_XB360GP_REPORT_DESCR()};


// A PlayStation 3 pad sends no input until its feature reports 0xF2 and 0xF5
// have been read. Sequence from SDL's PS3 driver (zlib).
static _kernel_oserror *ps3_init(const usb_device_t *dev, uint8_t interface)
{
  uint8_t buf[17];

  _kernel_oserror *e = usb_hid_get_report(dev, interface, UHID_FEATURE_REPORT, 0xF2, buf, 17);
  if (e) return e;

  e = usb_hid_get_report(dev, interface, UHID_FEATURE_REPORT, 0xF5, buf, 8);
  if (e) return e;

  // SDL ignores the result of this write, and some pads refuse it
  e = usb_hid_set_report_once(dev, interface, UHID_OUTPUT_REPORT, 0xF5, buf, 1);
  if (e)
    debug_printf("ps3_init: %s: the pad refused the final write: %s\n", dev, e->errmess);

  return NULL;
}


// The PlayStation USB adaptor ends each report with the pad's type byte
// (0x41 digital, 0x73 analogue). In analogue mode it reports buttons 10 and 11
// as held all the time, so clear them there. See doc.Rumble.
static void psx_fixup(uint8_t *body)
{
  if (body[6] == 0x73)
    body[1] &= ~0x0c;
}


// todo: can we get away with assuming protocol==0 for standard HID devices?  keyboard is 2
static const struct joy_device_struct joy_devices[] = {
  { 0xFF, 0x5D,   1, 0x45E, 0x028E, "XBOX360 wired controller", JOY_TYPE_XBOX360, (void*)&uhid_xb360gp_report_descr, sizeof(uhid_xb360gp_report_descr), 0, 0, { Joystick_Axis_Y, Joystick_Axis_RY }, 2, 0, JOY_QUIRK_NO_SERIAL, JOY_OUTPUT_ENDPOINT, rumble_xbox360 },
  { 0xFF, 0x5D, 129, 0x45E, 0x0719, "XBOX360 wireless controller", JOY_TYPE_XBOX360WL, (void*)&uhid_xb360gp_report_descr, sizeof(uhid_xb360gp_report_descr), 4, 0, { Joystick_Axis_Y, Joystick_Axis_RY }, 2, &xbox360wireless_data_validator, JOY_QUIRK_NO_SERIAL | JOY_QUIRK_LOSSY_OUTPUT, JOY_OUTPUT_ENDPOINT, rumble_xbox360wireless, led_xbox360wireless, led_xbox360wireless_packet },

  // Xbox 360 pads and receivers from other makers share the interface class,
  // subclass and protocol but not the vendor and product ids.
  { 0xFF, 0x5D,   1,     0,     0, "XBOX360 compatible wired controller", JOY_TYPE_XBOX360, (void*)&uhid_xb360gp_report_descr, sizeof(uhid_xb360gp_report_descr), 0, 0, { Joystick_Axis_Y, Joystick_Axis_RY }, 2, 0, JOY_QUIRK_NO_SERIAL, JOY_OUTPUT_ENDPOINT, rumble_xbox360 },
  { 0xFF, 0x5D, 129,     0,     0, "XBOX360 compatible wireless controller", JOY_TYPE_XBOX360WL, (void*)&uhid_xb360gp_report_descr, sizeof(uhid_xb360gp_report_descr), 4, 0, { Joystick_Axis_Y, Joystick_Axis_RY }, 2, &xbox360wireless_data_validator, JOY_QUIRK_NO_SERIAL | JOY_QUIRK_LOSSY_OUTPUT, JOY_OUTPUT_ENDPOINT, rumble_xbox360wireless, led_xbox360wireless, led_xbox360wireless_packet },

  { 0x03, 0x00,   0, 0xB43, 0x0003, "PlayStation USB adaptor", JOY_TYPE_STANDARD, 0, 0, 0, 0, { 0 }, 0, 0, 0, JOY_OUTPUT_CONTROL, rumble_psx, 0, 0, psx_fixup, 0xF000 },   // 0xF000: buttons 12-15 are the D-pad, mirrored
  { 0x03, 0x00,   0, 0x54C, 0x0268, "PlayStation 3 controller", JOY_TYPE_STANDARD, 0, 0, 0, ps3_init, { 0 }, 0, 0, 0, JOY_OUTPUT_CONTROL, rumble_ps3 },

  // PlayStation 4 pads (first and second generation) report as plain HID and
  // take rumble on the interface's interrupt OUT endpoint.
  { 0x03, 0x00,   0, 0x54C, 0x05C4, "PlayStation 4 controller", JOY_TYPE_STANDARD, 0, 0, 0, 0, { 0 }, 0, 0, 0, JOY_OUTPUT_ENDPOINT, rumble_ps4 },
  { 0x03, 0x00,   0, 0x54C, 0x09CC, "PlayStation 4 controller", JOY_TYPE_STANDARD, 0, 0, 0, 0, { 0 }, 0, 0, 0, JOY_OUTPUT_ENDPOINT, rumble_ps4 },

  { 0x03, 0x00,   0,     0,     0, "Standard HID controller (no boot)", JOY_TYPE_STANDARD, 0, 0, 0, 0, { 0 }, 0, 0, 0, JOY_OUTPUT_NONE, 0 },
  { 0x03, 0x01,   0,     0,     0, "Standard HID controller (boot)", JOY_TYPE_STANDARD, 0, 0, 0, 0, { 0 }, 0, 0, 0, JOY_OUTPUT_NONE, 0 }
};

uint32_t joy_devices_count = sizeof(joy_devices)/sizeof(joy_devices[0]);




int xbox360wireless_data_validator(char *d)
{
  return (int)(d[0] == 0 && d[1] == 1);
}



struct joy_device_struct *lookup_joy_device(uint8_t iClass, uint8_t iSubClass, uint8_t iProtocol, uint16_t vendor, uint16_t product)
{
  // look for a full match
  for (uint32_t i=0; i<joy_devices_count; i++) {
    if (joy_devices[i].interface_class == iClass &&
      joy_devices[i].interface_subclass == iSubClass &&
      joy_devices[i].interface_protocol == iProtocol &&
      joy_devices[i].vendor_id == vendor &&
      joy_devices[i].product_id == product)
      return (struct joy_device_struct*)&(joy_devices[i]);
  }

  // look for a match without vendor and product ids
  for (uint32_t i=0; i<joy_devices_count; i++) {
    if (joy_devices[i].interface_class == iClass &&
      joy_devices[i].interface_subclass == iSubClass &&
      joy_devices[i].interface_protocol == iProtocol &&
      joy_devices[i].vendor_id == 0 &&
      joy_devices[i].product_id == 0)
      return (struct joy_device_struct*)&(joy_devices[i]);
  }

  return 0;
}

