

// 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 "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)

  // this assumes the stick is a real stick id
  switch (reason)
  {
    case ACORN_SWI_READ_REASON_DEV_INFO:
      return joystick_read_acorn_dev_info(r, stick);
  }

  // the other reasons take a legacy number, so we need to find the mapped stick id
  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);
        case ACORN_SWI_READ_REASON_AXES_INFO:
          return joystick_read_acorn_axes_info(r, i);
        case ACORN_SWI_READ_REASON_AXES_VALUES:
          return joystick_read_acorn_axes_values(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;
    }
    case ACORN_SWI_READ_REASON_AXES_INFO:
    {
      // full: just return r0==0 (to indicate invalid id)
      r->r[0] = 0;
      break;
    }
    case ACORN_SWI_READ_REASON_AXES_VALUES:
    {
      // full: just return r0==0 (to indicate invalid id)
      r->r[0] = 0;
      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

  if (joy_data[i].mapped_x_8 == NOT_MAPPED || joy_data[i].mapped_y_8 == NOT_MAPPED) {
    r->r[0] = 0;
    return NULL;
  }

  struct axisdata_struct *ax = &(joy_data[i].axes[joy_data[i].mapped_x_8]);
  struct axisdata_struct *ay = &(joy_data[i].axes[joy_data[i].mapped_y_8]);

  int32_t x = 0;

  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
  int32_t y = 0;

  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

  if (joy_data[i].mapped_x_16 == NOT_MAPPED || joy_data[i].mapped_y_16 == NOT_MAPPED) {
    r->r[0] = 0;
    r->r[1] = 0;
    return NULL;
  }
  
  struct axisdata_struct *ax = &(joy_data[i].axes[joy_data[i].mapped_x_16]);
  struct axisdata_struct *ay = &(joy_data[i].axes[joy_data[i].mapped_y_16]);

  int32_t x = 0;

  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
  int32_t y = 0;

  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;
}






_kernel_oserror* joystick_read_acorn_dev_info(_kernel_swi_regs *r, uint32_t stick)
{
  // i is stick number (0-254)
  //      or ==255 for 'all'
  //
  // return r0: the size of the struct
  //        r1: the number of devices found
  //        r2: ptr to data structure

  uint8_t list_index = 0;
  for (uint32_t i=0; i<JOY_MAX; i++) {
    struct joydata_struct *joy = &joy_data[i];
    if (joy->in_use && (joy->mapped_number == stick || stick == 255)) {
      // a stick we want to report info on, so create block
      joy_read_dev_info[list_index].joystick_id = i;
      joy_read_dev_info[list_index].device_path = joy->usb_path;
      joy_read_dev_info[list_index].manufacturer = joy->manufacturer;
      joy_read_dev_info[list_index].product = joy->product;
      joy_read_dev_info[list_index].serial = joy->serial;
      joy_read_dev_info[list_index].number_axes = joy->num_axes;
      joy_read_dev_info[list_index].number_buttons = joy->num_buttons;
      list_index++;
    }
  }

  r->r[0] = sizeof(struct joystick_read_dev_info_struct);
  r->r[1] = list_index;
  r->r[2] = (int)joy_read_dev_info;

  return NULL;
}








_kernel_oserror* joystick_read_acorn_axes_info(_kernel_swi_regs *r, uint32_t i)
{
  // i is stick number (0-254)
  //
  // return r0: the size of the struct
  //        r1: the number of devices found
  //        r2: ptr to data structure
  //        r3: button state (32-bits)

  struct joydata_struct *j = get_stick(i);

  if (j == NULL)
    return &err_badjoy;

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

  uint8_t ii=0;
  for (uint8_t ai=0; ai<JOY_AXES-1; ai++) {
    struct axisdata_struct *a = get_stick_axis(j, ai);
    if (a != NULL) {
      joy_read_axes_info[ii].type = a->type;
      joy_read_axes_info[ii].name = a->name;
      joy_read_axes_info[ii].min = a->min;
      joy_read_axes_info[ii].mid = a->mid;
      joy_read_axes_info[ii].max = a->max;
      joy_read_axes_info[ii].val = a->val;
      ii++;
    }
  }

  r->r[0] = sizeof(struct joystick_read_axes_info_struct);
  r->r[1] = ii++;
  r->r[2] = (int)&joy_read_axes_info;
  r->r[3] = full_buttons;

  return NULL;
}





