
// C library

#include "swis.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "Global/NewErrors.h"
#include "Global/RISCOS.h"


#include "joyhelp.h"

#include "debugrep.h"
#include "USBJoystickHdr.h"
#include "usbjoystick.h"
#include "usbhid.h"        // HID_PAGE/HID_USAGE
#include "adc.h"
#include "mouse.h"
#include "device.h"
#include "errors.h"

#include "joyswis.h"


_kernel_oserror* swi_joystick_read(_kernel_swi_regs *r)
{
  char stick = r->r[0] & 0xFF;            // bits 0-7 are the stick
  char reason = (r->r[0] & 0xFF00) >> 8;  // bits 8-15 are the reason code
  // Paradise: 'bit 7' was used as a 'read stick 2' flag (we are not implementing)

  // both reasons take a legacy number, so we need to find the mapped slot
  for (uint32_t i=0; i<JOY_MAX; i++) {
    if (joy_data[i].mapped_number == stick) {
      // found the stick, so return data in Acorn format
      switch (reason)
      {
        case ACORN_SWI_READ_REASON_8:
          return joystick_read_acorn_8(r, i);
        case ACORN_SWI_READ_REASON_16:
          return joystick_read_acorn_16(r, i);
      }
    }
  }

  //debug_printf("swi_joystick_read: stick %d reason %d, no map, returning default response\n", stick, reason);

  switch (reason)
  {
    case ACORN_SWI_READ_REASON_8:
    {
      // 8-bit: buttons and axes on r0 (all zero)
      r->r[0] = ACORN_REST_8;
      break;
    }
    case ACORN_SWI_READ_REASON_16:
    {
      // 16-bit: buttons on r1, axes both 32768
      r->r[0] = ACORN_AXES_REST_16;
      r->r[1] = ACORN_BUTTONS_REST_16;
      break;
    }
  }

  //debug_printf("swi_joystick_read: default (reason %d):  r0 %u (0x%x)   r1 %u (0x%x)\n", reason, r->r[0], r->r[0], r->r[1], r->r[1]);

  return NULL;
}




_kernel_oserror* joystick_read_acorn_8(_kernel_swi_regs *r, uint32_t i)
{
  // 8-bit Acorn API
  //   -127 is down/left and +127 is up/right (centered is 0)
  //   Vertical Twist recommend digital inputs (HAT) to be switched at +64 and -64

  // X and Y are mapped independently: an unmapped axis simply reads its
  // rest value (0, centred) rather than the whole stick bailing out. This
  // lets a one-axis device (a paddle/throttle mapped to Y only) work, and
  // keeps the buttons (mapped separately, below) reporting regardless. The
  // all-rest, no-buttons result only arises when nothing at all is mapped.
  int32_t x = ACORN_REST_8;
  int32_t y = ACORN_REST_8;

  if (joy_data[i].mapped_x_8 != NOT_MAPPED) {
    struct axisdata_struct *ax = &(joy_data[i].axes[joy_data[i].mapped_x_8]);

    if (ax->is_digital)
      // HAT value of -1, 0 or +1 easily translates to -64, 0 or +64
      x = ax->val * ACORN_AXIS_HAT_SCALE_8;
    else
      x = scaled_value_8(ax);

    if (ax->flip)
      x = x * -1;
  }

  // For Y-axis, we *-1 so the lower value equates to down/bottom
  if (joy_data[i].mapped_y_8 != NOT_MAPPED) {
    struct axisdata_struct *ay = &(joy_data[i].axes[joy_data[i].mapped_y_8]);

    if (ay->is_digital)
      // HAT value of -1, 0 or +1 easily translates to -64, 0 or +64
      y = ay->val * ACORN_AXIS_HAT_SCALE_8;
    else
      y = scaled_value_8(ay);

    if (!(ay->flip))
      y = y * -1;
  }

  uint32_t acorn = (y & 0xff) | ((x & 0xff) << 8);

  for (int32_t b=0; b<ACORN_BUTTONS; b++) {
    if (joy_data[i].mapped_buttons[b] != NOT_MAPPED) {
        uint32_t bd = joy_data[i].buttons[joy_data[i].mapped_buttons[b]];
        acorn = acorn | (bd << (16+b));
      }
  }

  //if (acorn != 0)
    //debug_printf("joystick_read_acorn_8: i %d   rawx %d x %d   rawy %d y %d --> result is %u\n", i, ax->val, x, ay->val, y, acorn);

  r->r[0] = acorn;

  return NULL;
}





