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

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


#include "debugrep.h"
#include "usbjoystick.h"
#include "errors.h"
#include "joyhelp.h"
#include "joyswis.h"
#include "mouse.h"
#include "adc.h"
#include "device.h"
#include "pktlog.h"

#include "cmdshow.h"


static void print_slot_title(uint32_t slot)
{
  struct joydata_struct *j = &joy_data[slot];
  char vendor[24], product[24];

  if (j->manufacturer[0] == '\0')
    sprintf(vendor, "vendor 0x%04x", j->vendor_id);
  if (j->product[0] == '\0')
    sprintf(product, "product 0x%04x", j->product_id);

  printf("Joystick slot %u (%s - %s)\n", slot,
         j->manufacturer[0] ? j->manufacturer : vendor,
         j->product[0] ? j->product : product);
}



_kernel_oserror *command_read(const char *args, int32_t argc)
{
  if (argc != 1) {
    debug_printf("command_read: Bad arguments, argc isn't 1 (it is %d)\n", argc);
    return geterror(Error_BadNoParms);
  }

  _kernel_oserror *error = NULL;
  uint32_t slot;
  uint32_t ret = sscanf(args, "%d", &slot);
  if (ret != 1) {
    debug_printf("command_read: Bad arguments, didn't read 1 arg (read %d)\n", ret);
    return geterror(Error_BadNoParms);
  }

  if (slot >= Joystick_MaxSlots) {
    debug_printf("command_read: Bad arguments, slot (%d) is out of range\n", slot);
    return geterror(Error_BadNoParms);
  }

  if (!joy_data[slot].in_use) {
    debug_printf("command_read: Joystick slot (%d) isn't active\n", slot);
    return geterror(Error_JoyNotActive);
  }

  if (!joy_data[slot].open) {
    debug_printf("command_read: Joystick slot (%d) isn't open\n", slot);
    return geterror(Error_JoyNotOpen);
  }

  debug_printf("command_read: slot %u\n", slot);

  print_slot_title(slot);

  for (uint32_t a=0; a<joy_data[slot].num_axes; a++) {
    printf("Axis %02d %6s : ", a, joy_data[slot].axes[a].name);
    printf("Val %8d  ", joy_data[slot].axes[a].val);
    printf("Min %8d  ", joy_data[slot].axes[a].min);
    printf("Mid %8d  ", joy_data[slot].axes[a].mid);
    printf("Max %8d  ", joy_data[slot].axes[a].max);
    printf("Invert=");
    if (joy_data[slot].axes[a].invert)
      printf("on");
    else
      printf("off");

    printf("\n");
  }

  printf("Buttons: ");
  for (uint32_t b=0; b<joy_data[slot].num_buttons; b++) {
    printf("%u=%u ", b, joy_data[slot].buttons[b]);
  }
  printf("\n");

  IGNORE(error);

  return NULL;
}




_kernel_oserror *command_debug(const char *args, int32_t argc)
{
  if (argc >= 1) {
    int32_t desired_slot = -1;
    uint32_t ret = sscanf(args, "%d", &desired_slot);
    if (ret != 1) {
      debug_printf("Debug: Bad arguments, didn't read 1 args (read %d)\n", ret);
      return geterror(Error_BadNoParms);
    }

    if (desired_slot < 0 || desired_slot >= Joystick_MaxSlots) {
      debug_printf("Debug: Bad arguments, slot (%d) is too big\n", desired_slot);
      return geterror(Error_BadNoParms);
    }

    if (!joy_data[desired_slot].in_use) {
      debug_printf("Debug: Joystick slot (%d) isn't active\n", desired_slot);
      return geterror(Error_JoyNotActive);
    }

    if (!joy_data[desired_slot].open) {
      debug_printf("Debug: Joystick slot (%d) isn't open\n", desired_slot);
      return geterror(Error_JoyNotOpen);
    }

    debug_printf("Debug: Printing out single stick slot %d\n", desired_slot);
    debug_joystick(desired_slot);
  }
  else {
    int any = FALSE;

    for (uint32_t slot=0; slot<Joystick_MaxSlots; slot++) {
      if (joy_data[slot].in_use == TRUE) {
        debug_printf("Debug: Printing out all active sticks, currently on stick slot %d\n", slot);
        debug_joystick(slot);
        any = TRUE;
      }
    }

    if (any == FALSE) {
      printf("No active joysticks.\n");
    }
  }

  return NULL;
}