_kernel_oserror* joystick_read_acorn_axes_values(_kernel_swi_regs *r, uint32_t i)
{
  // i is stick number (0-254)
  //
  // return r0: the size of the struct (currently each is an int)
  //        r1: the number of devices found
  //        r2: ptr to data structure
  //        r3: button state (32-bits)

  struct joydata_struct *j = get_stick(i);

  if (j == NULL)
    return &err_badjoy;

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

  uint8_t ii=0;
  for (uint8_t ai=0; ai<JOY_AXES; ai++) {
    struct axisdata_struct *a = get_stick_axis(j, ai);
    if (a != NULL) {
      joy_read_axes_values[ii] = a->val;
      ii++;
    }
  }

  r->r[0] = sizeof(int32_t);
  r->r[1] = ii++;
  r->r[2] = (int)&joy_read_axes_values;
  r->r[3] = full_buttons;

  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;
}






/*

Hooks to pick-up non-Acorn SWI interface

*/


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

  // our pre-veneer has copied R11 (SWI number) to R9 so we can get at it
  // it also uses r9 as an output parameter to claim or pass-on the vector

  uint32_t swi_number = r->r[9];

  if (emulate_serial_port_on && swi_number == USBJoystickSerialPortSWI)
  {
    r->r[9] = 1; // claim (see pre-veneer)
    return swi_joystick_status(r);
  }
  else if (emulate_joy_on && swi_number == JoySWI_Read0)
  {
    r->r[9] = 1;
    return swi_joy_read(r, 0);
  }
  else if (emulate_joy_on && swi_number == JoySWI_Read1)
  {
    r->r[9] = 1;
    return swi_joy_read(r, 1);
  }
  else if (emulate_joy_on && swi_number == JoySWI_Test)
  {
    r->r[9] = 1;
    return swi_joy_test(r);
  }
  else if (emulate_joy_on && swi_number == JoySWI_ReadPipe)
  {
    r->r[9] = 1;
    return swi_joy_readpipe(r);
  }
  else
  {
    r->r[9] = 0; // pass-on (see pre-veneer)
    return NULL;
  }
}







/*

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)
{
  if (joy_data[i].mapped_x_8 == NOT_MAPPED || joy_data[i].mapped_y_8 == NOT_MAPPED) {
    return 0;
  }
  
  struct axisdata_struct *ax = &(joy_data[i].axes[joy_data[i].mapped_x_8]);
  struct axisdata_struct *ay = &(joy_data[i].axes[joy_data[i].mapped_y_8]);

  int32_t left = (ax->val < ax->serial_port_lower_bound);
  int32_t right = (ax->val > ax->serial_port_upper_bound);

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

  int32_t up = (ay->val < ay->serial_port_lower_bound);
  int32_t 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;
}






/*

RTFM interface

*/



_kernel_oserror* swi_joy_read(_kernel_swi_regs *r, int32_t n)
{
  // n is 0 or 1 (stick)

  // return r0 = % FUDLR
  // or        = 0x1F for 'no stick attached'

  for (uint32_t i=0; i<JOY_MAX; i++) {
    if (joy_data[i].mapped_number == n) {
      uint32_t j = encode_stick_serial_port(i);
      //debug_printf("swi_joy_read: Stick %d (id %u), returning encoded value %u in r0\n", n, i, j);
      r->r[0] = j;
      return NULL;       // ok to leave early!
    }
  }

  //debug_printf("swi_joy_read: No stick mapped as %d, so returning 'no stick attached' (0x1F in r0)\n", n);
  r->r[0] = 0x1F;  // default response if no stick mapped
  return NULL;
}




_kernel_oserror* swi_joy_test(_kernel_swi_regs *r)
{
  // 255: nothing in Econet (no RTFM hardware)
  // 128: Econet present
  //   0: RTFM present

  swi_joy_read(r, 0);
  int32_t j1 = r->r[0];

  swi_joy_read(r, 1);
  int32_t j2 = r->r[0];

  if (j1 == 0x1F && j2 == 0x1F) {
    //debug_printf("swi_joy_test: neither stick 1 or 2 is mapped, so returning 255 in r0 to indicate no RTFM\n");
    r->r[0] = 255;
  }
  else {
    //debug_printf("swi_joy_test: something is mapped, so returning 0 in r0 to indicate presence\n");
    r->r[0] = 0;
  }
  return NULL;
}





_kernel_oserror* swi_joy_readpipe(_kernel_swi_regs *r)
{
  IGNORE(r);
  // don't know what to do here...
  //debug_printf("swi_joy_readpipe: doing nothing\n");
  return NULL;
}