_kernel_oserror* joystick_read_acorn_16(_kernel_swi_regs *r, uint32_t i)
{
  // 16-bit Acorn API
  //   0 is down/left and 65535 is up/right (centered is 32768)
  //   Acorn advise using switch points of 12288 (down/left) and 53247 (up/right) for HAT

  // X and Y are mapped independently: an unmapped axis reads its rest value
  // (the 32768 centre) rather than the whole stick bailing out - see the
  // 8-bit read above for the rationale (one-axis devices, buttons still
  // report). Buttons are mapped separately, below.
  int32_t x = ACORN_AXIS_MID_VALUE_16;
  int32_t y = ACORN_AXIS_MID_VALUE_16;

  if (joy_data[i].mapped_x_16 != NOT_MAPPED) {
    struct axisdata_struct *ax = &(joy_data[i].axes[joy_data[i].mapped_x_16]);

    if (ax->is_digital) {
      if (ax->val == ax->min)
        x = ACORN_AXIS_HAT_MIN_16;
      else if (ax->val == ax->max)
        x = ACORN_AXIS_HAT_MAX_16;
      else
        x = ACORN_AXIS_HAT_MID_16;
    }
    else {
      x = scaled_value_16(ax);
    }

    if (ax->flip)
      x = ACORN_AXIS_MAX_VALUE_16 - x;
  }

  // For Y-axis, we invert so the lower value equates to down/bottom
  if (joy_data[i].mapped_y_16 != NOT_MAPPED) {
    struct axisdata_struct *ay = &(joy_data[i].axes[joy_data[i].mapped_y_16]);

    if (ay->is_digital) {
      if (ay->val == ay->min)
        y = ACORN_AXIS_HAT_MIN_16;
      else if (ay->val == ay->max)
        y = ACORN_AXIS_HAT_MAX_16;
      else
        y = ACORN_AXIS_HAT_MID_16;
    }
    else {
      y = scaled_value_16(ay);
    }

    if (!(ay->flip))
      y = ACORN_AXIS_MAX_VALUE_16 - y;
  }

  uint32_t position = (y & 0xffff) | ((x & 0xffff) << 16);
  uint32_t buttons = 0;

  for (int32_t b=0; b<ACORN_BUTTONS; b++) {
    if (joy_data[i].mapped_buttons[b] != NOT_MAPPED) {
        uint32_t bd = joy_data[i].buttons[joy_data[i].mapped_buttons[b]];
        buttons = buttons | (bd << b);
      }
  }

  //if (position != 0 || buttons != 0)
    //debug_printf("joystick_read_acorn_16: i %u   rawx %d, x %d, xdig %d   rawy %d, y %d, ydig %d   -->   pos %u, buttons %u\n", i, ax->val, x, ax->is_digital, ay->val, y, ay->is_digital, position, buttons);

  r->r[0] = position;
  r->r[1] = buttons;
  // Paradise: make r2 return 'position' for the second stick (not implemented here)

  return NULL;
}




int32_t scaled_value_8(struct axisdata_struct *a)
{
  int32_t value = 0;

  if (a->acorn_slope_div_8)
    value = ACORN_AXIS_MIN_VALUE_8 + ((a->val - a->min) / a->acorn_slope_8);
  else
    value = ACORN_AXIS_MIN_VALUE_8 + (a->acorn_slope_8 * (a->val - a->min));

  if (value < ACORN_AXIS_MIN_VALUE_8)
    return ACORN_AXIS_MIN_VALUE_8;
  else if (value > ACORN_AXIS_MAX_VALUE_8)
    return ACORN_AXIS_MAX_VALUE_8;
  else
    return value;
}