_kernel_oserror *command_list(const char *args, int32_t argc)
{
  if (argc >= 1) {
    int32_t desired_slot = -1;
    uint32_t ret = sscanf(args, "%d", &desired_slot);
    if (ret != 1) {
      debug_printf("command_list: Bad arguments, didn't read 1 args (read %d)\n", ret);
      return geterror(Error_BadNoParms);
    }

    if (desired_slot < 0 || desired_slot >= Joystick_MaxSlots) {
      debug_printf("command_list: Bad arguments, slot (%d) is too big\n", desired_slot);
      return geterror(Error_BadNoParms);
    }

    if (!joy_data[desired_slot].in_use) {
      debug_printf("command_list: Joystick slot (%d) isn't active\n", desired_slot);
      return geterror(Error_JoyNotActive);
    }

    if (!joy_data[desired_slot].open) {
      debug_printf("command_list: Joystick slot (%d) isn't open\n", desired_slot);
      return geterror(Error_JoyNotOpen);
    }

    debug_printf("command_list: Printing out single stick slot %d\n", desired_slot);
    print_joystick(desired_slot);
  }
  else {
    int any = FALSE;

    for (uint32_t slot=0; slot<Joystick_MaxSlots; slot++) {
      if (joy_data[slot].in_use == TRUE) {
        debug_printf("command_list: Printing out all active sticks, currently on stick slot %d\n", slot);
        print_joystick(slot);
        any = TRUE;
      }
    }

    if (any == FALSE) {
      printf("No active joysticks.\n");
    }
  }

  return NULL;
}








static void print_capabilities(uint32_t slot)
{
  static const char *names[] = { "Rumble", "LED", "Battery", "Touchpad", "Sensors" };
  uint32_t caps = joyapi_capabilities(slot);
  int any = FALSE;

  for (uint32_t b=0; b<sizeof(names)/sizeof(names[0]); b++) {
    if (caps & (1u << b)) {
      printf("%s%s", any ? ", " : "", names[b]);
      any = TRUE;
    }
  }
  printf(any ? "\n" : "None\n");
}


void print_joystick(uint32_t slot)
{
  print_slot_title(slot);

  printf("  Local USB device name            : %s\n", joy_data[slot].device_name);
  printf("  Interface                        : %d\n", joy_data[slot].interface);
  printf("  Endpoint                         : %d\n", joy_data[slot].endpoint);
  printf("  Device manufacturer              : %s\n", joy_data[slot].manufacturer);
  printf("  Device product name              : %s\n", joy_data[slot].product);
  printf("  Device serial number             : %s\n", joy_data[slot].serial);
  printf("  Driver                           : ");
  if (joy_data[slot].device)
    printf("%s\n", joy_data[slot].device->name);
  else
    printf("N/A\n");

  printf("  Capabilities                     : ");
  print_capabilities(slot);

  printf("  Number of axes                   : %d\n", joy_data[slot].num_axes);
  printf("  Number of buttons                : %d\n", joy_data[slot].num_buttons);

  printf("  HAT switch                       : ");
  if (joy_data[slot].hat_item)
    printf("Yes\n");
  else
    printf("No\n");

  printf("  DPAD                             : ");
  if (joy_data[slot].has_dpad)
    printf("Yes\n");
  else
    printf("No\n");

  /* The mouse/keyboard mapping state is not summarised here - the binding
     table is the source of truth; *USBJoystick_Mappings shows it in full. */
}








// What the driver table and the descriptors gave this slot.
static void debug_discovered(uint32_t slot)
{
  struct joydata_struct *j = &joy_data[slot];
  struct joy_device_struct *d = j->device;

  if (d == NULL)
    return;

  printf("  Output transport        : ");
  switch (d->output_transport) {
    case JOY_OUTPUT_ENDPOINT:
      if (j->output.endpoint)
        printf("OUT endpoint %u (%s)\n", j->output.endpoint, j->output.fp ? "output stream open" : "output stream not open");
      else
        printf("OUT endpoint (none found)\n");
      break;
    case JOY_OUTPUT_CONTROL:
      printf("Control pipe (HID output report)\n");
      break;
    default:
      printf("None\n");
  }

  printf("  Capabilities            : ");
  print_capabilities(slot);

  printf("  Report descriptor       : %s\n", d->report_descriptor ? "Baked in" : "From the device");

  printf("  Report id               : ");
  if (j->has_report_id)
    printf("%u\n", j->report_id);
  else
    printf("None\n");

  printf("  Report data offset      : %u\n", d->report_data_offset);
  printf("  Data validator          : %s\n", d->data_valid ? "Yes" : "No");
  printf("  Start-up routine        : %s\n", d->init ? "Yes" : "No");
  printf("  Auto-inverted axis types: %u\n", d->auto_invert_axes_length);
}


