
#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 "USBJoystickHdr.h"   // led_callback veneer
#include "device.h"
#include "joyswis.h"          // Joystick_Cap_*
#include "rumble.h"

#include "led.h"


// A pad can drop a command, so each connect sends the LED a few times.
#define LED_SENDS        3
#define LED_FIRST_DELAY  30   // cs from the connect to the first send
#define LED_SPACING      50   // cs between sends

// Packet type 0x08 is the receiver reporting a pad's presence; bit 7 of the
// next byte is set when the pad has connected.
#define PRESENCE_PACKET    0x08
#define PRESENCE_CONNECTED 0x80

// LED command, 12 bytes: 00 00 08 <0x40 + pattern> then zeros. Patterns
// 06..09 light LED 1..4 steady.
#define LED_PATTERN_BASE   0x06
#define LED_COMMAND_BASE   0x40


static int callback_pending = FALSE;


static uint32_t now_cs(void)
{
  uint32_t t = 0;
  _swix(OS_ReadMonotonicTime, _OUT(0), &t);
  return t;
}


static uint32_t player(uint32_t slot)
{
  return (slot & 3) + 1;
}


uint32_t led_capabilities(uint32_t slot)
{
  struct joydata_struct *j = &joy_data[slot];

  if (!j->in_use || j->device == NULL || j->device->led == NULL)
    return 0;

  if (j->device->output_transport == JOY_OUTPUT_ENDPOINT && j->output.endpoint == 0)
    return 0;

  return Joystick_Cap_LED;
}


_kernel_oserror *led_xbox360wireless(uint32_t slot)
{
  uint8_t pkt[12] = { 0x00, 0x00, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

  pkt[3] = (uint8_t)(LED_COMMAND_BASE + LED_PATTERN_BASE + (player(slot) - 1));
  return rumble_send(slot, pkt, sizeof(pkt));
}


void led_xbox360wireless_packet(uint32_t slot, const uint8_t *data, uint32_t len, int input)
{
  struct joy_output_struct *o = &joy_data[slot].output;

  // a pad that connected before the module started is seen by its first input
  int connect = !input && len >= 2 && data[0] == PRESENCE_PACKET && (data[1] & PRESENCE_CONNECTED);
  if (connect || (input && !o->led_armed)) {
    o->led_armed = TRUE;
    o->led_sends_left = LED_SENDS;
    o->led_next = now_cs() + LED_FIRST_DELAY;
  }

  if (o->led_sends_left == 0)
    return;

  uint32_t now = now_cs();
  if ((int32_t)(now - o->led_next) < 0)
    return;

  o->led_sends_left--;
  o->led_next = now + LED_SPACING;
  o->led_due = TRUE;

  if (!callback_pending) {
    callback_pending = TRUE;
    if (_swix(OS_AddCallBack, _INR(0,1), led_callback, g_module_pw))
      callback_pending = FALSE;
  }
}


void led_shutdown(void)
{
  _swix(OS_RemoveCallBack, _INR(0,1), led_callback, g_module_pw);
  callback_pending = FALSE;
}


_kernel_oserror *led_callback_handler(_kernel_swi_regs *r, void *pw)
{
  IGNORE(r);
  IGNORE(pw);

  callback_pending = FALSE;

  for (uint32_t i=0; i<Joystick_MaxSlots; i++) {
    struct joydata_struct *j = &joy_data[i];

    if (!j->output.led_due)
      continue;
    j->output.led_due = FALSE;

    if (!(led_capabilities(i) & Joystick_Cap_LED))
      continue;

    debug_printf("led: slot %u: setting player %u LED, send %u of %u\n", i, player(i), LED_SENDS - j->output.led_sends_left, LED_SENDS);
    _kernel_oserror *e = j->device->led(i);
    if (e)
      debug_printf("led: slot %u: send failed: %s\n", i, e->errmess);
  }

  return NULL;
}