int32_t scaled_value_16(struct axisdata_struct *a)
{
  int32_t value = 0;

  if (a->acorn_slope_div_16)
    value = ACORN_AXIS_MIN_VALUE_16 + ((a->val - a->min) / a->acorn_slope_16);
  else
    value = ACORN_AXIS_MIN_VALUE_16 + (a->acorn_slope_16 * (a->val - a->min));

  if (value < ACORN_AXIS_MIN_VALUE_16)
    return ACORN_AXIS_MIN_VALUE_16;
  else if (value > ACORN_AXIS_MAX_VALUE_16)
    return ACORN_AXIS_MAX_VALUE_16;
  else
    return value;
}




int32_t scaled_value_pc(struct axisdata_struct *a)
{
  int32_t value = 0;

  if (a->pc_slope_div)
    value = (a->val - a->min) / a->pc_slope;
  else
    value = a->pc_slope * (a->val - a->min);

  if (value < 0)
    return 0;
  else if (value > 100)
    return 100;
  else
    return value;
}




/*

The Serial Port interface

*/

_kernel_oserror* swi_joystick_status(_kernel_swi_regs *r)
{
  uint32_t stick1 = 0;
  uint32_t stick2 = 0;

  for (uint32_t i=0; i<JOY_MAX; i++) {
    if (joy_data[i].mapped_number == 0) {
      stick1 = encode_stick_serial_port(i);
    }
    else if (joy_data[i].mapped_number == 1) {
      stick2 = encode_stick_serial_port(i);
    }
  }

  uint32_t sticks = (stick1 & 0xFF) | ((stick2 & 0xFF) << 8);
  debug_printf("swi_joystick_status: stick1 %u, stick2 %u, both sticks %u\n", stick1, stick2, sticks);
  r->r[0] = sticks;

  return NULL;
}




uint32_t encode_stick_serial_port(int32_t i)
{
  // X and Y independent (see the Acorn reads above): an unmapped axis
  // contributes no direction (reads centred), and the buttons below report
  // regardless. Result is only 0 when nothing at all is mapped.
  int32_t left = 0, right = 0, up = 0, down = 0;

  if (joy_data[i].mapped_x_8 != NOT_MAPPED) {
    struct axisdata_struct *ax = &(joy_data[i].axes[joy_data[i].mapped_x_8]);
    left = (ax->val < ax->serial_port_lower_bound);
    right = (ax->val > ax->serial_port_upper_bound);

    if (ax->flip) {
      int32_t temp = left;
      left = right;
      right = temp;
    }
  }

  if (joy_data[i].mapped_y_8 != NOT_MAPPED) {
    struct axisdata_struct *ay = &(joy_data[i].axes[joy_data[i].mapped_y_8]);
    up = (ay->val < ay->serial_port_lower_bound);
    down = (ay->val > ay->serial_port_upper_bound);

    if (ay->flip) {
      int32_t temp = up;
      up = down;
      down = temp;
    }
  }

  // note: checks for any button
  int32_t buttons = 0;
  for (int32_t b=0; b<ACORN_BUTTONS; b++) {
    if (joy_data[i].mapped_buttons[b] != NOT_MAPPED) {
        uint32_t bd = joy_data[i].buttons[joy_data[i].mapped_buttons[b]];
        buttons = buttons | (bd << b);
      }
  }

  uint32_t anybutton = (buttons > 0);
  uint32_t sp = (right << 0) | (left << 1) | (down << 2) | (up << 3) | (anybutton << 4);
  //debug_printf("encode_stick_serial_port: id %d, left %d, right %d, up %d, down %d, buttons %d, anybutton %d --> result %u\n", i, left, right, up, down, buttons, anybutton, sp);

  return sp;
}




/*

Native slot-based API - see doc.APIs section 5. Module-owned result storage
for the introspection SWIs below: the pointer a SWI hands back is only valid
until the next call that reuses it, which is fine since every real caller
consumes it synchronously.

*/

static struct joystick_device_info joyapi_device_info;
static struct joystick_axis_info   joyapi_axis_info[JOY_AXES];
static int32_t                     joyapi_axis_values[JOY_AXES];
static struct joystick_mapping     joyapi_mapping;


