
#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 "errors.h"
#include "joyswis.h"

#include "cmdemu.h"   // joy_flag_toggle - shared with *USBJoystick_Emulate
#include "cmdctrl.h"


// Global output gates (mouse_control_on/keyboard_control_on, h.usbjoystick):
// independent of the binding table - see h.joyswis for why this is a
// separate SWI/command family from Emulate, and c.mouse and c.keys for how a
// gate being turned off cleanly releases anything it was holding.


// A * command tail is terminated by a control character (< 32), not always a
// NUL, and the bytes beyond it aren't ours. NUL-terminate the string at that
// control char so strtok and the string comparisons never run off the end of
// the tail into unrelated memory. (Same helper as c.cmdbind's and c.cmdemu's
// terminate_tail - kept local rather than shared, since each command file is
// self-contained.)
static void terminate_tail(char *s)
{
  while ((unsigned char)*s >= ' ') s++;
  *s = '\0';
}


// ASCII lower-case, done inline rather than via the shared C library's
// tolower(): in this module context the library ctype lookup faults (data
// abort in ROM) on some inputs, so we never call it. (Same helper as
// c.cmdbind's lc/ci_equal - kept local rather than shared, self-contained.)
static int lc(int c)
{
  return (c >= 'A' && c <= 'Z') ? c + 32 : c;
}

// Case-insensitive equality (the DDE C library has no portable strcasecmp) -
// so *USBJoystick_Control mouse on works the same as Mouse On.
static int ci_equal(const char *a, const char *b)
{
  while (*a && *b) {
    if (lc((unsigned char)*a++) != lc((unsigned char)*b++)) return 0;
  }
  return *a == *b;
}


// The <Mouse|Keyboard> method argument - text form of the JOYSTICK_CONTROL_*
// selectors (h.joyswis), matching how Joystick_Control's R0 documents them,
// so the *command mirrors the SWI's own selector 1:1.
static const struct { const char *arg; int *flag; const char *debug_name; } control_methods[2] = {
  { "Mouse",    &mouse_control_on,    "USBJoystick_Control Mouse"    },
  { "Keyboard", &keyboard_control_on, "USBJoystick_Control Keyboard" }
};


_kernel_oserror *command_control(const char *args, int32_t argc)
{
  if (argc < 1 || argc > 2)
    return geterror(Error_BadNoParms);

  const char sp[2] = " ";
  char *a = (char *) args;
  terminate_tail(a);

  char *method = strtok(a, sp);
  char *onoff  = strtok(NULL, sp);

  int i;
  int *flag = NULL;
  const char *name = NULL;
  for (i = 0; i < 2; i++)
    if (method && ci_equal(method, control_methods[i].arg))
    { flag = control_methods[i].flag; name = control_methods[i].debug_name; break; }

  if (flag == NULL) {
    debug_printf("USBJoystick_Control: Bad arguments, unknown method '%s' - must be Mouse or Keyboard\n", method ? method : "");
    return geterror(Error_BadNoParms);
  }

  if (onoff == NULL) {
    printf("%s is %s\n", control_methods[i].arg, *flag ? "on" : "off");
    return NULL;
  }

  int32_t op;
  if (ci_equal(onoff, "on")) op = 1;
  else if (ci_equal(onoff, "off")) op = 0;
  else {
    debug_printf("%s: Bad argument - must be on or off\n", name);
    return geterror(Error_BadNoParms);
  }

  return joy_flag_toggle(flag, op, name);
}


_kernel_oserror *swi_joystick_control(_kernel_swi_regs *r)
{
  int *flag;
  const char *name;

  switch (r->r[0]) {
    case JOYSTICK_CONTROL_MOUSE:    flag = &mouse_control_on;    name = "Joystick_Control(Mouse)";    break;
    case JOYSTICK_CONTROL_KEYBOARD: flag = &keyboard_control_on; name = "Joystick_Control(Keyboard)"; break;
    default:
      debug_printf("Joystick_Control: Bad arguments, unknown selector %u\n", r->r[0]);
      return geterror(Error_BadNoParms);
  }

  _kernel_oserror *err = joy_flag_toggle(flag, (int32_t) r->r[1], name);
  if (err) return err;

  r->r[0] = *flag;
  return NULL;
}
