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

#include "cmdemu.h"


_kernel_oserror *joy_flag_toggle(int *flag, int32_t op, const char *name)
{
  if (op == -1)
    return NULL;   // query only - caller reads *flag back for themselves

  int arg_on = (op == 1);
  int arg_off = (op == 0);

  if (!arg_on && !arg_off) {
    debug_printf("%s: Bad argument - must be on(1), off(0) or query(-1)\n", name);
    return geterror(Error_BadNoParms);
  }

  *flag = arg_on;
  return NULL;
}


// 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 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_Emulate joy on works the same as Joy 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 <SerialPort|Joy|ADC> method argument - text form of the
// JOYSTICK_EMULATE_* selectors (h.joyswis), matching how Joystick_Emulate'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; } emulate_methods[3] = {
  { "SerialPort", &emulate_serial_port_on, "USBJoystick_Emulate SerialPort" },
  { "Joy",        &emulate_joy_on,         "USBJoystick_Emulate Joy"        },
  { "ADC",        &emulate_adc_on,         "USBJoystick_Emulate ADC"        }
};


_kernel_oserror *command_emulate(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 < 3; i++)
    if (method && ci_equal(method, emulate_methods[i].arg))
    { flag = emulate_methods[i].flag; name = emulate_methods[i].debug_name; break; }

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

  if (onoff == NULL) {
    printf("%s is %s\n", emulate_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_emulate(_kernel_swi_regs *r)
{
  int *flag;
  const char *name;

  switch (r->r[0]) {
    case JOYSTICK_EMULATE_SERIALPORT: flag = &emulate_serial_port_on; name = "Joystick_Emulate(SerialPort)"; break;
    case JOYSTICK_EMULATE_JOY:        flag = &emulate_joy_on;         name = "Joystick_Emulate(Joy)";        break;
    case JOYSTICK_EMULATE_ADC:        flag = &emulate_adc_on;         name = "Joystick_Emulate(ADC)";        break;
    default:
      debug_printf("Joystick_Emulate: Bad arguments, unknown API 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;
}