_kernel_oserror* swi_joystick_enumerate(_kernel_swi_regs *r)
{
  uint32_t mask = 0;

  for (uint32_t slot=0; slot<JOY_MAX; slot++) {
    if (joy_data[slot].in_use)
      mask |= (1u << slot);
  }

  r->r[0] = mask;
  r->r[1] = joy_generation;

  return NULL;
}




uint32_t joyapi_capabilities(uint32_t slot)
{
  // Nothing populates JOYCAP_RUMBLE yet - that's for the future (XBox360 pad
  // first). Kept as a separate function now so that phase lands as a
  // one-line change here, not a new response shape.
  IGNORE(slot);
  return 0;
}




void joyapi_build_guid(uint32_t slot, uint8_t guid[16])
{
  // SDL-inspired, not byte-identical: {bus_type, vendor_id, product_id,
  // device_id/version}, little-endian, zero-padded - enough that the same
  // physical model always produces the same GUID and different models
  // differ, without needing a CRC16 implementation just to look more
  // like SDL's own USB-bus encoding.
  memset(guid, 0, 16);

  struct joydata_struct *j = &joy_data[slot];

  guid[0] = 0x03;  // arbitrary "bus type" byte - USB, matching SDL's own
  guid[1] = 0x00;  // constant for this bus type, for what it's worth

  guid[4]  = (uint8_t) (j->vendor_id & 0xff);
  guid[5]  = (uint8_t) ((j->vendor_id >> 8) & 0xff);

  guid[8]  = (uint8_t) (j->product_id & 0xff);
  guid[9]  = (uint8_t) ((j->product_id >> 8) & 0xff);

  guid[12] = (uint8_t) (j->device_id & 0xff);
  guid[13] = (uint8_t) ((j->device_id >> 8) & 0xff);
}




_kernel_oserror* swi_joystick_device_info(_kernel_swi_regs *r)
{
  uint32_t slot = r->r[0];

  _kernel_oserror *err = validate_joystick_slot("Joystick_DeviceInfo", slot);
  if (err) return err;

  err = validate_joystick_slot_active("Joystick_DeviceInfo", slot);
  if (err) return err;

  struct joydata_struct *j = &joy_data[slot];

  joyapi_device_info.size = sizeof(struct joystick_device_info);
  joyapi_device_info.manufacturer = j->manufacturer;
  joyapi_device_info.product = j->product;
  joyapi_device_info.serial = j->serial;
  joyapi_device_info.vendor_id = j->vendor_id;
  joyapi_device_info.product_id = j->product_id;
  joyapi_device_info.num_axes = j->num_axes;
  joyapi_device_info.num_buttons = j->num_buttons;
  joyapi_device_info.num_hats = (j->has_hat ? 1 : 0) + (j->has_dpad ? 1 : 0);
  joyapi_device_info.capabilities = joyapi_capabilities(slot);
  joyapi_build_guid(slot, joyapi_device_info.guid);

  r->r[0] = 0;
  r->r[1] = sizeof(struct joystick_device_info);
  r->r[2] = (int) &joyapi_device_info;

  return NULL;
}




_kernel_oserror* swi_joystick_axis_info(_kernel_swi_regs *r)
{
  uint32_t slot = r->r[0];

  _kernel_oserror *err = validate_joystick_slot("Joystick_AxisInfo", slot);
  if (err) return err;

  err = validate_joystick_slot_active("Joystick_AxisInfo", slot);
  if (err) return err;

  struct joydata_struct *j = &joy_data[slot];

  for (uint32_t a=0; a<j->num_axes; a++) {
    struct axisdata_struct *ax = &j->axes[a];
    struct hid_item *item = get_axis_item_by_type(slot, ax->type);

    joyapi_axis_info[a].hid_usage_page = item ? HID_PAGE(item->usage) : 0;
    joyapi_axis_info[a].hid_usage      = item ? HID_USAGE(item->usage) : 0;
    joyapi_axis_info[a].type = ax->type;
    joyapi_axis_info[a].name = ax->name;
    joyapi_axis_info[a].min = ax->min;
    joyapi_axis_info[a].mid = ax->mid;
    joyapi_axis_info[a].max = ax->max;
    joyapi_axis_info[a].is_digital = ax->is_digital;
  }

  r->r[0] = 0;
  r->r[1] = sizeof(struct joystick_axis_info);
  r->r[2] = j->num_axes;
  r->r[3] = (int) joyapi_axis_info;

  return NULL;
}