void debug_joystick(uint32_t slot)
{
  print_slot_title(slot);

  printf("  Path                    : %s\n", joy_data[slot].usb_path);
  printf("  File handle             : %u\n", joy_data[slot].fp);

  printf("  Data stream open        : ");
  if (joy_data[slot].open)
    printf("Yes\n");
  else
    printf("No (is something else using it?)\n");

  printf("  Local USB device name   : %s\n", joy_data[slot].device_name);
  printf("  Interface               : %d\n", joy_data[slot].interface);
  printf("  Endpoint                : %d\n", joy_data[slot].endpoint);
  printf("  Device manufacturer     : %s\n", joy_data[slot].manufacturer);
  printf("  Device product name     : %s\n", joy_data[slot].product);
  printf("  Device serial number    : %s\n", joy_data[slot].serial);
  printf("  Driver                  : ");
  if (joy_data[slot].device)
    printf("%s\n", joy_data[slot].device->name);
  else
    printf("N/A\n");

  debug_discovered(slot);

  printf("  USB vendor id           : 0x%04x\n", joy_data[slot].vendor_id);
  printf("  USB product id          : 0x%04x\n", joy_data[slot].product_id);
  printf("  USB device id           : 0x%04x\n", joy_data[slot].device_id);

  printf("  Buffer handle           : %u\n", (uint32_t)(joy_data[slot].buffer_handle));
  printf("  Buffer internal id      : %u\n", (uint32_t)(joy_data[slot].buffer_internal_id));
  printf("  Buffer service routine  : 0x%08x\n", (uint32_t)(joy_data[slot].buffer_service_routine));
  printf("  Buffer workspace        : 0x%08x\n", (uint32_t)(joy_data[slot].buffer_workspace));

  printf("  DeviceFS handle         : 0x%08x\n", joy_data[slot].devicefs_handle);
  printf("  Report data length      : %u\n", joy_data[slot].report_length);

  printf("  Upcalls                 : %u (delta: %u)\n", joy_data[slot].upcalls, joy_data[slot].upcalls_delta);
  joy_data[slot].upcalls_delta = 0;

  printf("  Good reads              : %u (delta: %u)\n", joy_data[slot].good_reads, joy_data[slot].good_reads_delta);
  joy_data[slot].good_reads_delta = 0;

  printf("  Bad reads               : %u (delta: %u)\n", joy_data[slot].bad_reads, joy_data[slot].bad_reads_delta);
  joy_data[slot].bad_reads_delta = 0;

  printf("  Raw report data         : ");
  for (int32_t b=0; b<joy_data[slot].report_length + (joy_data[slot].has_report_id ? 1 : 0); b++) {
    printf("%02x ", joy_data[slot].data_buf[b]);
  }
  printf("\n");

  if (joy_data[slot].device && joy_data[slot].device->data_valid) {
    printf("  Recent packets          : (changes only, oldest first)\n");
    uint32_t seen = pktlog_print(slot, 8);
    printf("  Non-input packets seen  : %u\n", seen);
  }

  printf("  Number of axes          : %d\n", joy_data[slot].num_axes);
  printf("  Number of buttons       : %d\n", joy_data[slot].num_buttons);

  printf("  HAT switch              : ");
  if (joy_data[slot].hat_item)
    printf("Yes\n");
  else
    printf("No\n");

  printf("  DPAD                    : ");
  if (joy_data[slot].has_dpad)
    printf("Yes\n");
  else
    printf("No\n");

  printf("  Axes:\n");

  for (int32_t a=0; a<joy_data[slot].num_axes; a++) {
    printf("  Axis %02d %5s : ", a, joy_data[slot].axes[a].name);
    printf("Slope 8 = %4u ", joy_data[slot].axes[a].read8_slope);
    if (joy_data[slot].axes[a].read8_slope_div)
      printf("(div), ");
    else
      printf("(mul), ");

    printf("Slope 16 = %4u ", joy_data[slot].axes[a].read16_slope);
    if (joy_data[slot].axes[a].read16_slope_div)
      printf("(div), ");
    else
      printf("(mul), ");

    printf("Mouse factor = %4u, ", joy_data[slot].axes[a].mouse_factor);

    printf("SP = %6d:%6d, ", joy_data[slot].axes[a].serial_port_lower_bound, joy_data[slot].axes[a].serial_port_upper_bound);

    printf("Invert = ");
    if (joy_data[slot].axes[a].invert)
      printf(" on, ");
    else
      printf("off, ");

    struct hid_item *item = get_axis_item_by_type(slot, joy_data[slot].axes[a].type);
    if (item)
      printf("ReportId = %d", item->report_ID);
    else
      printf("ReportId = (none)");

    printf("\n");
  }

  printf("  Button ReportIds : ");
  for (int32_t a=0; a<Joystick_MaxButtons; a++) {
    if (joy_data[slot].button_items[a])
      printf("%02d=%d ", a, joy_data[slot].button_items[a]->report_ID);
  }
  printf("\n");
}