_kernel_oserror* swi_joystick_axis_values(_kernel_swi_regs *r)
{
  uint32_t slot = r->r[0];

  _kernel_oserror *err = validate_joystick_slot("Joystick_AxisValues", slot);
  if (err) return err;

  err = validate_joystick_slot_active("Joystick_AxisValues", slot);
  if (err) return err;

  struct joydata_struct *j = &joy_data[slot];

  for (uint32_t a=0; a<j->num_axes; a++)
    joyapi_axis_values[a] = j->axes[a].val;

  uint32_t buttons = 0;
  for (uint32_t b=0; b<j->num_buttons; b++)
    buttons |= (j->buttons[b] << b);

  r->r[0] = 0;
  r->r[1] = sizeof(int32_t);
  r->r[2] = j->num_axes;
  r->r[3] = (int) joyapi_axis_values;
  r->r[4] = buttons;

  return NULL;
}




_kernel_oserror* swi_joystick_get_mapping(_kernel_swi_regs *r)
{
  uint32_t slot = r->r[0];

  _kernel_oserror *err = validate_joystick_slot("Joystick_GetMapping", slot);
  if (err) return err;

  err = validate_joystick_slot_active("Joystick_GetMapping", slot);
  if (err) return err;

  struct joydata_struct *j = &joy_data[slot];
  struct joystick_mapping *m = &joyapi_mapping;

  m->legacy_stick_number = j->mapped_number;
  m->axes8_x = j->mapped_x_8;
  m->axes8_y = j->mapped_y_8;
  m->axes16_x = j->mapped_x_16;
  m->axes16_y = j->mapped_y_16;

  m->num_buttons_mapped = 0;
  for (uint32_t b=0; b<JOY_BUTTONS; b++) {
    m->buttons[b] = j->mapped_buttons[b];
    if (m->buttons[b] != NOT_MAPPED)
      m->num_buttons_mapped = b + 1;
  }

  for (uint32_t c=0; c<ADC_CHANNELS; c++) {
    if (adc_map.channels[c].joy_id == (int32_t) slot) {
      m->adc_channel[c] = adc_map.channels[c].axis;
      m->adc_channel_start[c] = adc_map.channels[c].start;
      m->adc_channel_end[c] = adc_map.channels[c].end;
    }
    else {
      m->adc_channel[c] = NOT_MAPPED;
      m->adc_channel_start[c] = 0;
      m->adc_channel_end[c] = 0;
    }
  }

  for (uint32_t b=0; b<ADC_BUTTONS; b++) {
    m->adc_fire[b] = (adc_map.buttons[b].joy_id == (int32_t) slot)
                        ? adc_map.buttons[b].button
                        : NOT_MAPPED;
  }

  m->mouse_x = j->mapped_mouse_x;
  m->mouse_y = j->mapped_mouse_y;
  m->mouse_select = j->mapped_mouse_select;
  m->mouse_menu = j->mapped_mouse_menu;
  m->mouse_adjust = j->mapped_mouse_adjust;
  // "Active" means actually driving the pointer right now, not just bound -
  // needs the global Control gate on too (Joystick_Control, h.joyswis).
  m->mouse_control_active = mouse_control_on &&
    j->mapped_mouse_x != NOT_MAPPED && j->mapped_mouse_y != NOT_MAPPED;

  m->key_up = j->key_up;
  m->key_down = j->key_down;
  m->key_left = j->key_left;
  m->key_right = j->key_right;
  for (uint32_t b=0; b<JOY_BUTTONS; b++)
    m->key_buttons[b] = j->key_buttons[b];

  m->automap = j->automap;

  r->r[0] = 0;
  r->r[1] = sizeof(struct joystick_mapping);
  r->r[2] = (int) m;

  return NULL;
}
